Skip to content

Commit

Permalink
Auto merge of #117377 - dtolnay:deprecatedsince, r=cjgillot
Browse files Browse the repository at this point in the history
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`.
  • Loading branch information
bors committed Oct 31, 2023
2 parents ffb7ed9 + 8b8906b commit 22b2712
Show file tree
Hide file tree
Showing 14 changed files with 166 additions and 196 deletions.
84 changes: 62 additions & 22 deletions compiler/rustc_attr/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ pub enum StabilityLevel {
/// `#[stable]`
Stable {
/// Rust release which stabilized this feature.
since: Since,
since: StableSince,
/// Is this item allowed to be referred to on stable, despite being contained in unstable
/// modules?
allowed_through_unstable_modules: bool,
Expand All @@ -149,7 +149,7 @@ pub enum StabilityLevel {
/// Rust release in which a feature is stabilized.
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
#[derive(HashStable_Generic)]
pub enum Since {
pub enum StableSince {
Version(RustcVersion),
/// Stabilized in the upcoming version, whatever number that is.
Current,
Expand Down Expand Up @@ -378,16 +378,16 @@ fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, Stabilit

let since = if let Some(since) = since {
if since.as_str() == VERSION_PLACEHOLDER {
Since::Current
StableSince::Current
} else if let Some(version) = parse_version(since) {
Since::Version(version)
StableSince::Version(version)
} else {
sess.emit_err(session_diagnostics::InvalidSince { span: attr.span });
Since::Err
StableSince::Err
}
} else {
sess.emit_err(session_diagnostics::MissingSince { span: attr.span });
Since::Err
StableSince::Err
};

match feature {
Expand Down Expand Up @@ -720,17 +720,49 @@ pub fn eval_condition(

#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
pub struct Deprecation {
pub since: Option<Symbol>,
pub since: 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. Deprecation versions outside the standard
/// library are allowed to be arbitrary strings, for better or worse.
NonStandard(Symbol),
/// Deprecation version is unspecified but optional.
Unspecified,
/// Failed to parse a deprecation version, or the deprecation version is
/// unspecified and required. An error has already been emitted.
Err,
}

impl Deprecation {
/// 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 is_in_effect(&self) -> bool {
match self.since {
DeprecatedSince::RustcVersion(since) => since <= RustcVersion::CURRENT,
DeprecatedSince::Future => false,
// The `since` field doesn't have semantic purpose without `#![staged_api]`.
DeprecatedSince::NonStandard(_) => true,
// Assume deprecation is in effect if "since" field is absent or invalid.
DeprecatedSince::Unspecified | DeprecatedSince::Err => true,
}
}

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

/// Finds the deprecation attribute. `None` if none exists.
Expand Down Expand Up @@ -839,22 +871,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" {
DeprecatedSince::Future
} else if !is_rustc {
DeprecatedSince::NonStandard(since)
} else if let Some(version) = parse_version(since) {
DeprecatedSince::RustcVersion(version)
} else {
sess.emit_err(session_diagnostics::InvalidSince { span: attr.span });
DeprecatedSince::Err
}
} else if is_rustc {
sess.emit_err(session_diagnostics::MissingSince { span: attr.span });
DeprecatedSince::Err
} else {
DeprecatedSince::Unspecified
};

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
72 changes: 20 additions & 52 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 All @@ -16,7 +18,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
use rustc_session::lint::{BuiltinLintDiagnostics, Level, Lint, LintBuffer};
use rustc_session::parse::feature_err_issue;
use rustc_session::{RustcVersion, Session};
use rustc_session::Session;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use std::num::NonZeroU32;
Expand Down Expand Up @@ -123,41 +125,6 @@ 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 {
// The `since` field doesn't have semantic purpose without `#![staged_api]`.
return 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(
diag: &mut Diagnostic,
kind: &str,
Expand All @@ -180,25 +147,26 @@ fn deprecation_lint(is_in_effect: bool) -> &'static Lint {

fn deprecation_message(
is_in_effect: bool,
since: Option<Symbol>,
since: 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") {
format!("use of {kind} `{path}` that will be deprecated in a future Rust version")
} else {
format!(
"use of {} `{}` that will be deprecated in future version {}",
kind,
path,
since.unwrap()
)
match since {
DeprecatedSince::RustcVersion(version) => format!(
"use of {kind} `{path}` that will be deprecated in future version {version}"
),
DeprecatedSince::Future => {
format!("use of {kind} `{path}` that will be deprecated in a future Rust version")
}
DeprecatedSince::NonStandard(_)
| DeprecatedSince::Unspecified
| DeprecatedSince::Err => {
unreachable!("this deprecation is always in effect; {since:?}")
}
}
};

Expand All @@ -213,7 +181,7 @@ pub fn deprecation_message_and_lint(
kind: &str,
path: &str,
) -> (String, &'static Lint) {
let is_in_effect = deprecation_in_effect(depr);
let is_in_effect = depr.is_in_effect();
(
deprecation_message(is_in_effect, depr.since, depr.note, kind, path),
deprecation_lint(is_in_effect),
Expand Down Expand Up @@ -381,11 +349,11 @@ 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 || depr_attr.is_since_rustc_version() {
// 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.
let is_in_effect = deprecation_in_effect(depr_attr);
let is_in_effect = depr_attr.is_in_effect();
let lint = deprecation_lint(is_in_effect);
if self.lint_level_at_node(lint, id).0 != Level::Allow {
let def_path = with_no_trimmed_paths!(self.def_path_str(def_id));
Expand Down
5 changes: 0 additions & 5 deletions compiler/rustc_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -396,11 +396,6 @@ passes_invalid_attr_at_crate_level =
passes_invalid_attr_at_crate_level_item =
the inner attribute doesn't annotate this {$kind}
passes_invalid_deprecation_version =
invalid deprecation version found
.label = invalid deprecation version
.item = the stability attribute annotates this item
passes_invalid_macro_export_arguments = `{$name}` isn't a valid `#[macro_export]` argument
passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments
Expand Down
10 changes: 0 additions & 10 deletions compiler/rustc_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1524,16 +1524,6 @@ pub struct CannotStabilizeDeprecated {
pub item_sp: Span,
}

#[derive(Diagnostic)]
#[diag(passes_invalid_deprecation_version)]
pub struct InvalidDeprecationVersion {
#[primary_span]
#[label]
pub span: Span,
#[label(passes_item)]
pub item_sp: Span,
}

#[derive(Diagnostic)]
#[diag(passes_missing_stability_attr)]
pub struct MissingStabilityAttr<'a> {
Expand Down
55 changes: 15 additions & 40 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, Since, Stability, StabilityLevel, 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,10 +196,8 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
}
}

if let Some((rustc_attr::Deprecation { is_since_rustc_version: true, .. }, span)) = &depr {
if stab.is_none() {
self.tcx.sess.emit_err(errors::DeprecatedAttribute { span: *span });
}
if let Some((depr, span)) = &depr && depr.is_since_rustc_version() && stab.is_none() {
self.tcx.sess.emit_err(errors::DeprecatedAttribute { span: *span });
}

if let Some((body_stab, _span)) = body_stab {
Expand All @@ -223,44 +219,23 @@ 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().map(|(d, _)| d.since), &stab.level)
{
match stab_since {
Since::Current => {
StableSince::Current => {
self.tcx.sess.emit_err(errors::CannotStabilizeDeprecated { span, item_sp });
}
Since::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;
}
}
StableSince::Version(stab_since) => {
if dep_since < stab_since {
self.tcx
.sess
.emit_err(errors::CannotStabilizeDeprecated { span, item_sp });
}
}
Since::Err => {
StableSince::Err => {
// An error already reported. Assume the unparseable stabilization
// version is older than the deprecation version.
}
Expand Down
Loading

0 comments on commit 22b2712

Please sign in to comment.