///
/// - `#[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.
}
}
-#[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 {
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;
'outer: for attr in attrs_iter {
if ![
- sym::rustc_deprecated,
sym::rustc_const_unstable,
sym::rustc_const_stable,
sym::unstable,
}
};
- 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);
(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,
(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,
}
}
- // 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 {
#[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.
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;
}
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() {
}
};
- let mut since = None;
- let mut note = None;
for meta in list {
match meta {
NestedMetaItem::MetaItem(mi) => match mi.name_or_empty() {
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;
}
}
}
+ }
+ }
+
+ if suggestion.is_some() && attr.check_name(sym::deprecated) {
+ unreachable!("only allowed on rustc_deprecated")
+ }
- Some(Deprecation { since, note })
+ 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
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
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;
/// 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.
}
}
-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(
.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,
+ );
}
};
}
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 {
) 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);
}
}
- 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
}
}
- 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)));
}
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;
}
}
}
- 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,
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) {
"stability attributes may not be used outside of the standard library",
)
.emit();
+ has_error = true;
}
}
}
}
- 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
}
}
is_soft: false,
},
feature: sym::rustc_private,
- rustc_depr: None,
});
annotator.parent_stab = Some(stability);
}
);
}
}
- 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,
+ );
}
}
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,
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,
}
}
}
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");
}
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) => {
pub level: stability::StabilityLevel,
pub feature: Option<String>,
pub since: String,
- pub deprecation: Option<Deprecation>,
pub unstable_reason: Option<String>,
pub issue: Option<NonZeroU32>,
}
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
}
// 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);
}
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();
#[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")]
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() { }
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`.