Skip to content

Commit

Permalink
Store version of deprecated attribute in structured form
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Oct 30, 2023
1 parent 5c7cf83 commit 2fe7d17
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 105 deletions.
61 changes: 45 additions & 16 deletions compiler/rustc_attr/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
46 changes: 12 additions & 34 deletions compiler/rustc_middle/src/middle/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand All @@ -180,17 160,15 @@ 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,
) -> String {
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!(
Expand Down Expand Up @@ -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.
Expand Down
49 changes: 15 additions & 34 deletions compiler/rustc_passes/src/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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 });
}
Expand All @@ -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 => {
Expand Down
13 changes: 5 additions & 8 deletions src/librustdoc/html/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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")
Expand Down
3 changes: 1 addition & 2 deletions src/librustdoc/json/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()) }
}

Expand Down
5 changes: 2 additions & 3 deletions tests/ui/stability-attribute/stability-attribute-sanity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
13 changes: 5 additions & 8 deletions tests/ui/stability-attribute/stability-attribute-sanity.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down

0 comments on commit 2fe7d17

Please sign in to comment.