Skip to content
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

Store #[deprecated] attribute's since value in parsed form #117377

Merged
merged 10 commits into from
Oct 31, 2023
Merged
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.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "or if we can't determine the current Rust version" part of this comment should have been removed by #117256.

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
Loading