Skip to content

Commit 22b2712

Browse files
committed
Auto merge of #117377 - dtolnay:deprecatedsince, r=cjgillot
Store #[deprecated] attribute's `since` value in parsed form This PR implements the first followup bullet listed in #117148 (comment). We centralize error handling to the attribute parsing code in `compiler/rustc_attr/src/builtin.rs`, and thereby remove some awkward error codepaths from later phases of compilation that had to make sense of these #\[deprecated\] attributes, namely `compiler/rustc_passes/src/stability.rs` and `compiler/rustc_middle/src/middle/stability.rs`.
2 parents ffb7ed9 + 8b8906b commit 22b2712

File tree

14 files changed

+166
-196
lines changed

14 files changed

+166
-196
lines changed

compiler/rustc_attr/src/builtin.rs

+62-22
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ pub enum StabilityLevel {
139139
/// `#[stable]`
140140
Stable {
141141
/// Rust release which stabilized this feature.
142-
since: Since,
142+
since: StableSince,
143143
/// Is this item allowed to be referred to on stable, despite being contained in unstable
144144
/// modules?
145145
allowed_through_unstable_modules: bool,
@@ -149,7 +149,7 @@ pub enum StabilityLevel {
149149
/// Rust release in which a feature is stabilized.
150150
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
151151
#[derive(HashStable_Generic)]
152-
pub enum Since {
152+
pub enum StableSince {
153153
Version(RustcVersion),
154154
/// Stabilized in the upcoming version, whatever number that is.
155155
Current,
@@ -378,16 +378,16 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit
378378

379379
let since = if let Some(since) = since {
380380
if since.as_str() == VERSION_PLACEHOLDER {
381-
Since::Current
381+
StableSince::Current
382382
} else if let Some(version) = parse_version(since) {
383-
Since::Version(version)
383+
StableSince::Version(version)
384384
} else {
385385
sess.emit_err(session_diagnostics::InvalidSince { span: attr.span });
386-
Since::Err
386+
StableSince::Err
387387
}
388388
} else {
389389
sess.emit_err(session_diagnostics::MissingSince { span: attr.span });
390-
Since::Err
390+
StableSince::Err
391391
};
392392

393393
match feature {
@@ -720,17 +720,49 @@ pub fn eval_condition(
720720

721721
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
722722
pub struct Deprecation {
723-
pub since: Option<Symbol>,
723+
pub since: DeprecatedSince,
724724
/// The note to issue a reason.
725725
pub note: Option<Symbol>,
726726
/// A text snippet used to completely replace any use of the deprecated item in an expression.
727727
///
728728
/// This is currently unstable.
729729
pub suggestion: Option<Symbol>,
730+
}
731+
732+
/// Release in which an API is deprecated.
733+
#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
734+
pub enum DeprecatedSince {
735+
RustcVersion(RustcVersion),
736+
/// Deprecated in the future ("to be determined").
737+
Future,
738+
/// `feature(staged_api)` is off. Deprecation versions outside the standard
739+
/// library are allowed to be arbitrary strings, for better or worse.
740+
NonStandard(Symbol),
741+
/// Deprecation version is unspecified but optional.
742+
Unspecified,
743+
/// Failed to parse a deprecation version, or the deprecation version is
744+
/// unspecified and required. An error has already been emitted.
745+
Err,
746+
}
747+
748+
impl Deprecation {
749+
/// Whether an item marked with #[deprecated(since = "X")] is currently
750+
/// deprecated (i.e., whether X is not greater than the current rustc
751+
/// version).
752+
pub fn is_in_effect(&self) -> bool {
753+
match self.since {
754+
DeprecatedSince::RustcVersion(since) => since <= RustcVersion::CURRENT,
755+
DeprecatedSince::Future => false,
756+
// The `since` field doesn't have semantic purpose without `#![staged_api]`.
757+
DeprecatedSince::NonStandard(_) => true,
758+
// Assume deprecation is in effect if "since" field is absent or invalid.
759+
DeprecatedSince::Unspecified | DeprecatedSince::Err => true,
760+
}
761+
}
730762

731-
/// Whether to treat the since attribute as being a Rust version identifier
732-
/// (rather than an opaque string).
733-
pub is_since_rustc_version: bool,
763+
pub fn is_since_rustc_version(&self) -> bool {
764+
matches!(self.since, DeprecatedSince::RustcVersion(_))
765+
}
734766
}
735767

736768
/// Finds the deprecation attribute. `None` if none exists.
@@ -839,22 +871,30 @@ pub fn find_deprecation(
839871
}
840872
}
841873

842-
if is_rustc {
843-
if since.is_none() {
844-
sess.emit_err(session_diagnostics::MissingSince { span: attr.span });
845-
continue;
874+
let since = if let Some(since) = since {
875+
if since.as_str() == "TBD" {
876+
DeprecatedSince::Future
877+
} else if !is_rustc {
878+
DeprecatedSince::NonStandard(since)
879+
} else if let Some(version) = parse_version(since) {
880+
DeprecatedSince::RustcVersion(version)
881+
} else {
882+
sess.emit_err(session_diagnostics::InvalidSince { span: attr.span });
883+
DeprecatedSince::Err
846884
}
885+
} else if is_rustc {
886+
sess.emit_err(session_diagnostics::MissingSince { span: attr.span });
887+
DeprecatedSince::Err
888+
} else {
889+
DeprecatedSince::Unspecified
890+
};
847891

848-
if note.is_none() {
849-
sess.emit_err(session_diagnostics::MissingNote { span: attr.span });
850-
continue;
851-
}
892+
if is_rustc && note.is_none() {
893+
sess.emit_err(session_diagnostics::MissingNote { span: attr.span });
894+
continue;
852895
}
853896

854-
depr = Some((
855-
Deprecation { since, note, suggestion, is_since_rustc_version: is_rustc },
856-
attr.span,
857-
));
897+
depr = Some((Deprecation { since, note, suggestion }, attr.span));
858898
}
859899

860900
depr

compiler/rustc_middle/src/middle/stability.rs

+20-52
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ pub use self::StabilityLevel::*;
55

66
use crate::ty::{self, TyCtxt};
77
use rustc_ast::NodeId;
8-
use rustc_attr::{self as attr, ConstStability, DefaultBodyStability, Deprecation, Stability};
8+
use rustc_attr::{
9+
self as attr, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability,
10+
};
911
use rustc_data_structures::fx::FxHashMap;
1012
use rustc_errors::{Applicability, Diagnostic};
1113
use rustc_feature::GateIssue;
@@ -16,7 +18,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
1618
use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
1719
use rustc_session::lint::{BuiltinLintDiagnostics, Level, Lint, LintBuffer};
1820
use rustc_session::parse::feature_err_issue;
19-
use rustc_session::{RustcVersion, Session};
21+
use rustc_session::Session;
2022
use rustc_span::symbol::{sym, Symbol};
2123
use rustc_span::Span;
2224
use std::num::NonZeroU32;
@@ -123,41 +125,6 @@ pub fn report_unstable(
123125
}
124126
}
125127

126-
/// Checks whether an item marked with `deprecated(since="X")` is currently
127-
/// deprecated (i.e., whether X is not greater than the current rustc version).
128-
pub fn deprecation_in_effect(depr: &Deprecation) -> bool {
129-
let is_since_rustc_version = depr.is_since_rustc_version;
130-
let since = depr.since.as_ref().map(Symbol::as_str);
131-
132-
if !is_since_rustc_version {
133-
// The `since` field doesn't have semantic purpose without `#![staged_api]`.
134-
return true;
135-
}
136-
137-
if let Some(since) = since {
138-
if since == "TBD" {
139-
return false;
140-
}
141-
142-
// We ignore non-integer components of the version (e.g., "nightly").
143-
let since: Vec<u16> =
144-
since.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect();
145-
146-
// We simply treat invalid `since` attributes as relating to a previous
147-
// Rust version, thus always displaying the warning.
148-
if since.len() != 3 {
149-
return true;
150-
}
151-
152-
let rustc = RustcVersion::CURRENT;
153-
return since.as_slice() <= &[rustc.major, rustc.minor, rustc.patch];
154-
};
155-
156-
// Assume deprecation is in effect if "since" field is missing
157-
// or if we can't determine the current Rust version.
158-
true
159-
}
160-
161128
pub fn deprecation_suggestion(
162129
diag: &mut Diagnostic,
163130
kind: &str,
@@ -180,25 +147,26 @@ fn deprecation_lint(is_in_effect: bool) -> &'static Lint {
180147

181148
fn deprecation_message(
182149
is_in_effect: bool,
183-
since: Option<Symbol>,
150+
since: DeprecatedSince,
184151
note: Option<Symbol>,
185152
kind: &str,
186153
path: &str,
187154
) -> String {
188155
let message = if is_in_effect {
189156
format!("use of deprecated {kind} `{path}`")
190157
} else {
191-
let since = since.as_ref().map(Symbol::as_str);
192-
193-
if since == Some("TBD") {
194-
format!("use of {kind} `{path}` that will be deprecated in a future Rust version")
195-
} else {
196-
format!(
197-
"use of {} `{}` that will be deprecated in future version {}",
198-
kind,
199-
path,
200-
since.unwrap()
201-
)
158+
match since {
159+
DeprecatedSince::RustcVersion(version) => format!(
160+
"use of {kind} `{path}` that will be deprecated in future version {version}"
161+
),
162+
DeprecatedSince::Future => {
163+
format!("use of {kind} `{path}` that will be deprecated in a future Rust version")
164+
}
165+
DeprecatedSince::NonStandard(_)
166+
| DeprecatedSince::Unspecified
167+
| DeprecatedSince::Err => {
168+
unreachable!("this deprecation is always in effect; {since:?}")
169+
}
202170
}
203171
};
204172

@@ -213,7 +181,7 @@ pub fn deprecation_message_and_lint(
213181
kind: &str,
214182
path: &str,
215183
) -> (String, &'static Lint) {
216-
let is_in_effect = deprecation_in_effect(depr);
184+
let is_in_effect = depr.is_in_effect();
217185
(
218186
deprecation_message(is_in_effect, depr.since, depr.note, kind, path),
219187
deprecation_lint(is_in_effect),
@@ -381,11 +349,11 @@ impl<'tcx> TyCtxt<'tcx> {
381349
// With #![staged_api], we want to emit down the whole
382350
// hierarchy.
383351
let depr_attr = &depr_entry.attr;
384-
if !skip || depr_attr.is_since_rustc_version {
352+
if !skip || depr_attr.is_since_rustc_version() {
385353
// Calculating message for lint involves calling `self.def_path_str`.
386354
// Which by default to calculate visible path will invoke expensive `visible_parent_map` query.
387355
// So we skip message calculation altogether, if lint is allowed.
388-
let is_in_effect = deprecation_in_effect(depr_attr);
356+
let is_in_effect = depr_attr.is_in_effect();
389357
let lint = deprecation_lint(is_in_effect);
390358
if self.lint_level_at_node(lint, id).0 != Level::Allow {
391359
let def_path = with_no_trimmed_paths!(self.def_path_str(def_id));

compiler/rustc_passes/messages.ftl

-5
Original file line numberDiff line numberDiff line change
@@ -396,11 +396,6 @@ passes_invalid_attr_at_crate_level =
396396
passes_invalid_attr_at_crate_level_item =
397397
the inner attribute doesn't annotate this {$kind}
398398
399-
passes_invalid_deprecation_version =
400-
invalid deprecation version found
401-
.label = invalid deprecation version
402-
.item = the stability attribute annotates this item
403-
404399
passes_invalid_macro_export_arguments = `{$name}` isn't a valid `#[macro_export]` argument
405400
406401
passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments

compiler/rustc_passes/src/errors.rs

-10
Original file line numberDiff line numberDiff line change
@@ -1524,16 +1524,6 @@ pub struct CannotStabilizeDeprecated {
15241524
pub item_sp: Span,
15251525
}
15261526

1527-
#[derive(Diagnostic)]
1528-
#[diag(passes_invalid_deprecation_version)]
1529-
pub struct InvalidDeprecationVersion {
1530-
#[primary_span]
1531-
#[label]
1532-
pub span: Span,
1533-
#[label(passes_item)]
1534-
pub item_sp: Span,
1535-
}
1536-
15371527
#[derive(Diagnostic)]
15381528
#[diag(passes_missing_stability_attr)]
15391529
pub struct MissingStabilityAttr<'a> {

compiler/rustc_passes/src/stability.rs

+15-40
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
44
use crate::errors;
55
use rustc_attr::{
6-
self as attr, ConstStability, Since, Stability, StabilityLevel, Unstable, UnstableReason,
7-
VERSION_PLACEHOLDER,
6+
self as attr, ConstStability, DeprecatedSince, Stability, StabilityLevel, StableSince,
7+
Unstable, UnstableReason, VERSION_PLACEHOLDER,
88
};
99
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
1010
use rustc_hir as hir;
@@ -24,8 +24,6 @@ use rustc_span::symbol::{sym, Symbol};
2424
use rustc_span::Span;
2525
use rustc_target::spec::abi::Abi;
2626

27-
use std::cmp::Ordering;
28-
use std::iter;
2927
use std::mem::replace;
3028
use std::num::NonZeroU32;
3129

@@ -198,10 +196,8 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
198196
}
199197
}
200198

201-
if let Some((rustc_attr::Deprecation { is_since_rustc_version: true, .. }, span)) = &depr {
202-
if stab.is_none() {
203-
self.tcx.sess.emit_err(errors::DeprecatedAttribute { span: *span });
204-
}
199+
if let Some((depr, span)) = &depr && depr.is_since_rustc_version() && stab.is_none() {
200+
self.tcx.sess.emit_err(errors::DeprecatedAttribute { span: *span });
205201
}
206202

207203
if let Some((body_stab, _span)) = body_stab {
@@ -223,44 +219,23 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
223219

224220
// Check if deprecated_since < stable_since. If it is,
225221
// this is *almost surely* an accident.
226-
if let (&Some(dep_since), &attr::Stable { since: stab_since, .. }) =
227-
(&depr.as_ref().and_then(|(d, _)| d.since), &stab.level)
222+
if let (
223+
&Some(DeprecatedSince::RustcVersion(dep_since)),
224+
&attr::Stable { since: stab_since, .. },
225+
) = (&depr.as_ref().map(|(d, _)| d.since), &stab.level)
228226
{
229227
match stab_since {
230-
Since::Current => {
228+
StableSince::Current => {
231229
self.tcx.sess.emit_err(errors::CannotStabilizeDeprecated { span, item_sp });
232230
}
233-
Since::Version(stab_since) => {
234-
// Explicit version of iter::order::lt to handle parse errors properly
235-
for (dep_v, stab_v) in iter::zip(
236-
dep_since.as_str().split('.'),
237-
[stab_since.major, stab_since.minor, stab_since.patch],
238-
) {
239-
match dep_v.parse::<u64>() {
240-
Ok(dep_vp) => match dep_vp.cmp(&u64::from(stab_v)) {
241-
Ordering::Less => {
242-
self.tcx.sess.emit_err(errors::CannotStabilizeDeprecated {
243-
span,
244-
item_sp,
245-
});
246-
break;
247-
}
248-
Ordering::Equal => continue,
249-
Ordering::Greater => break,
250-
},
251-
Err(_) => {
252-
if dep_v != "TBD" {
253-
self.tcx.sess.emit_err(errors::InvalidDeprecationVersion {
254-
span,
255-
item_sp,
256-
});
257-
}
258-
break;
259-
}
260-
}
231+
StableSince::Version(stab_since) => {
232+
if dep_since < stab_since {
233+
self.tcx
234+
.sess
235+
.emit_err(errors::CannotStabilizeDeprecated { span, item_sp });
261236
}
262237
}
263-
Since::Err => {
238+
StableSince::Err => {
264239
// An error already reported. Assume the unparseable stabilization
265240
// version is older than the deprecation version.
266241
}

0 commit comments

Comments
 (0)