Skip to content

Refactor deprecation attributes #82432

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 103 additions & 42 deletions compiler/rustc_attr/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use rustc_session::parse::{feature_err, ParseSess};
use rustc_session::Session;
use rustc_span::hygiene::Transparency;
use rustc_span::{symbol::sym, symbol::Symbol, Span};
use std::fmt;
use std::num::NonZeroU32;

pub fn is_builtin_attr(attr: &Attribute) -> bool {
Expand All @@ -18,7 +19,7 @@ pub fn is_builtin_attr(attr: &Attribute) -> bool {
enum AttrError {
MultipleItem(String),
UnknownMetaItem(String, &'static [&'static str]),
MissingSince,
InvalidSince,
NonIdentFeature,
MissingFeature,
MultipleStabilityLevels,
Expand All @@ -37,8 +38,8 @@ fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) {
.span_label(span, format!("expected one of {}", expected.join(", ")))
.emit();
}
AttrError::MissingSince => {
struct_span_err!(diag, span, E0542, "missing 'since'").emit();
AttrError::InvalidSince => {
struct_span_err!(diag, span, E0542, "invalid 'since'").emit();
}
AttrError::NonIdentFeature => {
struct_span_err!(diag, span, E0546, "'feature' is not an identifier").emit();
Expand Down Expand Up @@ -158,7 +159,7 @@ pub struct ConstStability {
pub enum StabilityLevel {
// Reason for the current stability level and the relevant rust-lang issue
Unstable { reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool },
Stable { since: Symbol },
Stable { since: Version },
}

impl StabilityLevel {
Expand Down Expand Up @@ -428,7 +429,7 @@ where
}
}

match (feature, since) {
match (feature, since.and_then(|s| parse_version(&s.as_str(), false))) {
(Some(feature), Some(since)) => {
let level = Stable { since };
if sym::stable == meta_name {
Expand All @@ -443,7 +444,7 @@ where
continue;
}
_ => {
handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince);
handle_errors(&sess.parse_sess, attr.span, AttrError::InvalidSince);
continue;
}
}
Expand Down Expand Up @@ -525,11 +526,18 @@ fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &F
}
}

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Version {
major: u16,
minor: u16,
patch: u16,
#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(HashStable_Generic)]
pub struct Version {
major: u8,
minor: u8,
patch: u8,
}

impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
}
}

fn parse_version(s: &str, allow_appendix: bool) -> Option<Version> {
Expand Down Expand Up @@ -648,30 +656,56 @@ pub fn eval_condition(
}

#[derive(Debug, Encodable, Decodable, Clone, HashStable_Generic)]
pub struct Deprecation {
pub since: Option<Symbol>,
/// The note to issue a reason.
pub note: Option<Symbol>,
/// A text snippet used to completely replace any use of the deprecated item in an expression.
///
/// This is currently unstable.
pub suggestion: Option<Symbol>,

/// Whether to treat the since attribute as being a Rust version identifier
/// (rather than an opaque string).
pub is_since_rustc_version: bool,
pub enum DeprKind {
/// The stable #[deprecated] attribute
Deprecated {
/// Opaque, unvalidated string
since: Option<Symbol>,
/// Note displayed alongside deprecation warning
note: Option<Symbol>,
},
/// The compiler-only #[rustc_deprecated] attribute
RustcDeprecated {
/// Whether or not the deprecation is currently in effect,
/// i.e. whether `since` is at least the current Rust version.
now: bool,
/// `None` if `since="TBD"`, otherwise a Rust version triple "X.Y.Z"
since: Option<Version>,
/// Note displayed alongside deprecation warning
note: Symbol,
/// A text snippet used to completely replace any use
/// of the deprecated item in an expression.
/// Currently unstable.
suggestion: Option<Symbol>,
},
}

impl DeprKind {
pub fn note(&self) -> Option<Symbol> {
match *self {
Self::Deprecated { note, .. } => note,
Self::RustcDeprecated { note, .. } => Some(note),
}
}

pub fn suggestion(&self) -> Option<Symbol> {
match *self {
Self::Deprecated { .. } => None,
Self::RustcDeprecated { suggestion, .. } => suggestion,
}
}
}

/// Finds the deprecation attribute. `None` if none exists.
pub fn find_deprecation(sess: &Session, attrs: &[Attribute]) -> Option<(Deprecation, Span)> {
pub fn find_deprecation(sess: &Session, attrs: &[Attribute]) -> Option<(DeprKind, Span)> {
find_deprecation_generic(sess, attrs.iter())
}

fn find_deprecation_generic<'a, I>(sess: &Session, attrs_iter: I) -> Option<(Deprecation, Span)>
fn find_deprecation_generic<'a, I>(sess: &Session, attrs_iter: I) -> Option<(DeprKind, Span)>
where
I: Iterator<Item = &'a Attribute>,
{
let mut depr: Option<(Deprecation, Span)> = None;
let mut depr: Option<(DeprKind, Span)> = None;
let diagnostic = &sess.parse_sess.span_diagnostic;

'outer: for attr in attrs_iter {
Expand Down Expand Up @@ -786,26 +820,53 @@ where
}
}

if suggestion.is_some() && sess.check_name(attr, sym::deprecated) {
unreachable!("only allowed on rustc_deprecated")
}
depr = if sess.check_name(attr, sym::deprecated) {
Some((DeprKind::Deprecated { since, note }, attr.span))
} else {
let since = match since {
None => {
// `since` field must be present
handle_errors(&sess.parse_sess, attr.span, AttrError::InvalidSince);
continue;
}
Some(since_sym) => {
if since_sym == sym::TBD {
None // "TBD" is the only non-version allowed for the `since` field
} else {
if let Some(since_ver) = parse_version(&since_sym.as_str(), false) {
Some(since_ver)
} else {
// `since` field must be a valid version
handle_errors(&sess.parse_sess, attr.span, AttrError::InvalidSince);
continue;
}
}
}
};

if sess.check_name(attr, sym::rustc_deprecated) {
if since.is_none() {
handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince);
continue;
}
let note = match note {
None => {
struct_span_err!(diagnostic, attr.span, E0543, "missing 'reason'").emit();
continue;
}
Some(n) => n,
};

if note.is_none() {
struct_span_err!(diagnostic, attr.span, E0543, "missing 'reason'").emit();
continue;
}
}
// Whether the deprecation is currently active
let now = match since {
None => false, // "TBD", therefore deprecated_in_future
Some(since_ver) => {
match option_env!("CFG_RELEASE").and_then(|s| parse_version(s, true)) {
None => true, // if invalid Rust version, assume deprecation is in effect
Some(rust_ver) => rust_ver > since_ver,
}
}
};

sess.mark_attr_used(&attr);
Some((DeprKind::RustcDeprecated { now, since, note, suggestion }, attr.span))
};

let is_since_rustc_version = sess.check_name(attr, sym::rustc_deprecated);
depr = Some((Deprecation { since, note, suggestion, is_since_rustc_version }, attr.span));
sess.mark_attr_used(&attr);
}

depr
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_attr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ extern crate rustc_macros;
mod builtin;

pub use builtin::*;
pub use DeprKind::*;
pub use IntType::*;
pub use ReprAttr::*;
pub use StabilityLevel::*;
Expand Down
50 changes: 28 additions & 22 deletions compiler/rustc_error_codes/src/error_codes/E0542.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,48 @@
The `since` value is missing in a stability attribute.
The `since` value in a stability attribute is either missing
or does not parse to a version string.

Erroneous code example:

```compile_fail,E0542
#![feature(staged_api)]
#![stable(since = "1.0.0", feature = "test")]
#![stable(since = "1.0.0", feature = "foo")]

#[stable(feature = "_stable_fn")] // invalid
fn _stable_fn() {}
#[stable(feature = "foo")] // missing
fn _stable1() {}

#[rustc_const_stable(feature = "_stable_const_fn")] // invalid
fn _stable_const_fn() {}
#[stable(feature = "foo", since = "bar")] // invalid
fn _stable2() {}

#[stable(feature = "_deprecated_fn", since = "0.1.0")]
#[rustc_deprecated(
reason = "explanation for deprecation"
)] // invalid
fn _deprecated_fn() {}
#[rustc_const_stable(feature = "foo")] // missing
fn _const1() {}

#[rustc_const_stable(feature = "foo", since = "bar")] // invalid
fn _const2() {}

#[stable(feature = "foo", since = "1.0.0")]
#[rustc_deprecated(reason = "bar")] // missing
fn _deprecated1() {}

#[stable(feature = "foo", since = "1.0.0")]
#[rustc_deprecated(reason = "qux", since = "bar")] // invalid
fn _deprecated2() {}
```

To fix this issue, you need to provide the `since` field. Example:

```
#![feature(staged_api)]
#![stable(since = "1.0.0", feature = "test")]
#![stable(since = "1.0.0", feature = "foo")]

#[stable(feature = "_stable_fn", since = "1.0.0")] // ok!
fn _stable_fn() {}
#[stable(feature = "foo", since = "1.0.0")] // ok!
fn _stable() {}

#[rustc_const_stable(feature = "_stable_const_fn", since = "1.0.0")] // ok!
fn _stable_const_fn() {}
#[rustc_const_stable(feature = "foo", since = "1.0.0")] // ok!
fn _const() {}

#[stable(feature = "_deprecated_fn", since = "0.1.0")]
#[rustc_deprecated(
since = "1.0.0",
reason = "explanation for deprecation"
)] // ok!
fn _deprecated_fn() {}
#[stable(feature = "foo", since = "1.0.0")]
#[rustc_deprecated(reason = "qux", since = "1.0.0")] // ok!
fn _deprecated() {}
```

See the [How Rust is Made and “Nightly Rust”][how-rust-made-nightly] appendix
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_expand/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rustc_ast::token::{self, Nonterminal};
use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream};
use rustc_ast::visit::{AssocCtxt, Visitor};
use rustc_ast::{self as ast, Attribute, NodeId, PatKind};
use rustc_attr::{self as attr, Deprecation, HasAttrs, Stability};
use rustc_attr::{self as attr, DeprKind, HasAttrs, Stability};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::{self, Lrc};
use rustc_errors::{DiagnosticBuilder, ErrorReported};
Expand Down Expand Up @@ -705,7 +705,7 @@ pub struct SyntaxExtension {
/// The macro's stability info.
pub stability: Option<Stability>,
/// The macro's deprecation info.
pub deprecation: Option<Deprecation>,
pub deprecation: Option<DeprKind>,
/// Names of helper attributes registered by this macro.
pub helper_attrs: Vec<Symbol>,
/// Edition of the crate in which this macro is defined.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_metadata/src/rmeta/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
self.root.tables.const_stability.get(self, id).map(|stab| stab.decode(self))
}

fn get_deprecation(&self, id: DefIndex) -> Option<attr::Deprecation> {
fn get_deprecation(&self, id: DefIndex) -> Option<attr::DeprKind> {
self.root.tables.deprecation.get(self, id).map(|depr| depr.decode(self))
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ define_tables! {
children: Table<DefIndex, Lazy<[DefIndex]>>,
stability: Table<DefIndex, Lazy<attr::Stability>>,
const_stability: Table<DefIndex, Lazy<attr::ConstStability>>,
deprecation: Table<DefIndex, Lazy<attr::Deprecation>>,
deprecation: Table<DefIndex, Lazy<attr::DeprKind>>,
ty: Table<DefIndex, Lazy!(Ty<'tcx>)>,
fn_sig: Table<DefIndex, Lazy!(ty::PolyFnSig<'tcx>)>,
impl_trait_ref: Table<DefIndex, Lazy!(ty::TraitRef<'tcx>)>,
Expand Down
Loading