Rollup merge of #74572 - Mark-Simulacrum:unify-rustc-depr, r=petrochenkov

Internally unify rustc_deprecated and deprecated

This PR intentionally tries to be "featureless" in that the behavior is not altered for either attribute, though it more clearly exposes cases where that is the case in the code.
This commit is contained in:
Yuki Okushi 2020-07-24 18:56:27 +09:00 committed by GitHub
commit 0651dd4aab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 196 additions and 245 deletions

View File

@ -124,13 +124,11 @@ pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Op
///
/// - `#[stable]`
/// - `#[unstable]`
/// - `#[rustc_deprecated]`
#[derive(RustcEncodable, RustcDecodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(HashStable_Generic)]
pub struct Stability {
pub level: StabilityLevel,
pub feature: Symbol,
pub rustc_depr: Option<RustcDeprecation>,
}
/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes.
@ -163,15 +161,6 @@ impl StabilityLevel {
}
}
#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Copy, Clone, Debug, Eq, Hash)]
#[derive(HashStable_Generic)]
pub struct RustcDeprecation {
pub since: Symbol,
pub reason: Symbol,
/// A text snippet used to completely replace any use of the deprecated item in an expression.
pub suggestion: Option<Symbol>,
}
/// Checks if `attrs` contains an attribute like `#![feature(feature_name)]`.
/// This will not perform any "sanity checks" on the form of the attributes.
pub fn contains_feature_attr(attrs: &[Attribute], feature_name: Symbol) -> bool {
@ -205,7 +194,6 @@ where
use StabilityLevel::*;
let mut stab: Option<Stability> = None;
let mut rustc_depr: Option<RustcDeprecation> = None;
let mut const_stab: Option<ConstStability> = None;
let mut promotable = false;
let mut allow_const_fn_ptr = false;
@ -213,7 +201,6 @@ where
'outer: for attr in attrs_iter {
if ![
sym::rustc_deprecated,
sym::rustc_const_unstable,
sym::rustc_const_stable,
sym::unstable,
@ -258,76 +245,8 @@ where
}
};
macro_rules! get_meta {
($($name:ident),+) => {
$(
let mut $name = None;
)+
for meta in metas {
if let Some(mi) = meta.meta_item() {
match mi.name_or_empty() {
$(
sym::$name => if !get(mi, &mut $name) { continue 'outer },
)+
_ => {
let expected = &[ $( stringify!($name) ),+ ];
handle_errors(
sess,
mi.span,
AttrError::UnknownMetaItem(
pprust::path_to_string(&mi.path),
expected,
),
);
continue 'outer
}
}
} else {
handle_errors(
sess,
meta.span(),
AttrError::UnsupportedLiteral(
"unsupported literal",
false,
),
);
continue 'outer
}
}
}
}
let meta_name = meta.name_or_empty();
match meta_name {
sym::rustc_deprecated => {
if rustc_depr.is_some() {
struct_span_err!(
diagnostic,
item_sp,
E0540,
"multiple rustc_deprecated attributes"
)
.emit();
continue 'outer;
}
get_meta!(since, reason, suggestion);
match (since, reason) {
(Some(since), Some(reason)) => {
rustc_depr = Some(RustcDeprecation { since, reason, suggestion })
}
(None, _) => {
handle_errors(sess, attr.span, AttrError::MissingSince);
continue;
}
_ => {
struct_span_err!(diagnostic, attr.span, E0543, "missing 'reason'")
.emit();
continue;
}
}
}
sym::rustc_const_unstable | sym::unstable => {
if meta_name == sym::unstable && stab.is_some() {
handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
@ -429,7 +348,7 @@ where
(Some(feature), reason, Some(_)) => {
let level = Unstable { reason, issue: issue_num, is_soft };
if sym::unstable == meta_name {
stab = Some(Stability { level, feature, rustc_depr: None });
stab = Some(Stability { level, feature });
} else {
const_stab = Some(ConstStability {
level,
@ -501,7 +420,7 @@ where
(Some(feature), Some(since)) => {
let level = Stable { since };
if sym::stable == meta_name {
stab = Some(Stability { level, feature, rustc_depr: None });
stab = Some(Stability { level, feature });
} else {
const_stab = Some(ConstStability {
level,
@ -526,22 +445,6 @@ where
}
}
// Merge the deprecation info into the stability info
if let Some(rustc_depr) = rustc_depr {
if let Some(ref mut stab) = stab {
stab.rustc_depr = Some(rustc_depr);
} else {
struct_span_err!(
diagnostic,
item_sp,
E0549,
"rustc_deprecated attribute must be paired with \
either stable or unstable attribute"
)
.emit();
}
}
// Merge the const-unstable info into the stability info
if promotable || allow_const_fn_ptr {
if let Some(ref mut stab) = const_stab {
@ -714,7 +617,16 @@ pub fn eval_condition(
#[derive(RustcEncodable, RustcDecodable, Clone, HashStable_Generic)]
pub struct Deprecation {
pub since: Option<Symbol>,
/// 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>,
/// Whether to treat the since attribute as being a Rust version identifier
/// (rather than an opaque string).
pub is_since_rustc_version: bool,
}
/// Finds the deprecation attribute. `None` if none exists.
@ -738,7 +650,7 @@ where
let diagnostic = &sess.span_diagnostic;
'outer: for attr in attrs_iter {
if !attr.check_name(sym::deprecated) {
if !(attr.check_name(sym::deprecated) || attr.check_name(sym::rustc_deprecated)) {
continue;
}
@ -751,11 +663,12 @@ where
Some(meta) => meta,
None => continue,
};
depr = match &meta.kind {
MetaItemKind::Word => Some(Deprecation { since: None, note: None }),
MetaItemKind::NameValue(..) => {
meta.value_str().map(|note| Deprecation { since: None, note: Some(note) })
}
let mut since = None;
let mut note = None;
let mut suggestion = None;
match &meta.kind {
MetaItemKind::Word => {}
MetaItemKind::NameValue(..) => note = meta.value_str(),
MetaItemKind::List(list) => {
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
if item.is_some() {
@ -789,8 +702,6 @@ where
}
};
let mut since = None;
let mut note = None;
for meta in list {
match meta {
NestedMetaItem::MetaItem(mi) => match mi.name_or_empty() {
@ -799,18 +710,32 @@ where
continue 'outer;
}
}
sym::note => {
sym::note if attr.check_name(sym::deprecated) => {
if !get(mi, &mut note) {
continue 'outer;
}
}
sym::reason if attr.check_name(sym::rustc_deprecated) => {
if !get(mi, &mut note) {
continue 'outer;
}
}
sym::suggestion if attr.check_name(sym::rustc_deprecated) => {
if !get(mi, &mut suggestion) {
continue 'outer;
}
}
_ => {
handle_errors(
sess,
meta.span(),
AttrError::UnknownMetaItem(
pprust::path_to_string(&mi.path),
&["since", "note"],
if attr.check_name(sym::deprecated) {
&["since", "note"]
} else {
&["since", "reason", "suggestion"]
},
),
);
continue 'outer;
@ -829,10 +754,29 @@ where
}
}
}
Some(Deprecation { since, note })
}
};
}
if suggestion.is_some() && attr.check_name(sym::deprecated) {
unreachable!("only allowed on rustc_deprecated")
}
if attr.check_name(sym::rustc_deprecated) {
if since.is_none() {
handle_errors(sess, attr.span, AttrError::MissingSince);
continue;
}
if note.is_none() {
struct_span_err!(diagnostic, attr.span, E0543, "missing 'reason'").emit();
continue;
}
}
mark_used(&attr);
let is_since_rustc_version = attr.check_name(sym::rustc_deprecated);
depr = Some(Deprecation { since, note, suggestion, is_since_rustc_version });
}
depr

View File

@ -589,7 +589,7 @@ E0771: include_str!("./error_codes/E0771.md"),
E0521, // borrowed data escapes outside of closure
E0523,
// E0526, // shuffle indices are not constant
E0540, // multiple rustc_deprecated attributes
// E0540, // multiple rustc_deprecated attributes
E0542, // missing 'since'
E0543, // missing 'reason'
E0544, // multiple stability levels

View File

@ -5,7 +5,7 @@ pub use self::StabilityLevel::*;
use crate::ty::{self, TyCtxt};
use rustc_ast::ast::CRATE_NODE_ID;
use rustc_attr::{self as attr, ConstStability, Deprecation, RustcDeprecation, Stability};
use rustc_attr::{self as attr, ConstStability, Deprecation, Stability};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_feature::GateIssue;
@ -130,14 +130,26 @@ 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(since: &str) -> bool {
pub fn deprecation_in_effect(is_since_rustc_version: bool, since: Option<&str>) -> bool {
let since = if let Some(since) = since {
if is_since_rustc_version {
since
} else {
// We assume that the deprecation is in effect if it's not a
// rustc version.
return true;
}
} else {
// If since attribute is not set, then we're definitely in effect.
return true;
};
fn parse_version(ver: &str) -> Vec<u32> {
// We ignore non-integer components of the version (e.g., "nightly").
ver.split(|c| c == '.' || c == '-').flat_map(|s| s.parse()).collect()
}
if let Some(rustc) = option_env!("CFG_RELEASE") {
let since: Vec<u32> = parse_version(since);
let since: Vec<u32> = parse_version(&since);
let rustc: Vec<u32> = parse_version(rustc);
// We simply treat invalid `since` attributes as relating to a previous
// Rust version, thus always displaying the warning.
@ -167,31 +179,27 @@ pub fn deprecation_suggestion(
}
}
fn deprecation_message_common(message: String, reason: Option<Symbol>) -> String {
match reason {
Some(reason) => format!("{}: {}", message, reason),
None => message,
}
}
pub fn deprecation_message(depr: &Deprecation, path: &str) -> (String, &'static Lint) {
let message = format!("use of deprecated item '{}'", path);
(deprecation_message_common(message, depr.note), DEPRECATED)
}
pub fn rustc_deprecation_message(depr: &RustcDeprecation, path: &str) -> (String, &'static Lint) {
let (message, lint) = if deprecation_in_effect(&depr.since.as_str()) {
let (message, lint) = if deprecation_in_effect(
depr.is_since_rustc_version,
depr.since.map(Symbol::as_str).as_deref(),
) {
(format!("use of deprecated item '{}'", path), DEPRECATED)
} else {
(
format!(
"use of item '{}' that will be deprecated in future version {}",
path, depr.since
path,
depr.since.unwrap()
),
DEPRECATED_IN_FUTURE,
)
};
(deprecation_message_common(message, Some(depr.reason)), lint)
let message = match depr.note {
Some(reason) => format!("{}: {}", message, reason),
None => message,
};
(message, lint)
}
pub fn early_report_deprecation(
@ -289,10 +297,23 @@ impl<'tcx> TyCtxt<'tcx> {
.lookup_deprecation_entry(parent_def_id.to_def_id())
.map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry));
if !skip {
// #[deprecated] doesn't emit a notice if we're not on the
// topmost deprecation. For example, if a struct is deprecated,
// the use of a field won't be linted.
//
// #[rustc_deprecated] however wants to emit down the whole
// hierarchy.
if !skip || depr_entry.attr.is_since_rustc_version {
let (message, lint) =
deprecation_message(&depr_entry.attr, &self.def_path_str(def_id));
late_report_deprecation(self, &message, None, lint, span, id);
late_report_deprecation(
self,
&message,
depr_entry.attr.suggestion,
lint,
span,
id,
);
}
};
}
@ -310,16 +331,6 @@ impl<'tcx> TyCtxt<'tcx> {
def_id, span, stability
);
if let Some(id) = id {
if let Some(stability) = stability {
if let Some(depr) = &stability.rustc_depr {
let (message, lint) =
rustc_deprecation_message(depr, &self.def_path_str(def_id));
late_report_deprecation(self, &message, depr.suggestion, lint, span, id);
}
}
}
// Only the cross-crate scenario matters when checking unstable APIs
let cross_crate = !def_id.is_local();
if !cross_crate {

View File

@ -59,20 +59,50 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
) where
F: FnOnce(&mut Self),
{
debug!("annotate(id = {:?}, attrs = {:?})", hir_id, attrs);
let mut did_error = false;
if !self.tcx.features().staged_api {
self.forbid_staged_api_attrs(hir_id, attrs, item_sp, kind, visit_children);
return;
did_error = self.forbid_staged_api_attrs(hir_id, attrs);
}
// This crate explicitly wants staged API.
let depr = if did_error {
None
} else {
attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp)
};
let mut is_deprecated = false;
if let Some(depr) = &depr {
is_deprecated = true;
debug!("annotate(id = {:?}, attrs = {:?})", hir_id, attrs);
if let Some(..) = attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp) {
self.tcx.sess.span_err(
item_sp,
"`#[deprecated]` cannot be used in staged API; \
use `#[rustc_deprecated]` instead",
if kind == AnnotationKind::Prohibited {
self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless");
}
// `Deprecation` is just two pointers, no need to intern it
let depr_entry = DeprecationEntry::local(depr.clone(), hir_id);
self.index.depr_map.insert(hir_id, depr_entry);
} else if let Some(parent_depr) = self.parent_depr.clone() {
is_deprecated = true;
info!("tagging child {:?} as deprecated from parent", hir_id);
self.index.depr_map.insert(hir_id, parent_depr);
}
if self.tcx.features().staged_api {
if let Some(..) = attrs.iter().find(|a| a.check_name(sym::deprecated)) {
self.tcx.sess.span_err(
item_sp,
"`#[deprecated]` cannot be used in staged API; \
use `#[rustc_deprecated]` instead",
);
}
} else {
self.recurse_with_stability_attrs(
depr.map(|d| DeprecationEntry::local(d, hir_id)),
None,
None,
visit_children,
);
return;
}
let (stab, const_stab) = attr::find_stability(&self.tcx.sess.parse_sess, attrs, item_sp);
@ -92,33 +122,34 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
}
}
let stab = stab.map(|mut stab| {
if depr.as_ref().map_or(false, |d| d.is_since_rustc_version) {
if stab.is_none() {
struct_span_err!(
self.tcx.sess,
item_sp,
E0549,
"rustc_deprecated attribute must be paired with \
either stable or unstable attribute"
)
.emit();
}
}
let stab = stab.map(|stab| {
// Error if prohibited, or can't inherit anything from a container.
if kind == AnnotationKind::Prohibited
|| (kind == AnnotationKind::Container
&& stab.level.is_stable()
&& stab.rustc_depr.is_none())
|| (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated)
{
self.tcx.sess.span_err(item_sp, "This stability annotation is useless");
}
debug!("annotate: found {:?}", stab);
// If parent is deprecated and we're not, inherit this by merging
// deprecated_since and its reason.
if let Some(parent_stab) = self.parent_stab {
if parent_stab.rustc_depr.is_some() && stab.rustc_depr.is_none() {
stab.rustc_depr = parent_stab.rustc_depr
}
}
let stab = self.tcx.intern_stability(stab);
// Check if deprecated_since < stable_since. If it is,
// this is *almost surely* an accident.
if let (
&Some(attr::RustcDeprecation { since: dep_since, .. }),
&attr::Stable { since: stab_since },
) = (&stab.rustc_depr, &stab.level)
if let (&Some(dep_since), &attr::Stable { since: stab_since }) =
(&depr.as_ref().and_then(|d| d.since), &stab.level)
{
// Explicit version of iter::order::lt to handle parse errors properly
for (dep_v, stab_v) in
@ -163,19 +194,29 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
}
}
self.recurse_with_stability_attrs(stab, const_stab, visit_children);
self.recurse_with_stability_attrs(
depr.map(|d| DeprecationEntry::local(d, hir_id)),
stab,
const_stab,
visit_children,
);
}
fn recurse_with_stability_attrs(
&mut self,
depr: Option<DeprecationEntry>,
stab: Option<&'tcx Stability>,
const_stab: Option<&'tcx ConstStability>,
f: impl FnOnce(&mut Self),
) {
// These will be `Some` if this item changes the corresponding stability attribute.
let mut replaced_parent_depr = None;
let mut replaced_parent_stab = None;
let mut replaced_parent_const_stab = None;
if let Some(depr) = depr {
replaced_parent_depr = Some(replace(&mut self.parent_depr, Some(depr)));
}
if let Some(stab) = stab {
replaced_parent_stab = Some(replace(&mut self.parent_stab, Some(stab)));
}
@ -186,6 +227,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
f(self);
if let Some(orig_parent_depr) = replaced_parent_depr {
self.parent_depr = orig_parent_depr;
}
if let Some(orig_parent_stab) = replaced_parent_stab {
self.parent_stab = orig_parent_stab;
}
@ -194,14 +238,8 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
}
}
fn forbid_staged_api_attrs(
&mut self,
hir_id: HirId,
attrs: &[Attribute],
item_sp: Span,
kind: AnnotationKind,
visit_children: impl FnOnce(&mut Self),
) {
// returns true if an error occurred, used to suppress some spurious errors
fn forbid_staged_api_attrs(&mut self, hir_id: HirId, attrs: &[Attribute]) -> bool {
// Emit errors for non-staged-api crates.
let unstable_attrs = [
sym::unstable,
@ -210,6 +248,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
sym::rustc_const_unstable,
sym::rustc_const_stable,
];
let mut has_error = false;
for attr in attrs {
let name = attr.name_or_empty();
if unstable_attrs.contains(&name) {
@ -221,6 +260,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
"stability attributes may not be used outside of the standard library",
)
.emit();
has_error = true;
}
}
@ -232,24 +272,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
}
}
if let Some(depr) = attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp) {
if kind == AnnotationKind::Prohibited {
self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless");
}
// `Deprecation` is just two pointers, no need to intern it
let depr_entry = DeprecationEntry::local(depr, hir_id);
self.index.depr_map.insert(hir_id, depr_entry.clone());
let orig_parent_depr = replace(&mut self.parent_depr, Some(depr_entry));
visit_children(self);
self.parent_depr = orig_parent_depr;
} else if let Some(parent_depr) = self.parent_depr.clone() {
self.index.depr_map.insert(hir_id, parent_depr);
visit_children(self);
} else {
visit_children(self);
}
has_error
}
}
@ -454,7 +477,6 @@ fn new_index(tcx: TyCtxt<'tcx>) -> Index<'tcx> {
is_soft: false,
},
feature: sym::rustc_private,
rustc_depr: None,
});
annotator.parent_stab = Some(stability);
}

View File

@ -1017,22 +1017,17 @@ impl<'a> Resolver<'a> {
);
}
}
if let Some(depr) = &stability.rustc_depr {
let path = pprust::path_to_string(path);
let (message, lint) = stability::rustc_deprecation_message(depr, &path);
stability::early_report_deprecation(
&mut self.lint_buffer,
&message,
depr.suggestion,
lint,
span,
);
}
}
if let Some(depr) = &ext.deprecation {
let path = pprust::path_to_string(&path);
let (message, lint) = stability::deprecation_message(depr, &path);
stability::early_report_deprecation(&mut self.lint_buffer, &message, None, lint, span);
stability::early_report_deprecation(
&mut self.lint_buffer,
&message,
depr.suggestion,
lint,
span,
);
}
}

View File

@ -2353,10 +2353,6 @@ impl Clean<Stability> for attr::Stability {
attr::Stable { ref since } => since.to_string(),
_ => String::new(),
},
deprecation: self.rustc_depr.as_ref().map(|d| Deprecation {
note: Some(d.reason.to_string()).filter(|r| !r.is_empty()),
since: Some(d.since.to_string()).filter(|d| !d.is_empty()),
}),
unstable_reason: match self.level {
attr::Unstable { reason: Some(ref reason), .. } => Some(reason.to_string()),
_ => None,
@ -2374,6 +2370,7 @@ impl Clean<Deprecation> for attr::Deprecation {
Deprecation {
since: self.since.map(|s| s.to_string()).filter(|s| !s.is_empty()),
note: self.note.map(|n| n.to_string()).filter(|n| !n.is_empty()),
is_since_rustc_version: self.is_since_rustc_version,
}
}
}

View File

@ -195,7 +195,8 @@ impl Item {
classes.push("unstable");
}
if s.deprecation.is_some() {
// FIXME: what about non-staged API items that are deprecated?
if self.deprecation.is_some() {
classes.push("deprecated");
}
@ -216,14 +217,6 @@ impl Item {
ItemType::from(self)
}
/// Returns the info in the item's `#[deprecated]` or `#[rustc_deprecated]` attributes.
///
/// If the item is not deprecated, returns `None`.
pub fn deprecation(&self) -> Option<&Deprecation> {
self.deprecation
.as_ref()
.or_else(|| self.stability.as_ref().and_then(|s| s.deprecation.as_ref()))
}
pub fn is_default(&self) -> bool {
match self.inner {
ItemEnum::MethodItem(ref meth) => {
@ -1528,7 +1521,6 @@ pub struct Stability {
pub level: stability::StabilityLevel,
pub feature: Option<String>,
pub since: String,
pub deprecation: Option<Deprecation>,
pub unstable_reason: Option<String>,
pub issue: Option<NonZeroU32>,
}
@ -1537,6 +1529,7 @@ pub struct Stability {
pub struct Deprecation {
pub since: Option<String>,
pub note: Option<String>,
pub is_since_rustc_version: bool,
}
/// An type binding on an associated type (e.g., `A = Bar` in `Foo<A = Bar>` or

View File

@ -2216,16 +2216,10 @@ fn stability_tags(item: &clean::Item) -> String {
}
// The trailing space after each tag is to space it properly against the rest of the docs.
if item.deprecation().is_some() {
if let Some(depr) = &item.deprecation {
let mut message = "Deprecated";
if let Some(ref stab) = item.stability {
if let Some(ref depr) = stab.deprecation {
if let Some(ref since) = depr.since {
if !stability::deprecation_in_effect(&since) {
message = "Deprecation planned";
}
}
}
if !stability::deprecation_in_effect(depr.is_since_rustc_version, depr.since.as_deref()) {
message = "Deprecation planned";
}
tags += &tag_html("deprecated", message);
}
@ -2254,23 +2248,18 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
let mut stability = vec![];
let error_codes = cx.shared.codes;
if let Some(Deprecation { note, since }) = &item.deprecation() {
if let Some(Deprecation { ref note, ref since, is_since_rustc_version }) = item.deprecation {
// We display deprecation messages for #[deprecated] and #[rustc_deprecated]
// but only display the future-deprecation messages for #[rustc_deprecated].
let mut message = if let Some(since) = since {
format!("Deprecated since {}", Escape(since))
if !stability::deprecation_in_effect(is_since_rustc_version, Some(since)) {
format!("Deprecating in {}", Escape(&since))
} else {
format!("Deprecated since {}", Escape(&since))
}
} else {
String::from("Deprecated")
};
if let Some(ref stab) = item.stability {
if let Some(ref depr) = stab.deprecation {
if let Some(ref since) = depr.since {
if !stability::deprecation_in_effect(&since) {
message = format!("Deprecating in {}", Escape(&since));
}
}
}
}
if let Some(note) = note {
let mut ids = cx.id_map.borrow_mut();

View File

@ -62,7 +62,7 @@ fn multiple3() { }
#[rustc_deprecated(since = "b", reason = "text")]
#[rustc_const_unstable(feature = "c", issue = "none")]
#[rustc_const_unstable(feature = "d", issue = "none")] //~ ERROR multiple stability levels
pub const fn multiple4() { } //~ ERROR multiple rustc_deprecated attributes [E0540]
pub const fn multiple4() { } //~ ERROR multiple deprecated attributes
//~^ ERROR Invalid stability or deprecation version found
#[rustc_deprecated(since = "a", reason = "text")]

View File

@ -82,7 +82,7 @@ error[E0544]: multiple stability levels
LL | #[stable(feature = "a", since = "b")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0540]: multiple rustc_deprecated attributes
error[E0550]: multiple deprecated attributes
--> $DIR/stability-attribute-sanity.rs:65:1
|
LL | pub const fn multiple4() { }
@ -108,5 +108,5 @@ LL | fn deprecated_without_unstable_or_stable() { }
error: aborting due to 18 previous errors
Some errors have detailed explanations: E0539, E0541.
Some errors have detailed explanations: E0539, E0541, E0550.
For more information about an error, try `rustc --explain E0539`.