From 2fe7d17bd9aabe3948b8693c2a3d4841ce9fbd14 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 26 Oct 2023 10:14:29 -0700 Subject: [PATCH] Store version of `deprecated` attribute in structured form --- compiler/rustc_attr/src/builtin.rs | 61 ++++++++++++++----- compiler/rustc_middle/src/middle/stability.rs | 46 ++++---------- compiler/rustc_passes/src/stability.rs | 49 +++++---------- src/librustdoc/html/render/mod.rs | 13 ++-- src/librustdoc/json/conversions.rs | 3 +- .../stability-attribute-sanity.rs | 5 +- .../stability-attribute-sanity.stderr | 13 ++-- 7 files changed, 85 insertions(+), 105 deletions(-) diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 592c9ff115b..62709ef4db9 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -13,6 +13,7 @@ use rustc_session::parse::{feature_err, ParseSess}; use rustc_session::{RustcVersion, Session}; use rustc_span::hygiene::Transparency; use rustc_span::{symbol::sym, symbol::Symbol, Span}; +use std::fmt::{self, Display}; use std::num::NonZeroU32; use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; @@ -720,17 +721,37 @@ pub fn eval_condition( #[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)] pub struct Deprecation { - pub since: Option, + pub since: Option, /// The note to issue a reason. pub note: Option, /// A text snippet used to completely replace any use of the deprecated item in an expression. /// /// This is currently unstable. pub suggestion: Option, +} - /// Whether to treat the since attribute as being a Rust version identifier - /// (rather than an opaque string). - pub is_since_rustc_version: bool, +/// Release in which an API is deprecated. +#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)] +pub enum DeprecatedSince { + RustcVersion(RustcVersion), + /// Deprecated in the future ("to be determined"). + Future, + /// `feature(staged_api)` is off, or it's on but the deprecation version + /// cannot be parsed as a RustcVersion. In the latter case, an error has + /// already been emitted. In the former case, deprecation versions outside + /// the standard library are allowed to be arbitrary strings, for better or + /// worse. + Symbol(Symbol), +} + +impl Display for DeprecatedSince { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DeprecatedSince::RustcVersion(since) => Display::fmt(since, formatter), + DeprecatedSince::Future => formatter.write_str("TBD"), + DeprecatedSince::Symbol(since) => Display::fmt(since, formatter), + } + } } /// Finds the deprecation attribute. `None` if none exists. @@ -839,22 +860,30 @@ pub fn find_deprecation( } } - if is_rustc { - if since.is_none() { - sess.emit_err(session_diagnostics::MissingSince { span: attr.span }); - continue; + let since = if let Some(since) = since { + if since.as_str() == "TBD" { + Some(DeprecatedSince::Future) + } else if !is_rustc { + Some(DeprecatedSince::Symbol(since)) + } else if let Some(version) = parse_version(since) { + Some(DeprecatedSince::RustcVersion(version)) + } else { + sess.emit_err(session_diagnostics::InvalidSince { span: attr.span }); + Some(DeprecatedSince::Symbol(since)) } + } else if is_rustc { + sess.emit_err(session_diagnostics::MissingSince { span: attr.span }); + continue; + } else { + None + }; - if note.is_none() { - sess.emit_err(session_diagnostics::MissingNote { span: attr.span }); - continue; - } + if is_rustc && note.is_none() { + sess.emit_err(session_diagnostics::MissingNote { span: attr.span }); + continue; } - depr = Some(( - Deprecation { since, note, suggestion, is_since_rustc_version: is_rustc }, - attr.span, - )); + depr = Some((Deprecation { since, note, suggestion }, attr.span)); } depr diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 20547696d5a..b750352e13a 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -5,7 +5,9 @@ pub use self::StabilityLevel::*; use crate::ty::{self, TyCtxt}; use rustc_ast::NodeId; -use rustc_attr::{self as attr, ConstStability, DefaultBodyStability, Deprecation, Stability}; +use rustc_attr::{ + self as attr, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability, +}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diagnostic}; use rustc_feature::GateIssue; @@ -126,36 +128,14 @@ pub fn report_unstable( /// Checks whether an item marked with `deprecated(since="X")` is currently /// deprecated (i.e., whether X is not greater than the current rustc version). pub fn deprecation_in_effect(depr: &Deprecation) -> bool { - let is_since_rustc_version = depr.is_since_rustc_version; - let since = depr.since.as_ref().map(Symbol::as_str); - - if !is_since_rustc_version { + match depr.since { + Some(DeprecatedSince::RustcVersion(since)) => since <= RustcVersion::CURRENT, + Some(DeprecatedSince::Future) => false, // The `since` field doesn't have semantic purpose without `#![staged_api]`. - return true; + Some(DeprecatedSince::Symbol(_)) => true, + // Assume deprecation is in effect if "since" field is missing. + None => true, } - - if let Some(since) = since { - if since == "TBD" { - return false; - } - - // We ignore non-integer components of the version (e.g., "nightly"). - let since: Vec = - since.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect(); - - // We simply treat invalid `since` attributes as relating to a previous - // Rust version, thus always displaying the warning. - if since.len() != 3 { - return true; - } - - let rustc = RustcVersion::CURRENT; - return since.as_slice() <= &[rustc.major, rustc.minor, rustc.patch]; - }; - - // Assume deprecation is in effect if "since" field is missing - // or if we can't determine the current Rust version. - true } pub fn deprecation_suggestion( @@ -180,7 +160,7 @@ fn deprecation_lint(is_in_effect: bool) -> &'static Lint { fn deprecation_message( is_in_effect: bool, - since: Option, + since: Option, note: Option, kind: &str, path: &str, @@ -188,9 +168,7 @@ fn deprecation_message( let message = if is_in_effect { format!("use of deprecated {kind} `{path}`") } else { - let since = since.as_ref().map(Symbol::as_str); - - if since == Some("TBD") { + if let Some(DeprecatedSince::Future) = since { format!("use of {kind} `{path}` that will be deprecated in a future Rust version") } else { format!( @@ -381,7 +359,7 @@ impl<'tcx> TyCtxt<'tcx> { // With #![staged_api], we want to emit down the whole // hierarchy. let depr_attr = &depr_entry.attr; - if !skip || depr_attr.is_since_rustc_version { + if !skip || matches!(depr_attr.since, Some(DeprecatedSince::RustcVersion(_))) { // Calculating message for lint involves calling `self.def_path_str`. // Which by default to calculate visible path will invoke expensive `visible_parent_map` query. // So we skip message calculation altogether, if lint is allowed. diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index a3f0f2d6512..675fe6c5d42 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -3,8 +3,8 @@ use crate::errors; use rustc_attr::{ - self as attr, ConstStability, Stability, StabilityLevel, StableSince, Unstable, UnstableReason, - VERSION_PLACEHOLDER, + self as attr, ConstStability, DeprecatedSince, Stability, StabilityLevel, StableSince, + Unstable, UnstableReason, VERSION_PLACEHOLDER, }; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_hir as hir; @@ -24,8 +24,6 @@ use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; use rustc_target::spec::abi::Abi; -use std::cmp::Ordering; -use std::iter; use std::mem::replace; use std::num::NonZeroU32; @@ -198,7 +196,11 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } - if let Some((rustc_attr::Deprecation { is_since_rustc_version: true, .. }, span)) = &depr { + if let Some(( + rustc_attr::Deprecation { since: Some(DeprecatedSince::RustcVersion(_)), .. }, + span, + )) = &depr + { if stab.is_none() { self.tcx.sess.emit_err(errors::DeprecatedAttribute { span: *span }); } @@ -223,41 +225,20 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // Check if deprecated_since < stable_since. If it is, // this is *almost surely* an accident. - if let (&Some(dep_since), &attr::Stable { since: stab_since, .. }) = - (&depr.as_ref().and_then(|(d, _)| d.since), &stab.level) + if let ( + &Some(DeprecatedSince::RustcVersion(dep_since)), + &attr::Stable { since: stab_since, .. }, + ) = (&depr.as_ref().and_then(|(d, _)| d.since), &stab.level) { match stab_since { StableSince::Current => { self.tcx.sess.emit_err(errors::CannotStabilizeDeprecated { span, item_sp }); } StableSince::Version(stab_since) => { - // Explicit version of iter::order::lt to handle parse errors properly - for (dep_v, stab_v) in iter::zip( - dep_since.as_str().split('.'), - [stab_since.major, stab_since.minor, stab_since.patch], - ) { - match dep_v.parse::() { - Ok(dep_vp) => match dep_vp.cmp(&u64::from(stab_v)) { - Ordering::Less => { - self.tcx.sess.emit_err(errors::CannotStabilizeDeprecated { - span, - item_sp, - }); - break; - } - Ordering::Equal => continue, - Ordering::Greater => break, - }, - Err(_) => { - if dep_v != "TBD" { - self.tcx.sess.emit_err(errors::InvalidDeprecationVersion { - span, - item_sp, - }); - } - break; - } - } + if dep_since < stab_since { + self.tcx + .sess + .emit_err(errors::CannotStabilizeDeprecated { span, item_sp }); } } StableSince::Err => { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 7ab78e73ba7..e852d02b6ac 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -48,7 +48,7 @@ use std::str; use std::string::ToString; use askama::Template; -use rustc_attr::{ConstStability, Deprecation, StabilityLevel, StableSince}; +use rustc_attr::{ConstStability, DeprecatedSince, Deprecation, StabilityLevel, StableSince}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{DefId, DefIdSet}; @@ -617,21 +617,18 @@ fn short_item_info( ) -> Vec { let mut extra_info = vec![]; - if let Some(depr @ Deprecation { note, since, is_since_rustc_version: _, suggestion: _ }) = - item.deprecation(cx.tcx()) - { + if let Some(depr @ Deprecation { note, since, suggestion: _ }) = item.deprecation(cx.tcx()) { // We display deprecation messages for #[deprecated], but only display // the future-deprecation messages for rustc versions. let mut message = if let Some(since) = since { - let since = since.as_str(); if !stability::deprecation_in_effect(&depr) { - if since == "TBD" { + if let DeprecatedSince::Future = since { String::from("Deprecating in a future Rust version") } else { - format!("Deprecating in {}", Escape(since)) + format!("Deprecating in {}", Escape(&since.to_string())) } } else { - format!("Deprecated since {}", Escape(since)) + format!("Deprecated since {}", Escape(&since.to_string())) } } else { String::from("Deprecated") diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 563e0cffddd..ff2e6b1ebc9 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -138,8 +138,7 @@ where } pub(crate) fn from_deprecation(deprecation: rustc_attr::Deprecation) -> Deprecation { - #[rustfmt::skip] - let rustc_attr::Deprecation { since, note, is_since_rustc_version: _, suggestion: _ } = deprecation; + let rustc_attr::Deprecation { since, note, suggestion: _ } = deprecation; Deprecation { since: since.map(|s| s.to_string()), note: note.map(|s| s.to_string()) } } diff --git a/tests/ui/stability-attribute/stability-attribute-sanity.rs b/tests/ui/stability-attribute/stability-attribute-sanity.rs index 7030bcd3aba..7857a0603bd 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity.rs +++ b/tests/ui/stability-attribute/stability-attribute-sanity.rs @@ -64,9 +64,8 @@ fn multiple3() { } #[rustc_const_unstable(feature = "d", issue = "none")] //~ ERROR multiple stability levels pub const fn multiple4() { } -#[stable(feature = "a", since = "1.0.0")] //~ ERROR invalid deprecation version found -//~^ ERROR feature `a` is declared stable since 1.0.0 -#[deprecated(since = "invalid", note = "text")] +#[stable(feature = "a", since = "1.0.0")] //~ ERROR feature `a` is declared stable since 1.0.0 +#[deprecated(since = "invalid", note = "text")] //~ ERROR 'since' must be a Rust version number, such as "1.31.0" fn invalid_deprecation_version() {} #[deprecated(since = "5.5.5", note = "text")] diff --git a/tests/ui/stability-attribute/stability-attribute-sanity.stderr b/tests/ui/stability-attribute/stability-attribute-sanity.stderr index 27cd451f989..c614fc2b9f7 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity.stderr +++ b/tests/ui/stability-attribute/stability-attribute-sanity.stderr @@ -106,17 +106,14 @@ error[E0544]: multiple stability levels LL | #[rustc_const_unstable(feature = "d", issue = "none")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: invalid deprecation version found - --> $DIR/stability-attribute-sanity.rs:67:1 +error: 'since' must be a Rust version number, such as "1.31.0" + --> $DIR/stability-attribute-sanity.rs:68:1 | -LL | #[stable(feature = "a", since = "1.0.0")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid deprecation version -... -LL | fn invalid_deprecation_version() {} - | ----------------------------------- the stability attribute annotates this item +LL | #[deprecated(since = "invalid", note = "text")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0549]: deprecated attribute must be paired with either stable or unstable attribute - --> $DIR/stability-attribute-sanity.rs:72:1 + --> $DIR/stability-attribute-sanity.rs:71:1 | LL | #[deprecated(since = "5.5.5", note = "text")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^