about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Tolnay <dtolnay@gmail.com>2023-10-26 10:14:29 -0700
committerDavid Tolnay <dtolnay@gmail.com>2023-10-29 22:42:32 -0700
commit2fe7d17bd9aabe3948b8693c2a3d4841ce9fbd14 (patch)
treec8006b654e380b47fdea85fea88f84cb3e9b8d37
parent5c7cf837395db7221bd1061eee230fd27f81e6ad (diff)
downloadrust-2fe7d17bd9aabe3948b8693c2a3d4841ce9fbd14.tar.gz
rust-2fe7d17bd9aabe3948b8693c2a3d4841ce9fbd14.zip
Store version of `deprecated` attribute in structured form
-rw-r--r--compiler/rustc_attr/src/builtin.rs61
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs46
-rw-r--r--compiler/rustc_passes/src/stability.rs49
-rw-r--r--src/librustdoc/html/render/mod.rs13
-rw-r--r--src/librustdoc/json/conversions.rs3
-rw-r--r--tests/ui/stability-attribute/stability-attribute-sanity.rs5
-rw-r--r--tests/ui/stability-attribute/stability-attribute-sanity.stderr13
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<Symbol>,
+    pub since: Option<DeprecatedSince>,
     /// 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>,
+}
+
+/// 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),
+}
 
-    /// Whether to treat the since attribute as being a Rust version identifier
-    /// (rather than an opaque string).
-    pub is_since_rustc_version: bool,
+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<u16> =
-            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<Symbol>,
+    since: Option<DeprecatedSince>,
     note: Option<Symbol>,
     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::<u64>() {
-                                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<ShortItemInfo> {
     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")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^