From e3ed7b0501a24f4def3aff775c6fbf9481e3c77e Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 4 Dec 2015 19:34:28 +0300 Subject: [PATCH 1/3] Implement `#[deprecated]` attribute (RFC 1270) --- src/doc/reference.md | 2 + src/librustc/middle/cstore.rs | 2 + src/librustc/middle/stability.rs | 169 ++++++-- src/librustc_lint/builtin.rs | 63 ++- src/librustc_lint/lib.rs | 2 +- src/librustc_metadata/common.rs | 2 + src/librustc_metadata/csearch.rs | 6 + src/librustc_metadata/decoder.rs | 8 + src/librustc_metadata/encoder.rs | 60 ++- src/librustdoc/clean/inline.rs | 10 +- src/librustdoc/clean/mod.rs | 10 +- src/librustdoc/visit_ast.rs | 2 +- src/libsyntax/attr.rs | 91 ++++- src/libsyntax/feature_gate.rs | 7 + src/test/auxiliary/deprecation-lint.rs | 87 ++++ .../compile-fail/deprecation-in-staged-api.rs | 18 + src/test/compile-fail/deprecation-lint-2.rs | 23 ++ src/test/compile-fail/deprecation-lint-3.rs | 24 ++ src/test/compile-fail/deprecation-lint.rs | 384 ++++++++++++++++++ src/test/compile-fail/deprecation-sanity.rs | 39 ++ 20 files changed, 899 insertions(+), 110 deletions(-) create mode 100644 src/test/auxiliary/deprecation-lint.rs create mode 100644 src/test/compile-fail/deprecation-in-staged-api.rs create mode 100644 src/test/compile-fail/deprecation-lint-2.rs create mode 100644 src/test/compile-fail/deprecation-lint-3.rs create mode 100644 src/test/compile-fail/deprecation-lint.rs create mode 100644 src/test/compile-fail/deprecation-sanity.rs diff --git a/src/doc/reference.md b/src/doc/reference.md index 0262ff5a71aad..a20d257115211 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2390,6 +2390,8 @@ The currently implemented features of the reference compiler are: * - `stmt_expr_attributes` - Allows attributes on expressions and non-item statements. +* - `deprecated` - Allows using the `#[deprecated]` attribute. + If a feature is promoted to a language feature, then all existing programs will start to receive compilation warnings about `#![feature]` directives which enabled the new feature (because the directive is no longer necessary). However, if a diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 22a4ddd2f687b..2c3b89bf2fbb2 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -135,6 +135,7 @@ pub enum FoundAst<'ast> { pub trait CrateStore<'tcx> : Any { // item info fn stability(&self, def: DefId) -> Option; + fn deprecation(&self, def: DefId) -> Option; fn closure_kind(&self, tcx: &ty::ctxt<'tcx>, def_id: DefId) -> ty::ClosureKind; fn closure_ty(&self, tcx: &ty::ctxt<'tcx>, def_id: DefId) @@ -292,6 +293,7 @@ pub struct DummyCrateStore; impl<'tcx> CrateStore<'tcx> for DummyCrateStore { // item info fn stability(&self, def: DefId) -> Option { unimplemented!() } + fn deprecation(&self, def: DefId) -> Option { unimplemented!() } fn closure_kind(&self, tcx: &ty::ctxt<'tcx>, def_id: DefId) -> ty::ClosureKind { unimplemented!() } fn closure_ty(&self, tcx: &ty::ctxt<'tcx>, def_id: DefId) diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 0d92c3da83c8b..f7e2135d5a43b 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -25,7 +25,7 @@ use syntax::codemap::{Span, DUMMY_SP}; use syntax::ast; use syntax::ast::{NodeId, Attribute}; use syntax::feature_gate::{GateIssue, emit_feature_err}; -use syntax::attr::{self, Stability, AttrMetaMethods}; +use syntax::attr::{self, Stability, Deprecation, AttrMetaMethods}; use util::nodemap::{DefIdMap, FnvHashSet, FnvHashMap}; use rustc_front::hir; @@ -61,7 +61,8 @@ enum AnnotationKind { pub struct Index<'tcx> { /// This is mostly a cache, except the stabilities of local items /// are filled by the annotator. - map: DefIdMap>, + stab_map: DefIdMap>, + depr_map: DefIdMap>, /// Maps for each crate whether it is part of the staged API. staged_api: FnvHashMap @@ -71,7 +72,8 @@ pub struct Index<'tcx> { struct Annotator<'a, 'tcx: 'a> { tcx: &'a ty::ctxt<'tcx>, index: &'a mut Index<'tcx>, - parent: Option<&'tcx Stability>, + parent_stab: Option<&'tcx Stability>, + parent_depr: Option, access_levels: &'a AccessLevels, in_trait_impl: bool, in_enum: bool, @@ -86,22 +88,26 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> { { if self.index.staged_api[&LOCAL_CRATE] && self.tcx.sess.features.borrow().staged_api { debug!("annotate(id = {:?}, attrs = {:?})", id, attrs); + if let Some(..) = attr::find_deprecation(self.tcx.sess.diagnostic(), attrs, item_sp) { + self.tcx.sess.span_err(item_sp, "`#[deprecated]` cannot be used in staged api, \ + use `#[rustc_deprecated]` instead"); + } if let Some(mut stab) = attr::find_stability(self.tcx.sess.diagnostic(), attrs, item_sp) { // Error if prohibited, or can't inherit anything from a container if kind == AnnotationKind::Prohibited || (kind == AnnotationKind::Container && stab.level.is_stable() && - stab.depr.is_none()) { + stab.rustc_depr.is_none()) { 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 { - if parent_stab.depr.is_some() && stab.depr.is_none() { - stab.depr = parent_stab.depr.clone() + 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.clone() } } @@ -109,8 +115,8 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> { // Check if deprecated_since < stable_since. If it is, // this is *almost surely* an accident. - if let (&Some(attr::Deprecation {since: ref dep_since, ..}), - &attr::Stable {since: ref stab_since}) = (&stab.depr, &stab.level) { + if let (&Some(attr::RustcDeprecation {since: ref dep_since, ..}), + &attr::Stable {since: ref stab_since}) = (&stab.rustc_depr, &stab.level) { // Explicit version of iter::order::lt to handle parse errors properly for (dep_v, stab_v) in dep_since.split(".").zip(stab_since.split(".")) { if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::(), stab_v.parse()) { @@ -134,20 +140,20 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> { } let def_id = self.tcx.map.local_def_id(id); - self.index.map.insert(def_id, Some(stab)); + self.index.stab_map.insert(def_id, Some(stab)); - let parent = replace(&mut self.parent, Some(stab)); + let orig_parent_stab = replace(&mut self.parent_stab, Some(stab)); visit_children(self); - self.parent = parent; + self.parent_stab = orig_parent_stab; } else { - debug!("annotate: not found, parent = {:?}", self.parent); + debug!("annotate: not found, parent = {:?}", self.parent_stab); let mut is_error = kind == AnnotationKind::Required && self.access_levels.is_reachable(id) && !self.tcx.sess.opts.test; - if let Some(stab) = self.parent { + if let Some(stab) = self.parent_stab { if stab.level.is_unstable() { let def_id = self.tcx.map.local_def_id(id); - self.index.map.insert(def_id, Some(stab)); + self.index.stab_map.insert(def_id, Some(stab)); is_error = false; } } @@ -165,9 +171,35 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> { attr::mark_used(attr); self.tcx.sess.span_err(attr.span(), "stability attributes may not be used \ outside of the standard library"); + } else if tag == "deprecated" { + if !self.tcx.sess.features.borrow().deprecated { + self.tcx.sess.span_err(attr.span(), + "`#[deprecated]` attribute is unstable"); + fileline_help!(self.tcx.sess, attr.span(), "add #![feature(deprecated)] to \ + the crate features to enable"); + } } } - visit_children(self); + + if let Some(depr) = attr::find_deprecation(self.tcx.sess.diagnostic(), 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 def_id = self.tcx.map.local_def_id(id); + self.index.depr_map.insert(def_id, Some(depr.clone())); + + let orig_parent_depr = replace(&mut self.parent_depr, Some(depr)); + visit_children(self); + self.parent_depr = orig_parent_depr; + } else if let Some(depr) = self.parent_depr.clone() { + let def_id = self.tcx.map.local_def_id(id); + self.index.depr_map.insert(def_id, Some(depr)); + visit_children(self); + } else { + visit_children(self); + } } } } @@ -269,7 +301,8 @@ impl<'tcx> Index<'tcx> { let mut annotator = Annotator { tcx: tcx, index: self, - parent: None, + parent_stab: None, + parent_depr: None, access_levels: access_levels, in_trait_impl: false, in_enum: false, @@ -291,7 +324,8 @@ impl<'tcx> Index<'tcx> { staged_api.insert(LOCAL_CRATE, is_staged_api); Index { staged_api: staged_api, - map: DefIdMap(), + stab_map: DefIdMap(), + depr_map: DefIdMap(), } } } @@ -327,7 +361,11 @@ struct Checker<'a, 'tcx: 'a> { } impl<'a, 'tcx> Checker<'a, 'tcx> { - fn check(&mut self, id: DefId, span: Span, stab: &Option<&Stability>) { + fn check(&mut self, id: DefId, span: Span, + stab: &Option<&Stability>, _depr: &Option) { + if !is_staged_api(self.tcx, id) { + return; + } // Only the cross-crate scenario matters when checking unstable APIs let cross_crate = !id.is_local(); if !cross_crate { @@ -395,31 +433,31 @@ impl<'a, 'v, 'tcx> Visitor<'v> for Checker<'a, 'tcx> { if item.span == DUMMY_SP && item.name.as_str() == "__test" { return } check_item(self.tcx, item, true, - &mut |id, sp, stab| self.check(id, sp, stab)); + &mut |id, sp, stab, depr| self.check(id, sp, stab, depr)); intravisit::walk_item(self, item); } fn visit_expr(&mut self, ex: &hir::Expr) { check_expr(self.tcx, ex, - &mut |id, sp, stab| self.check(id, sp, stab)); + &mut |id, sp, stab, depr| self.check(id, sp, stab, depr)); intravisit::walk_expr(self, ex); } fn visit_path(&mut self, path: &hir::Path, id: ast::NodeId) { check_path(self.tcx, path, id, - &mut |id, sp, stab| self.check(id, sp, stab)); + &mut |id, sp, stab, depr| self.check(id, sp, stab, depr)); intravisit::walk_path(self, path) } fn visit_path_list_item(&mut self, prefix: &hir::Path, item: &hir::PathListItem) { check_path_list_item(self.tcx, item, - &mut |id, sp, stab| self.check(id, sp, stab)); + &mut |id, sp, stab, depr| self.check(id, sp, stab, depr)); intravisit::walk_path_list_item(self, prefix, item) } fn visit_pat(&mut self, pat: &hir::Pat) { check_pat(self.tcx, pat, - &mut |id, sp, stab| self.check(id, sp, stab)); + &mut |id, sp, stab, depr| self.check(id, sp, stab, depr)); intravisit::walk_pat(self, pat) } @@ -441,7 +479,7 @@ impl<'a, 'v, 'tcx> Visitor<'v> for Checker<'a, 'tcx> { /// Helper for discovering nodes to check for stability pub fn check_item(tcx: &ty::ctxt, item: &hir::Item, warn_about_defns: bool, - cb: &mut FnMut(DefId, Span, &Option<&Stability>)) { + cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option)) { match item.node { hir::ItemExternCrate(_) => { // compiler-generated `extern crate` items have a dummy span. @@ -478,7 +516,7 @@ pub fn check_item(tcx: &ty::ctxt, item: &hir::Item, warn_about_defns: bool, /// Helper for discovering nodes to check for stability pub fn check_expr(tcx: &ty::ctxt, e: &hir::Expr, - cb: &mut FnMut(DefId, Span, &Option<&Stability>)) { + cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option)) { let span; let id = match e.node { hir::ExprMethodCall(i, _, _) => { @@ -539,7 +577,7 @@ pub fn check_expr(tcx: &ty::ctxt, e: &hir::Expr, } pub fn check_path(tcx: &ty::ctxt, path: &hir::Path, id: ast::NodeId, - cb: &mut FnMut(DefId, Span, &Option<&Stability>)) { + cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option)) { match tcx.def_map.borrow().get(&id).map(|d| d.full_def()) { Some(def::DefPrimTy(..)) => {} Some(def::DefSelfTy(..)) => {} @@ -551,7 +589,7 @@ pub fn check_path(tcx: &ty::ctxt, path: &hir::Path, id: ast::NodeId, } pub fn check_path_list_item(tcx: &ty::ctxt, item: &hir::PathListItem, - cb: &mut FnMut(DefId, Span, &Option<&Stability>)) { + cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option)) { match tcx.def_map.borrow().get(&item.node.id()).map(|d| d.full_def()) { Some(def::DefPrimTy(..)) => {} Some(def) => { @@ -562,7 +600,7 @@ pub fn check_path_list_item(tcx: &ty::ctxt, item: &hir::PathListItem, } pub fn check_pat(tcx: &ty::ctxt, pat: &hir::Pat, - cb: &mut FnMut(DefId, Span, &Option<&Stability>)) { + cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option)) { debug!("check_pat(pat = {:?})", pat); if is_internal(tcx, pat.span) { return; } @@ -591,21 +629,21 @@ pub fn check_pat(tcx: &ty::ctxt, pat: &hir::Pat, } fn maybe_do_stability_check(tcx: &ty::ctxt, id: DefId, span: Span, - cb: &mut FnMut(DefId, Span, &Option<&Stability>)) { - if !is_staged_api(tcx, id) { - debug!("maybe_do_stability_check: \ - skipping id={:?} since it is not staged_api", id); - return; - } + cb: &mut FnMut(DefId, Span, + &Option<&Stability>, &Option)) { if is_internal(tcx, span) { debug!("maybe_do_stability_check: \ skipping span={:?} since it is internal", span); return; } - let ref stability = lookup(tcx, id); + let (stability, deprecation) = if is_staged_api(tcx, id) { + (lookup_stability(tcx, id), None) + } else { + (None, lookup_deprecation(tcx, id)) + }; debug!("maybe_do_stability_check: \ inspecting id={:?} span={:?} of stability={:?}", id, span, stability); - cb(id, span, stability); + cb(id, span, &stability, &deprecation); } fn is_internal(tcx: &ty::ctxt, span: Span) -> bool { @@ -627,24 +665,34 @@ fn is_staged_api(tcx: &ty::ctxt, id: DefId) -> bool { /// Lookup the stability for a node, loading external crate /// metadata as necessary. -pub fn lookup<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> { - if let Some(st) = tcx.stability.borrow().map.get(&id) { +pub fn lookup_stability<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> { + if let Some(st) = tcx.stability.borrow().stab_map.get(&id) { return *st; } - let st = lookup_uncached(tcx, id); - tcx.stability.borrow_mut().map.insert(id, st); + let st = lookup_stability_uncached(tcx, id); + tcx.stability.borrow_mut().stab_map.insert(id, st); st } -fn lookup_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> { +pub fn lookup_deprecation<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option { + if let Some(depr) = tcx.stability.borrow().depr_map.get(&id) { + return depr.clone(); + } + + let depr = lookup_deprecation_uncached(tcx, id); + tcx.stability.borrow_mut().depr_map.insert(id, depr.clone()); + depr +} + +fn lookup_stability_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> { debug!("lookup(id={:?})", id); // is this definition the implementation of a trait method? match tcx.trait_item_of_item(id) { Some(ty::MethodTraitItemId(trait_method_id)) if trait_method_id != id => { debug!("lookup: trait_method_id={:?}", trait_method_id); - return lookup(tcx, trait_method_id) + return lookup_stability(tcx, trait_method_id) } _ => {} } @@ -663,7 +711,40 @@ fn lookup_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stabil // unmarked impls for it. See FIXME above for more details. debug!("lookup: trait_id={:?}", trait_id); - return lookup(tcx, trait_id); + return lookup_stability(tcx, trait_id); + } + } + None + }) +} + +fn lookup_deprecation_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option { + debug!("lookup(id={:?})", id); + + // is this definition the implementation of a trait method? + match tcx.trait_item_of_item(id) { + Some(ty::MethodTraitItemId(trait_method_id)) if trait_method_id != id => { + debug!("lookup: trait_method_id={:?}", trait_method_id); + return lookup_deprecation(tcx, trait_method_id) + } + _ => {} + } + + let item_depr = if id.is_local() { + None // The stability cache is filled partially lazily + } else { + tcx.sess.cstore.deprecation(id) + }; + + item_depr.or_else(|| { + if tcx.is_impl(id) { + if let Some(trait_id) = tcx.trait_id_of_impl(id) { + // FIXME (#18969): for the time being, simply use the + // stability of the trait to determine the stability of any + // unmarked impls for it. See FIXME above for more details. + + debug!("lookup: trait_id={:?}", trait_id); + return lookup_deprecation(tcx, trait_id); } } None diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 90d9bcfffee72..249504cbd8dce 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -575,74 +575,71 @@ impl LateLintPass for MissingDebugImplementations { declare_lint! { DEPRECATED, Warn, - "detects use of #[rustc_deprecated] items" + "detects use of `#[deprecated]` or `#[rustc_deprecated]` items" } -/// Checks for use of items with `#[rustc_deprecated]` attributes +/// Checks for use of items with `#[deprecated]` or `#[rustc_deprecated]` attributes #[derive(Copy, Clone)] -pub struct Stability; +pub struct Deprecated; -impl Stability { - fn lint(&self, cx: &LateContext, _id: DefId, - span: Span, stability: &Option<&attr::Stability>) { +impl Deprecated { + fn lint(&self, cx: &LateContext, _id: DefId, span: Span, + stability: &Option<&attr::Stability>, deprecation: &Option) { // Deprecated attributes apply in-crate and cross-crate. - let (lint, label) = match *stability { - Some(&attr::Stability { depr: Some(_), .. }) => - (DEPRECATED, "deprecated"), - _ => return - }; - - output(cx, span, stability, lint, label); + if let Some(&attr::Stability{rustc_depr: Some(attr::RustcDeprecation{ref reason, ..}), ..}) + = *stability { + output(cx, DEPRECATED, span, Some(&reason)) + } else if let Some(attr::Deprecation{ref note, ..}) = *deprecation { + output(cx, DEPRECATED, span, note.as_ref().map(|x| &**x)) + } - fn output(cx: &LateContext, span: Span, stability: &Option<&attr::Stability>, - lint: &'static Lint, label: &'static str) { - let msg = match *stability { - Some(&attr::Stability {depr: Some(attr::Deprecation {ref reason, ..}), ..}) => { - format!("use of {} item: {}", label, reason) - } - _ => format!("use of {} item", label) + fn output(cx: &LateContext, lint: &'static Lint, span: Span, note: Option<&str>) { + let msg = if let Some(note) = note { + format!("use of deprecated item: {}", note) + } else { + format!("use of deprecated item") }; - cx.span_lint(lint, span, &msg[..]); + cx.span_lint(lint, span, &msg); } } } -impl LintPass for Stability { +impl LintPass for Deprecated { fn get_lints(&self) -> LintArray { lint_array!(DEPRECATED) } } -impl LateLintPass for Stability { +impl LateLintPass for Deprecated { fn check_item(&mut self, cx: &LateContext, item: &hir::Item) { stability::check_item(cx.tcx, item, false, - &mut |id, sp, stab| - self.lint(cx, id, sp, &stab)); + &mut |id, sp, stab, depr| + self.lint(cx, id, sp, &stab, &depr)); } fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { stability::check_expr(cx.tcx, e, - &mut |id, sp, stab| - self.lint(cx, id, sp, &stab)); + &mut |id, sp, stab, depr| + self.lint(cx, id, sp, &stab, &depr)); } fn check_path(&mut self, cx: &LateContext, path: &hir::Path, id: ast::NodeId) { stability::check_path(cx.tcx, path, id, - &mut |id, sp, stab| - self.lint(cx, id, sp, &stab)); + &mut |id, sp, stab, depr| + self.lint(cx, id, sp, &stab, &depr)); } fn check_path_list_item(&mut self, cx: &LateContext, item: &hir::PathListItem) { stability::check_path_list_item(cx.tcx, item, - &mut |id, sp, stab| - self.lint(cx, id, sp, &stab)); + &mut |id, sp, stab, depr| + self.lint(cx, id, sp, &stab, &depr)); } fn check_pat(&mut self, cx: &LateContext, pat: &hir::Pat) { stability::check_pat(cx.tcx, pat, - &mut |id, sp, stab| - self.lint(cx, id, sp, &stab)); + &mut |id, sp, stab, depr| + self.lint(cx, id, sp, &stab, &depr)); } } diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 1d7431404f545..69fd569c8d484 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -124,7 +124,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { UnusedAllocation, MissingCopyImplementations, UnstableFeatures, - Stability, + Deprecated, UnconditionalRecursion, InvalidNoMangleItems, PluginAsLibrary, diff --git a/src/librustc_metadata/common.rs b/src/librustc_metadata/common.rs index 5186c96913341..479ab75927847 100644 --- a/src/librustc_metadata/common.rs +++ b/src/librustc_metadata/common.rs @@ -237,6 +237,8 @@ pub const tag_impl_coerce_unsized_kind: usize = 0xa5; pub const tag_items_data_item_constness: usize = 0xa6; +pub const tag_items_data_item_deprecation: usize = 0xa7; + pub const tag_rustc_version: usize = 0x10f; pub fn rustc_version() -> String { format!( diff --git a/src/librustc_metadata/csearch.rs b/src/librustc_metadata/csearch.rs index ad00ef29e5f60..ecbc840233091 100644 --- a/src/librustc_metadata/csearch.rs +++ b/src/librustc_metadata/csearch.rs @@ -42,6 +42,12 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { decoder::get_stability(&*cdata, def.index) } + fn deprecation(&self, def: DefId) -> Option + { + let cdata = self.get_crate_data(def.krate); + decoder::get_deprecation(&*cdata, def.index) + } + fn closure_kind(&self, _tcx: &ty::ctxt<'tcx>, def_id: DefId) -> ty::ClosureKind { assert!(!def_id.is_local()); diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index d1917b29b9f12..357158c24bacd 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -526,6 +526,14 @@ pub fn get_stability(cdata: Cmd, id: DefIndex) -> Option { }) } +pub fn get_deprecation(cdata: Cmd, id: DefIndex) -> Option { + let item = cdata.lookup_item(id); + reader::maybe_get_doc(item, tag_items_data_item_deprecation).map(|doc| { + let mut decoder = reader::Decoder::new(doc); + Decodable::decode(&mut decoder).unwrap() + }) +} + pub fn get_repr_attrs(cdata: Cmd, id: DefIndex) -> Vec { let item = cdata.lookup_item(id); match reader::maybe_get_doc(item, tag_items_data_item_repr).map(|doc| { diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index a627eeb688075..888776eaa5610 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -342,8 +342,10 @@ fn encode_enum_variant_info<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_attributes(rbml_w, &attrs); encode_repr_attrs(rbml_w, ecx, &attrs); - let stab = stability::lookup(ecx.tcx, vid); + let stab = stability::lookup_stability(ecx.tcx, vid); + let depr = stability::lookup_deprecation(ecx.tcx, vid); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); encode_struct_fields(rbml_w, variant); @@ -450,8 +452,10 @@ fn encode_info_for_mod(ecx: &EncodeContext, encode_path(rbml_w, path.clone()); encode_visibility(rbml_w, vis); - let stab = stability::lookup(ecx.tcx, ecx.tcx.map.local_def_id(id)); + let stab = stability::lookup_stability(ecx.tcx, ecx.tcx.map.local_def_id(id)); + let depr = stability::lookup_deprecation(ecx.tcx, ecx.tcx.map.local_def_id(id)); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); // Encode the reexports of this module, if this module is public. if vis == hir::Public { @@ -538,8 +542,10 @@ fn encode_field<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_bounds_and_type_for_item(rbml_w, ecx, index, id); encode_def_id_and_key(ecx, rbml_w, field.did); - let stab = stability::lookup(ecx.tcx, field.did); + let stab = stability::lookup_stability(ecx.tcx, field.did); + let depr = stability::lookup_deprecation(ecx.tcx, field.did); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); rbml_w.end_tag(); } @@ -565,8 +571,10 @@ fn encode_info_for_struct_ctor<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_symbol(ecx, rbml_w, ctor_id); } - let stab = stability::lookup(ecx.tcx, ecx.tcx.map.local_def_id(ctor_id)); + let stab = stability::lookup_stability(ecx.tcx, ecx.tcx.map.local_def_id(ctor_id)); + let depr= stability::lookup_deprecation(ecx.tcx, ecx.tcx.map.local_def_id(ctor_id)); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); // indicate that this is a tuple struct ctor, because downstream users will normally want // the tuple struct definition, but without this there is no way for them to tell that @@ -700,8 +708,10 @@ fn encode_info_for_associated_const<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_bounds_and_type_for_item(rbml_w, ecx, index, ecx.local_id(associated_const.def_id)); - let stab = stability::lookup(ecx.tcx, associated_const.def_id); + let stab = stability::lookup_stability(ecx.tcx, associated_const.def_id); + let depr = stability::lookup_deprecation(ecx.tcx, associated_const.def_id); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); let elem = ast_map::PathName(associated_const.name); encode_path(rbml_w, impl_path.chain(Some(elem))); @@ -735,8 +745,10 @@ fn encode_info_for_method<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_parent_item(rbml_w, ecx.tcx.map.local_def_id(parent_id)); encode_item_sort(rbml_w, 'r'); - let stab = stability::lookup(ecx.tcx, m.def_id); + let stab = stability::lookup_stability(ecx.tcx, m.def_id); + let depr = stability::lookup_deprecation(ecx.tcx, m.def_id); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); let m_node_id = ecx.local_id(m.def_id); encode_bounds_and_type_for_item(rbml_w, ecx, index, m_node_id); @@ -789,8 +801,10 @@ fn encode_info_for_associated_type<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_parent_item(rbml_w, ecx.tcx.map.local_def_id(parent_id)); encode_item_sort(rbml_w, 't'); - let stab = stability::lookup(ecx.tcx, associated_type.def_id); + let stab = stability::lookup_stability(ecx.tcx, associated_type.def_id); + let depr = stability::lookup_deprecation(ecx.tcx, associated_type.def_id); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); let elem = ast_map::PathName(associated_type.name); encode_path(rbml_w, impl_path.chain(Some(elem))); @@ -891,6 +905,14 @@ fn encode_stability(rbml_w: &mut Encoder, stab_opt: Option<&attr::Stability>) { }); } +fn encode_deprecation(rbml_w: &mut Encoder, depr_opt: Option) { + depr_opt.map(|depr| { + rbml_w.start_tag(tag_items_data_item_deprecation); + depr.encode(rbml_w).unwrap(); + rbml_w.end_tag(); + }); +} + fn encode_xrefs<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, rbml_w: &mut Encoder, xrefs: FnvHashMap, u32>) @@ -931,7 +953,8 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, tcx.sess.codemap().span_to_string(item.span)); let def_id = ecx.tcx.map.local_def_id(item.id); - let stab = stability::lookup(tcx, ecx.tcx.map.local_def_id(item.id)); + let stab = stability::lookup_stability(tcx, ecx.tcx.map.local_def_id(item.id)); + let depr = stability::lookup_deprecation(tcx, ecx.tcx.map.local_def_id(item.id)); match item.node { hir::ItemStatic(_, m, _) => { @@ -949,6 +972,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_path(rbml_w, path); encode_visibility(rbml_w, vis); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); encode_attributes(rbml_w, &item.attrs); rbml_w.end_tag(); } @@ -964,6 +988,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_inlined_item(ecx, rbml_w, InlinedItemRef::Item(item)); encode_visibility(rbml_w, vis); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); rbml_w.end_tag(); } hir::ItemFn(ref decl, _, constness, _, ref generics, _) => { @@ -986,6 +1011,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_constness(rbml_w, constness); encode_visibility(rbml_w, vis); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); encode_method_argument_names(rbml_w, &**decl); rbml_w.end_tag(); } @@ -1015,6 +1041,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, } encode_visibility(rbml_w, vis); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); rbml_w.end_tag(); } hir::ItemTy(..) => { @@ -1027,6 +1054,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_path(rbml_w, path); encode_visibility(rbml_w, vis); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); rbml_w.end_tag(); } hir::ItemEnum(ref enum_definition, _) => { @@ -1051,6 +1079,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_visibility(rbml_w, vis); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); rbml_w.end_tag(); encode_enum_variant_info(ecx, @@ -1077,6 +1106,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_attributes(rbml_w, &item.attrs); encode_path(rbml_w, path.clone()); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); encode_visibility(rbml_w, vis); encode_repr_attrs(rbml_w, ecx, &item.attrs); @@ -1167,6 +1197,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, } encode_path(rbml_w, path.clone()); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); rbml_w.end_tag(); // Iterate down the trait items, emitting them. We rely on the @@ -1236,6 +1267,7 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_attributes(rbml_w, &item.attrs); encode_visibility(rbml_w, vis); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); for &method_def_id in tcx.trait_item_def_ids(def_id).iter() { rbml_w.start_tag(tag_item_trait_item); match method_def_id { @@ -1274,8 +1306,10 @@ fn encode_info_for_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_parent_item(rbml_w, def_id); - let stab = stability::lookup(tcx, item_def_id.def_id()); + let stab = stability::lookup_stability(tcx, item_def_id.def_id()); + let depr = stability::lookup_deprecation(tcx, item_def_id.def_id()); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); let trait_item_type = tcx.impl_or_trait_item(item_def_id.def_id()); @@ -1407,8 +1441,10 @@ fn encode_info_for_foreign_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, encode_inlined_item(ecx, rbml_w, InlinedItemRef::Foreign(nitem)); } encode_attributes(rbml_w, &*nitem.attrs); - let stab = stability::lookup(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id)); + let stab = stability::lookup_stability(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id)); + let depr = stability::lookup_deprecation(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id)); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); encode_symbol(ecx, rbml_w, nitem.id); encode_method_argument_names(rbml_w, &*fndecl); } @@ -1420,8 +1456,10 @@ fn encode_info_for_foreign_item<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>, } encode_bounds_and_type_for_item(rbml_w, ecx, index, nitem.id); encode_attributes(rbml_w, &*nitem.attrs); - let stab = stability::lookup(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id)); + let stab = stability::lookup_stability(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id)); + let depr = stability::lookup_deprecation(ecx.tcx, ecx.tcx.map.local_def_id(nitem.id)); encode_stability(rbml_w, stab); + encode_deprecation(rbml_w, depr); encode_symbol(ecx, rbml_w, nitem.id); encode_name(rbml_w, nitem.name); } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 40e7146670907..5c88576acb7b4 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -120,7 +120,7 @@ fn try_inline_def(cx: &DocContext, tcx: &ty::ctxt, attrs: load_attrs(cx, tcx, did), inner: inner, visibility: Some(hir::Public), - stability: stability::lookup(tcx, did).clean(cx), + stability: stability::lookup_stability(tcx, did).clean(cx), def_id: did, }); Some(ret) @@ -303,7 +303,7 @@ pub fn build_impl(cx: &DocContext, name: None, attrs: attrs, visibility: Some(hir::Inherited), - stability: stability::lookup(tcx, did).clean(cx), + stability: stability::lookup_stability(tcx, did).clean(cx), def_id: did, }); } @@ -333,7 +333,7 @@ pub fn build_impl(cx: &DocContext, source: clean::Span::empty(), attrs: vec![], visibility: None, - stability: stability::lookup(tcx, did).clean(cx), + stability: stability::lookup_stability(tcx, did).clean(cx), def_id: did }) } @@ -381,7 +381,7 @@ pub fn build_impl(cx: &DocContext, source: clean::Span::empty(), attrs: vec![], visibility: None, - stability: stability::lookup(tcx, did).clean(cx), + stability: stability::lookup_stability(tcx, did).clean(cx), def_id: did }) } @@ -414,7 +414,7 @@ pub fn build_impl(cx: &DocContext, name: None, attrs: attrs, visibility: Some(hir::Inherited), - stability: stability::lookup(tcx, did).clean(cx), + stability: stability::lookup_stability(tcx, did).clean(cx), def_id: did, }); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1336acb6b0355..942cc135aa8f5 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -62,7 +62,7 @@ mod simplify; // extract the stability index for a node from tcx, if possible fn get_stability(cx: &DocContext, def_id: DefId) -> Option { - cx.tcx_opt().and_then(|tcx| stability::lookup(tcx, def_id)).clean(cx) + cx.tcx_opt().and_then(|tcx| stability::lookup_stability(tcx, def_id)).clean(cx) } pub trait Clean { @@ -2689,12 +2689,12 @@ impl Clean for attr::Stability { attr::Stable {ref since} => since.to_string(), _ => "".to_string(), }, - deprecated_since: match self.depr { - Some(attr::Deprecation {ref since, ..}) => since.to_string(), + deprecated_since: match self.rustc_depr { + Some(attr::RustcDeprecation {ref since, ..}) => since.to_string(), _=> "".to_string(), }, reason: { - if let Some(ref depr) = self.depr { + if let Some(ref depr) = self.rustc_depr { depr.reason.to_string() } else if let attr::Unstable {reason: Some(ref reason), ..} = self.level { reason.to_string() @@ -2782,7 +2782,7 @@ impl<'tcx> Clean for ty::AssociatedType<'tcx> { inner: AssociatedTypeItem(bounds, self.ty.clean(cx)), visibility: self.vis.clean(cx), def_id: self.def_id, - stability: stability::lookup(cx.tcx(), self.def_id).clean(cx), + stability: stability::lookup_stability(cx.tcx(), self.def_id).clean(cx), } } } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 17291233e114a..89527c54a3851 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -64,7 +64,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { fn stability(&self, id: ast::NodeId) -> Option { self.cx.tcx_opt().and_then(|tcx| { self.cx.map.opt_local_def_id(id) - .and_then(|def_id| stability::lookup(tcx, def_id)) + .and_then(|def_id| stability::lookup_stability(tcx, def_id)) .cloned() }) } diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index e828d8ae24874..d511ce09a3616 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -398,7 +398,7 @@ pub fn cfg_matches(cfgs: &[P], pub struct Stability { pub level: StabilityLevel, pub feature: InternedString, - pub depr: Option, + pub rustc_depr: Option, } /// The available stability levels. @@ -410,11 +410,17 @@ pub enum StabilityLevel { } #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)] -pub struct Deprecation { +pub struct RustcDeprecation { pub since: InternedString, pub reason: InternedString, } +#[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)] +pub struct Deprecation { + pub since: Option, + pub note: Option, +} + impl StabilityLevel { pub fn is_unstable(&self) -> bool { if let Unstable {..} = *self { true } else { false }} pub fn is_stable(&self) -> bool { if let Stable {..} = *self { true } else { false }} @@ -427,7 +433,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler, where I: Iterator { let mut stab: Option = None; - let mut depr: Option = None; + let mut rustc_depr: Option = None; 'outer: for attr in attrs_iter { let tag = attr.name(); @@ -456,7 +462,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler, match tag { "rustc_deprecated" => { - if depr.is_some() { + if rustc_depr.is_some() { diagnostic.span_err(item_sp, "multiple rustc_deprecated attributes"); break } @@ -477,7 +483,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler, match (since, reason) { (Some(since), Some(reason)) => { - depr = Some(Deprecation { + rustc_depr = Some(RustcDeprecation { since: since, reason: reason, }) @@ -529,7 +535,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler, } }, feature: feature, - depr: None, + rustc_depr: None, }) } (None, _, _) => { @@ -569,7 +575,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler, since: since, }, feature: feature, - depr: None, + rustc_depr: None, }) } (None, _) => { @@ -591,12 +597,12 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler, } // Merge the deprecation info into the stability info - if let Some(depr) = depr { + if let Some(rustc_depr) = rustc_depr { if let Some(ref mut stab) = stab { if let Unstable {reason: ref mut reason @ None, ..} = stab.level { - *reason = Some(depr.reason.clone()) + *reason = Some(rustc_depr.reason.clone()) } - stab.depr = Some(depr); + stab.rustc_depr = Some(rustc_depr); } else { diagnostic.span_err(item_sp, "rustc_deprecated attribute must be paired with \ either stable or unstable attribute"); @@ -606,12 +612,77 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler, stab } +fn find_deprecation_generic<'a, I>(diagnostic: &SpanHandler, + attrs_iter: I, + item_sp: Span) + -> Option + where I: Iterator +{ + let mut depr: Option = None; + + 'outer: for attr in attrs_iter { + if attr.name() != "deprecated" { + continue + } + + mark_used(attr); + + if depr.is_some() { + diagnostic.span_err(item_sp, "multiple deprecated attributes"); + break + } + + depr = if let Some(metas) = attr.meta_item_list() { + let get = |meta: &MetaItem, item: &mut Option| { + if item.is_some() { + diagnostic.span_err(meta.span, &format!("multiple '{}' items", + meta.name())); + return false + } + if let Some(v) = meta.value_str() { + *item = Some(v); + true + } else { + diagnostic.span_err(meta.span, "incorrect meta item"); + false + } + }; + + let mut since = None; + let mut note = None; + for meta in metas { + match &*meta.name() { + "since" => if !get(meta, &mut since) { continue 'outer }, + "note" => if !get(meta, &mut note) { continue 'outer }, + _ => { + diagnostic.span_err(meta.span, &format!("unknown meta item '{}'", + meta.name())); + continue 'outer + } + } + } + + Some(Deprecation {since: since, note: note}) + } else { + Some(Deprecation{since: None, note: None}) + } + } + + depr +} + /// Find the first stability attribute. `None` if none exists. pub fn find_stability(diagnostic: &SpanHandler, attrs: &[Attribute], item_sp: Span) -> Option { find_stability_generic(diagnostic, attrs.iter(), item_sp) } +/// Find the deprecation attribute. `None` if none exists. +pub fn find_deprecation(diagnostic: &SpanHandler, attrs: &[Attribute], + item_sp: Span) -> Option { + find_deprecation_generic(diagnostic, attrs.iter(), item_sp) +} + pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[P]) { let mut set = HashSet::new(); for meta in metas { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index c456b7dc8b91c..6b138b50f038b 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -230,6 +230,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status // Allow attributes on expressions and non-item statements ("stmt_expr_attributes", "1.6.0", Some(15701), Active), + + // Allows `#[deprecated]` attribute + ("deprecated", "1.6.0", Some(29935), Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) @@ -377,6 +380,7 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat ("must_use", Whitelisted, Ungated), ("stable", Whitelisted, Ungated), ("unstable", Whitelisted, Ungated), + ("deprecated", Whitelisted, Ungated), ("rustc_paren_sugar", Normal, Gated("unboxed_closures", "unboxed_closures are still evolving")), @@ -539,6 +543,7 @@ pub struct Features { pub braced_empty_structs: bool, pub staged_api: bool, pub stmt_expr_attributes: bool, + pub deprecated: bool, } impl Features { @@ -573,6 +578,7 @@ impl Features { braced_empty_structs: false, staged_api: false, stmt_expr_attributes: false, + deprecated: false, } } } @@ -1151,6 +1157,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, braced_empty_structs: cx.has_feature("braced_empty_structs"), staged_api: cx.has_feature("staged_api"), stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"), + deprecated: cx.has_feature("deprecated"), } } diff --git a/src/test/auxiliary/deprecation-lint.rs b/src/test/auxiliary/deprecation-lint.rs new file mode 100644 index 0000000000000..61c91590b31fe --- /dev/null +++ b/src/test/auxiliary/deprecation-lint.rs @@ -0,0 +1,87 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(deprecated)] + +#[deprecated(since = "1.0.0", note = "text")] +pub fn deprecated() {} +#[deprecated(since = "1.0.0", note = "text")] +pub fn deprecated_text() {} + +pub struct MethodTester; + +impl MethodTester { + #[deprecated(since = "1.0.0", note = "text")] + pub fn method_deprecated(&self) {} + #[deprecated(since = "1.0.0", note = "text")] + pub fn method_deprecated_text(&self) {} +} + +pub trait Trait { + #[deprecated(since = "1.0.0", note = "text")] + fn trait_deprecated(&self) {} + #[deprecated(since = "1.0.0", note = "text")] + fn trait_deprecated_text(&self) {} +} + +impl Trait for MethodTester {} + +#[deprecated(since = "1.0.0", note = "text")] +pub struct DeprecatedStruct { + pub i: isize +} + +#[deprecated(since = "1.0.0", note = "text")] +pub struct DeprecatedUnitStruct; + +pub enum Enum { + #[deprecated(since = "1.0.0", note = "text")] + DeprecatedVariant, +} + +#[deprecated(since = "1.0.0", note = "text")] +pub struct DeprecatedTupleStruct(pub isize); + +pub struct Stable { + #[deprecated(since = "1.0.0", note = "text")] + pub override2: u8, +} + +pub struct Stable2(pub u8, pub u8, #[deprecated(since = "1.0.0", note = "text")] pub u8); + +#[deprecated(since = "1.0.0", note = "text")] +pub struct Deprecated { + pub inherit: u8, +} + +#[deprecated(since = "1.0.0", note = "text")] +pub struct Deprecated2(pub u8, + pub u8, + pub u8); + +#[deprecated(since = "1.0.0", note = "text")] +pub mod deprecated_mod { + pub fn deprecated() {} +} + +#[macro_export] +macro_rules! macro_test { + () => (deprecated()); +} + +#[macro_export] +macro_rules! macro_test_arg { + ($func:expr) => ($func); +} + +#[macro_export] +macro_rules! macro_test_arg_nested { + ($func:ident) => (macro_test_arg!($func())); +} diff --git a/src/test/compile-fail/deprecation-in-staged-api.rs b/src/test/compile-fail/deprecation-in-staged-api.rs new file mode 100644 index 0000000000000..4f4aed21f994d --- /dev/null +++ b/src/test/compile-fail/deprecation-in-staged-api.rs @@ -0,0 +1,18 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// #[deprecated] can't be used in staged api + +#![feature(deprecated, staged_api)] + +#![stable(feature = "test_feature", since = "1.0.0")] + +#[deprecated] +fn main() { } //~ERROR `#[deprecated]` cannot be used in staged api diff --git a/src/test/compile-fail/deprecation-lint-2.rs b/src/test/compile-fail/deprecation-lint-2.rs new file mode 100644 index 0000000000000..2817e06652afb --- /dev/null +++ b/src/test/compile-fail/deprecation-lint-2.rs @@ -0,0 +1,23 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:deprecation-lint.rs +// error-pattern: use of deprecated item + +#![deny(deprecated)] + +#[macro_use] +extern crate deprecation_lint; + +use deprecation_lint::*; + +fn main() { + macro_test!(); +} diff --git a/src/test/compile-fail/deprecation-lint-3.rs b/src/test/compile-fail/deprecation-lint-3.rs new file mode 100644 index 0000000000000..7faaa181d3922 --- /dev/null +++ b/src/test/compile-fail/deprecation-lint-3.rs @@ -0,0 +1,24 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:deprecation-lint.rs +// error-pattern: use of deprecated item + +#![deny(deprecated)] +#![allow(warnings)] + +#[macro_use] +extern crate deprecation_lint; + +use deprecation_lint::*; + +fn main() { + macro_test_arg_nested!(deprecated_text); +} diff --git a/src/test/compile-fail/deprecation-lint.rs b/src/test/compile-fail/deprecation-lint.rs new file mode 100644 index 0000000000000..db6d5fd63e59b --- /dev/null +++ b/src/test/compile-fail/deprecation-lint.rs @@ -0,0 +1,384 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:deprecation-lint.rs + +#![feature(deprecated)] + +#![deny(deprecated)] +#![allow(warnings)] + +#[macro_use] +extern crate deprecation_lint; + +mod cross_crate { + use deprecation_lint::*; + + fn test() { + type Foo = MethodTester; + let foo = MethodTester; + + deprecated(); //~ ERROR use of deprecated item + foo.method_deprecated(); //~ ERROR use of deprecated item + Foo::method_deprecated(&foo); //~ ERROR use of deprecated item + ::method_deprecated(&foo); //~ ERROR use of deprecated item + foo.trait_deprecated(); //~ ERROR use of deprecated item + Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item + ::trait_deprecated(&foo); //~ ERROR use of deprecated item + ::trait_deprecated(&foo); //~ ERROR use of deprecated item + + deprecated_text(); //~ ERROR use of deprecated item: text + foo.method_deprecated_text(); //~ ERROR use of deprecated item: text + Foo::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text + ::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text + foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text + Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text + + let _ = DeprecatedStruct { //~ ERROR use of deprecated item + i: 0 //~ ERROR use of deprecated item + }; + + let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item + + let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item + + let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item + + // At the moment, the lint checker only checks stability in + // in the arguments of macros. + // Eventually, we will want to lint the contents of the + // macro in the module *defining* it. Also, stability levels + // on macros themselves are not yet linted. + macro_test_arg!(deprecated_text()); //~ ERROR use of deprecated item: text + macro_test_arg!(macro_test_arg!(deprecated_text())); //~ ERROR use of deprecated item: text + } + + fn test_method_param(foo: Foo) { + foo.trait_deprecated(); //~ ERROR use of deprecated item + Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item + ::trait_deprecated(&foo); //~ ERROR use of deprecated item + ::trait_deprecated(&foo); //~ ERROR use of deprecated item + foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text + Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text + } + + fn test_method_object(foo: &Trait) { + foo.trait_deprecated(); //~ ERROR use of deprecated item + foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text + } + + pub fn foo() { + let x = Stable { + override2: 3, + //~^ ERROR use of deprecated item + }; + + let _ = x.override2; + //~^ ERROR use of deprecated item + + let Stable { + override2: _ + //~^ ERROR use of deprecated item + } = x; + // all fine + let Stable { .. } = x; + + let x = Stable2(1, 2, 3); + + let _ = x.2; + //~^ ERROR use of deprecated item + + let Stable2(_, + _, + _) + //~^ ERROR use of deprecated item + = x; + // all fine + let Stable2(..) = x; + + let x = Deprecated { + //~^ ERROR use of deprecated item + inherit: 1, + //~^ ERROR use of deprecated item + }; + + let _ = x.inherit; + //~^ ERROR use of deprecated item + + let Deprecated { + //~^ ERROR use of deprecated item + inherit: _, + //~^ ERROR use of deprecated item + } = x; + + let Deprecated + //~^ ERROR use of deprecated item + { .. } = x; + + let x = Deprecated2(1, 2, 3); + //~^ ERROR use of deprecated item + + let _ = x.0; + //~^ ERROR use of deprecated item + let _ = x.1; + //~^ ERROR use of deprecated item + let _ = x.2; + //~^ ERROR use of deprecated item + + let Deprecated2 + //~^ ERROR use of deprecated item + (_, + //~^ ERROR use of deprecated item + _, + //~^ ERROR use of deprecated item + _) + //~^ ERROR use of deprecated item + = x; + let Deprecated2 + //~^ ERROR use of deprecated item + // the patterns are all fine: + (..) = x; + } +} + +mod inheritance { + use deprecation_lint::*; + + fn test_inheritance() { + deprecated_mod::deprecated(); //~ ERROR use of deprecated item + } +} + +mod this_crate { + #[deprecated(since = "1.0.0", note = "text")] + pub fn deprecated() {} + #[deprecated(since = "1.0.0", note = "text")] + pub fn deprecated_text() {} + + pub struct MethodTester; + + impl MethodTester { + #[deprecated(since = "1.0.0", note = "text")] + pub fn method_deprecated(&self) {} + #[deprecated(since = "1.0.0", note = "text")] + pub fn method_deprecated_text(&self) {} + } + + pub trait Trait { + #[deprecated(since = "1.0.0", note = "text")] + fn trait_deprecated(&self) {} + #[deprecated(since = "1.0.0", note = "text")] + fn trait_deprecated_text(&self) {} + } + + impl Trait for MethodTester {} + + #[deprecated(since = "1.0.0", note = "text")] + pub struct DeprecatedStruct { + i: isize + } + pub struct UnstableStruct { + i: isize + } + pub struct StableStruct { + i: isize + } + + #[deprecated(since = "1.0.0", note = "text")] + pub struct DeprecatedUnitStruct; + + pub enum Enum { + #[deprecated(since = "1.0.0", note = "text")] + DeprecatedVariant, + } + + #[deprecated(since = "1.0.0", note = "text")] + pub struct DeprecatedTupleStruct(isize); + + fn test() { + // Only the deprecated cases of the following should generate + // errors, because other stability attributes now have meaning + // only *across* crates, not within a single crate. + + type Foo = MethodTester; + let foo = MethodTester; + + deprecated(); //~ ERROR use of deprecated item + foo.method_deprecated(); //~ ERROR use of deprecated item + Foo::method_deprecated(&foo); //~ ERROR use of deprecated item + ::method_deprecated(&foo); //~ ERROR use of deprecated item + foo.trait_deprecated(); //~ ERROR use of deprecated item + Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item + ::trait_deprecated(&foo); //~ ERROR use of deprecated item + ::trait_deprecated(&foo); //~ ERROR use of deprecated item + + deprecated_text(); //~ ERROR use of deprecated item: text + foo.method_deprecated_text(); //~ ERROR use of deprecated item: text + Foo::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text + ::method_deprecated_text(&foo); //~ ERROR use of deprecated item: text + foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text + Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text + + let _ = DeprecatedStruct { + //~^ ERROR use of deprecated item + i: 0 //~ ERROR use of deprecated item + }; + + let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item + + let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item + + let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item + } + + fn test_method_param(foo: Foo) { + foo.trait_deprecated(); //~ ERROR use of deprecated item + Trait::trait_deprecated(&foo); //~ ERROR use of deprecated item + ::trait_deprecated(&foo); //~ ERROR use of deprecated item + ::trait_deprecated(&foo); //~ ERROR use of deprecated item + foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text + Trait::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text + ::trait_deprecated_text(&foo); //~ ERROR use of deprecated item: text + } + + fn test_method_object(foo: &Trait) { + foo.trait_deprecated(); //~ ERROR use of deprecated item + foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text + } + + #[deprecated(since = "1.0.0", note = "text")] + fn test_fn_body() { + fn fn_in_body() {} + fn_in_body(); //~ ERROR use of deprecated item: text + } + + impl MethodTester { + #[deprecated(since = "1.0.0", note = "text")] + fn test_method_body(&self) { + fn fn_in_body() {} + fn_in_body(); //~ ERROR use of deprecated item: text + } + } + + #[deprecated(since = "1.0.0", note = "text")] + pub trait DeprecatedTrait { + fn dummy(&self) { } + } + + struct S; + + impl DeprecatedTrait for S { } //~ ERROR use of deprecated item + + trait LocalTrait : DeprecatedTrait { } //~ ERROR use of deprecated item +} + +mod this_crate2 { + struct Stable { + #[deprecated(since = "1.0.0", note = "text")] + override2: u8, + } + + struct Stable2(u8, + u8, + #[deprecated(since = "1.0.0", note = "text")] u8); + + #[deprecated(since = "1.0.0", note = "text")] + struct Deprecated { + inherit: u8, + } + + #[deprecated(since = "1.0.0", note = "text")] + struct Deprecated2(u8, + u8, + u8); + + pub fn foo() { + let x = Stable { + override2: 3, + //~^ ERROR use of deprecated item + }; + + let _ = x.override2; + //~^ ERROR use of deprecated item + + let Stable { + override2: _ + //~^ ERROR use of deprecated item + } = x; + // all fine + let Stable { .. } = x; + + let x = Stable2(1, 2, 3); + + let _ = x.2; + //~^ ERROR use of deprecated item + + let Stable2(_, + _, + _) + //~^ ERROR use of deprecated item + = x; + // all fine + let Stable2(..) = x; + + let x = Deprecated { + //~^ ERROR use of deprecated item + inherit: 1, + //~^ ERROR use of deprecated item + }; + + let _ = x.inherit; + //~^ ERROR use of deprecated item + + let Deprecated { + //~^ ERROR use of deprecated item + inherit: _, + //~^ ERROR use of deprecated item + } = x; + + let Deprecated + //~^ ERROR use of deprecated item + // the patterns are all fine: + { .. } = x; + + let x = Deprecated2(1, 2, 3); + //~^ ERROR use of deprecated item + + let _ = x.0; + //~^ ERROR use of deprecated item + let _ = x.1; + //~^ ERROR use of deprecated item + let _ = x.2; + //~^ ERROR use of deprecated item + + let Deprecated2 + //~^ ERROR use of deprecated item + (_, + //~^ ERROR use of deprecated item + _, + //~^ ERROR use of deprecated item + _) + //~^ ERROR use of deprecated item + = x; + let Deprecated2 + //~^ ERROR use of deprecated item + // the patterns are all fine: + (..) = x; + } +} + +fn main() {} diff --git a/src/test/compile-fail/deprecation-sanity.rs b/src/test/compile-fail/deprecation-sanity.rs new file mode 100644 index 0000000000000..6ee5cd2c7e3cf --- /dev/null +++ b/src/test/compile-fail/deprecation-sanity.rs @@ -0,0 +1,39 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Various checks that deprecation attributes are used correctly + +#![feature(deprecated)] + +mod bogus_attribute_types_1 { + #[deprecated(since = "a", note = "a", reason)] //~ ERROR unknown meta item 'reason' + fn f1() { } + + #[deprecated(since = "a", note)] //~ ERROR incorrect meta item + fn f2() { } + + #[deprecated(since, note = "a")] //~ ERROR incorrect meta item + fn f3() { } + + #[deprecated(since = "a", note(b))] //~ ERROR incorrect meta item + fn f5() { } + + #[deprecated(since(b), note = "a")] //~ ERROR incorrect meta item + fn f6() { } +} + +#[deprecated(since = "a", note = "b")] +#[deprecated(since = "a", note = "b")] +fn multiple1() { } //~ ERROR multiple deprecated attributes + +#[deprecated(since = "a", since = "b", note = "c")] //~ ERROR multiple 'since' items +fn f1() { } + +fn main() { } From 105bd152076ecc094fc8358f160d01f9fd866f55 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 12 Dec 2015 21:40:45 +0300 Subject: [PATCH 2/3] Address the review comments --- src/librustc/middle/stability.rs | 63 ++--------------------- src/librustc_lint/builtin.rs | 2 +- src/libsyntax/feature_gate.rs | 2 +- src/test/auxiliary/deprecation-lint.rs | 3 ++ src/test/auxiliary/lint_stability.rs | 6 +++ src/test/compile-fail/deprecation-lint.rs | 5 ++ src/test/compile-fail/lint-stability.rs | 3 +- 7 files changed, 22 insertions(+), 62 deletions(-) diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index f7e2135d5a43b..31158901775f3 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -171,13 +171,6 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> { attr::mark_used(attr); self.tcx.sess.span_err(attr.span(), "stability attributes may not be used \ outside of the standard library"); - } else if tag == "deprecated" { - if !self.tcx.sess.features.borrow().deprecated { - self.tcx.sess.span_err(attr.span(), - "`#[deprecated]` attribute is unstable"); - fileline_help!(self.tcx.sess, attr.span(), "add #![feature(deprecated)] to \ - the crate features to enable"); - } } } @@ -687,68 +680,20 @@ pub fn lookup_deprecation<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> { debug!("lookup(id={:?})", id); - - // is this definition the implementation of a trait method? - match tcx.trait_item_of_item(id) { - Some(ty::MethodTraitItemId(trait_method_id)) if trait_method_id != id => { - debug!("lookup: trait_method_id={:?}", trait_method_id); - return lookup_stability(tcx, trait_method_id) - } - _ => {} - } - - let item_stab = if id.is_local() { + if id.is_local() { None // The stability cache is filled partially lazily } else { tcx.sess.cstore.stability(id).map(|st| tcx.intern_stability(st)) - }; - - item_stab.or_else(|| { - if tcx.is_impl(id) { - if let Some(trait_id) = tcx.trait_id_of_impl(id) { - // FIXME (#18969): for the time being, simply use the - // stability of the trait to determine the stability of any - // unmarked impls for it. See FIXME above for more details. - - debug!("lookup: trait_id={:?}", trait_id); - return lookup_stability(tcx, trait_id); - } - } - None - }) + } } fn lookup_deprecation_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option { debug!("lookup(id={:?})", id); - - // is this definition the implementation of a trait method? - match tcx.trait_item_of_item(id) { - Some(ty::MethodTraitItemId(trait_method_id)) if trait_method_id != id => { - debug!("lookup: trait_method_id={:?}", trait_method_id); - return lookup_deprecation(tcx, trait_method_id) - } - _ => {} - } - - let item_depr = if id.is_local() { + if id.is_local() { None // The stability cache is filled partially lazily } else { tcx.sess.cstore.deprecation(id) - }; - - item_depr.or_else(|| { - if tcx.is_impl(id) { - if let Some(trait_id) = tcx.trait_id_of_impl(id) { - // FIXME (#18969): for the time being, simply use the - // stability of the trait to determine the stability of any - // unmarked impls for it. See FIXME above for more details. - - debug!("lookup: trait_id={:?}", trait_id); - return lookup_deprecation(tcx, trait_id); - } - } - None - }) + } } /// Given the list of enabled features that were not language features (i.e. that diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 249504cbd8dce..e403e6d067af1 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -575,7 +575,7 @@ impl LateLintPass for MissingDebugImplementations { declare_lint! { DEPRECATED, Warn, - "detects use of `#[deprecated]` or `#[rustc_deprecated]` items" + "detects use of deprecated items" } /// Checks for use of items with `#[deprecated]` or `#[rustc_deprecated]` attributes diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 6b138b50f038b..f186aff6d363a 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -380,7 +380,7 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat ("must_use", Whitelisted, Ungated), ("stable", Whitelisted, Ungated), ("unstable", Whitelisted, Ungated), - ("deprecated", Whitelisted, Ungated), + ("deprecated", Normal, Gated("deprecated", "`#[deprecated]` attribute is unstable")), ("rustc_paren_sugar", Normal, Gated("unboxed_closures", "unboxed_closures are still evolving")), diff --git a/src/test/auxiliary/deprecation-lint.rs b/src/test/auxiliary/deprecation-lint.rs index 61c91590b31fe..ff872efb7bdb1 100644 --- a/src/test/auxiliary/deprecation-lint.rs +++ b/src/test/auxiliary/deprecation-lint.rs @@ -31,6 +31,9 @@ pub trait Trait { fn trait_deprecated_text(&self) {} } +#[deprecated(since = "1.0.0", note = "text")] +pub trait DeprecatedTrait { fn dummy(&self) { } } + impl Trait for MethodTester {} #[deprecated(since = "1.0.0", note = "text")] diff --git a/src/test/auxiliary/lint_stability.rs b/src/test/auxiliary/lint_stability.rs index 09d8302095f3d..3100aba4b72be 100644 --- a/src/test/auxiliary/lint_stability.rs +++ b/src/test/auxiliary/lint_stability.rs @@ -98,6 +98,12 @@ impl Trait for MethodTester {} #[unstable(feature = "test_feature", issue = "0")] pub trait UnstableTrait { fn dummy(&self) { } } +#[stable(feature = "test_feature", since = "1.0.0")] +#[rustc_deprecated(since = "1.0.0", reason = "text")] +pub trait DeprecatedTrait { + #[stable(feature = "test_feature", since = "1.0.0")] fn dummy(&self) { } +} + #[stable(feature = "test_feature", since = "1.0.0")] #[rustc_deprecated(since = "1.0.0", reason = "text")] pub struct DeprecatedStruct { diff --git a/src/test/compile-fail/deprecation-lint.rs b/src/test/compile-fail/deprecation-lint.rs index db6d5fd63e59b..58fa00fb41086 100644 --- a/src/test/compile-fail/deprecation-lint.rs +++ b/src/test/compile-fail/deprecation-lint.rs @@ -78,6 +78,11 @@ mod cross_crate { foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text } + struct S; + + impl DeprecatedTrait for S {} //~ ERROR use of deprecated item: text + trait LocalTrait : DeprecatedTrait { } //~ ERROR use of deprecated item: text + pub fn foo() { let x = Stable { override2: 3, diff --git a/src/test/compile-fail/lint-stability.rs b/src/test/compile-fail/lint-stability.rs index f32d7db244bcf..414d2a857acc7 100644 --- a/src/test/compile-fail/lint-stability.rs +++ b/src/test/compile-fail/lint-stability.rs @@ -227,8 +227,9 @@ mod cross_crate { struct S; impl UnstableTrait for S { } //~ ERROR use of unstable library feature - + impl DeprecatedTrait for S {} //~ ERROR use of deprecated item: text trait LocalTrait : UnstableTrait { } //~ ERROR use of unstable library feature + trait LocalTrait2 : DeprecatedTrait { } //~ ERROR use of deprecated item: text impl Trait for S { fn trait_stable(&self) {} From 67a978411a2009093bb40c4f1320a08d2a28b6c7 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 12 Dec 2015 23:01:27 +0300 Subject: [PATCH 3/3] Support `#[deprecated]` in rustdoc --- src/librustdoc/clean/inline.rs | 5 ++++ src/librustdoc/clean/mod.rs | 45 ++++++++++++++++++++++++++++++++++ src/librustdoc/doctree.rs | 12 +++++++++ src/librustdoc/fold.rs | 5 ++-- src/librustdoc/html/render.rs | 24 ++++++++++++++++-- src/librustdoc/visit_ast.rs | 18 ++++++++++++++ src/test/rustdoc/deprecated.rs | 16 ++++++++++++ 7 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 src/test/rustdoc/deprecated.rs diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 5c88576acb7b4..c0f62cddb98bf 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -121,6 +121,7 @@ fn try_inline_def(cx: &DocContext, tcx: &ty::ctxt, inner: inner, visibility: Some(hir::Public), stability: stability::lookup_stability(tcx, did).clean(cx), + deprecation: stability::lookup_deprecation(tcx, did).clean(cx), def_id: did, }); Some(ret) @@ -304,6 +305,7 @@ pub fn build_impl(cx: &DocContext, attrs: attrs, visibility: Some(hir::Inherited), stability: stability::lookup_stability(tcx, did).clean(cx), + deprecation: stability::lookup_deprecation(tcx, did).clean(cx), def_id: did, }); } @@ -334,6 +336,7 @@ pub fn build_impl(cx: &DocContext, attrs: vec![], visibility: None, stability: stability::lookup_stability(tcx, did).clean(cx), + deprecation: stability::lookup_deprecation(tcx, did).clean(cx), def_id: did }) } @@ -382,6 +385,7 @@ pub fn build_impl(cx: &DocContext, attrs: vec![], visibility: None, stability: stability::lookup_stability(tcx, did).clean(cx), + deprecation: stability::lookup_deprecation(tcx, did).clean(cx), def_id: did }) } @@ -415,6 +419,7 @@ pub fn build_impl(cx: &DocContext, attrs: attrs, visibility: Some(hir::Inherited), stability: stability::lookup_stability(tcx, did).clean(cx), + deprecation: stability::lookup_deprecation(tcx, did).clean(cx), def_id: did, }); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 942cc135aa8f5..52eeb781b31cc 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -65,6 +65,10 @@ fn get_stability(cx: &DocContext, def_id: DefId) -> Option { cx.tcx_opt().and_then(|tcx| stability::lookup_stability(tcx, def_id)).clean(cx) } +fn get_deprecation(cx: &DocContext, def_id: DefId) -> Option { + cx.tcx_opt().and_then(|tcx| stability::lookup_deprecation(tcx, def_id)).clean(cx) +} + pub trait Clean { fn clean(&self, cx: &DocContext) -> T; } @@ -188,6 +192,7 @@ impl<'a, 'tcx> Clean for visit_ast::RustdocVisitor<'a, 'tcx> { attrs: child.attrs.clone(), visibility: Some(hir::Public), stability: None, + deprecation: None, def_id: DefId::local(prim.to_def_index()), inner: PrimitiveItem(prim), }); @@ -254,6 +259,7 @@ pub struct Item { pub visibility: Option, pub def_id: DefId, pub stability: Option, + pub deprecation: Option, } impl Item { @@ -417,6 +423,7 @@ impl Clean for doctree::Module { source: whence.clean(cx), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), + deprecation: self.depr.clean(cx), def_id: cx.map.local_def_id(self.id), inner: ModuleItem(Module { is_crate: self.is_crate, @@ -1078,6 +1085,7 @@ impl Clean for doctree::Function { source: self.whence.clean(cx), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), + deprecation: self.depr.clean(cx), def_id: cx.map.local_def_id(self.id), inner: FunctionItem(Function { decl: self.decl.clean(cx), @@ -1204,6 +1212,7 @@ impl Clean for doctree::Trait { def_id: cx.map.local_def_id(self.id), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), + deprecation: self.depr.clean(cx), inner: TraitItem(Trait { unsafety: self.unsafety, items: self.items.clean(cx), @@ -1254,6 +1263,7 @@ impl Clean for hir::TraitItem { def_id: cx.map.local_def_id(self.id), visibility: None, stability: get_stability(cx, cx.map.local_def_id(self.id)), + deprecation: get_deprecation(cx, cx.map.local_def_id(self.id)), inner: inner } } @@ -1287,6 +1297,7 @@ impl Clean for hir::ImplItem { def_id: cx.map.local_def_id(self.id), visibility: self.vis.clean(cx), stability: get_stability(cx, cx.map.local_def_id(self.id)), + deprecation: get_deprecation(cx, cx.map.local_def_id(self.id)), inner: inner } } @@ -1357,6 +1368,7 @@ impl<'tcx> Clean for ty::Method<'tcx> { name: Some(self.name.clean(cx)), visibility: Some(hir::Inherited), stability: get_stability(cx, self.def_id), + deprecation: get_deprecation(cx, self.def_id), def_id: self.def_id, attrs: inline::load_attrs(cx, cx.tcx(), self.def_id), source: Span::empty(), @@ -1715,6 +1727,7 @@ impl Clean for hir::StructField { source: self.span.clean(cx), visibility: Some(vis), stability: get_stability(cx, cx.map.local_def_id(self.node.id)), + deprecation: get_deprecation(cx, cx.map.local_def_id(self.node.id)), def_id: cx.map.local_def_id(self.node.id), inner: StructFieldItem(TypedStructField(self.node.ty.clean(cx))), } @@ -1740,6 +1753,7 @@ impl<'tcx> Clean for ty::FieldDefData<'tcx, 'static> { source: Span::empty(), visibility: Some(self.vis), stability: get_stability(cx, self.did), + deprecation: get_deprecation(cx, self.did), def_id: self.did, inner: StructFieldItem(TypedStructField(self.unsubst_ty().clean(cx))), } @@ -1771,6 +1785,7 @@ impl Clean for doctree::Struct { def_id: cx.map.local_def_id(self.id), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), + deprecation: self.depr.clean(cx), inner: StructItem(Struct { struct_type: self.struct_type, generics: self.generics.clean(cx), @@ -1817,6 +1832,7 @@ impl Clean for doctree::Enum { def_id: cx.map.local_def_id(self.id), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), + deprecation: self.depr.clean(cx), inner: EnumItem(Enum { variants: self.variants.clean(cx), generics: self.generics.clean(cx), @@ -1839,6 +1855,7 @@ impl Clean for doctree::Variant { source: self.whence.clean(cx), visibility: None, stability: self.stab.clean(cx), + deprecation: self.depr.clean(cx), def_id: cx.map.local_def_id(self.def.id()), inner: VariantItem(Variant { kind: struct_def_to_variant_kind(&self.def, cx), @@ -1876,6 +1893,7 @@ impl<'tcx> Clean for ty::VariantDefData<'tcx, 'static> { // at the needed information here. def_id: self.did, stability: get_stability(cx, self.did), + deprecation: get_deprecation(cx, self.did), inner: StructFieldItem( TypedStructField(field.unsubst_ty().clean(cx)) ) @@ -1892,6 +1910,7 @@ impl<'tcx> Clean for ty::VariantDefData<'tcx, 'static> { def_id: self.did, inner: VariantItem(Variant { kind: kind }), stability: get_stability(cx, self.did), + deprecation: get_deprecation(cx, self.did), } } } @@ -2067,6 +2086,7 @@ impl Clean for doctree::Typedef { def_id: cx.map.local_def_id(self.id.clone()), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), + deprecation: self.depr.clean(cx), inner: TypedefItem(Typedef { type_: self.ty.clean(cx), generics: self.gen.clean(cx), @@ -2118,6 +2138,7 @@ impl Clean for doctree::Static { def_id: cx.map.local_def_id(self.id), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), + deprecation: self.depr.clean(cx), inner: StaticItem(Static { type_: self.type_.clean(cx), mutability: self.mutability.clean(cx), @@ -2142,6 +2163,7 @@ impl Clean for doctree::Constant { def_id: cx.map.local_def_id(self.id), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), + deprecation: self.depr.clean(cx), inner: ConstantItem(Constant { type_: self.type_.clean(cx), expr: self.expr.span.to_src(cx), @@ -2216,6 +2238,7 @@ impl Clean> for doctree::Impl { def_id: cx.map.local_def_id(self.id), visibility: self.vis.clean(cx), stability: self.stab.clean(cx), + deprecation: self.depr.clean(cx), inner: ImplItem(Impl { unsafety: self.unsafety, generics: self.generics.clean(cx), @@ -2298,6 +2321,7 @@ impl Clean for doctree::DefaultImpl { def_id: cx.map.local_def_id(self.id), visibility: Some(hir::Public), stability: None, + deprecation: None, inner: DefaultImplItem(DefaultImpl { unsafety: self.unsafety, trait_: self.trait_.clean(cx), @@ -2315,6 +2339,7 @@ impl Clean for doctree::ExternCrate { def_id: cx.map.local_def_id(0), visibility: self.vis.clean(cx), stability: None, + deprecation: None, inner: ExternCrateItem(self.name.clean(cx), self.path.clone()) } } @@ -2380,6 +2405,7 @@ impl Clean> for doctree::Import { def_id: cx.map.local_def_id(0), visibility: self.vis.clean(cx), stability: None, + deprecation: None, inner: ImportItem(inner) }); ret @@ -2466,6 +2492,7 @@ impl Clean for hir::ForeignItem { def_id: cx.map.local_def_id(self.id), visibility: self.vis.clean(cx), stability: get_stability(cx, cx.map.local_def_id(self.id)), + deprecation: get_deprecation(cx, cx.map.local_def_id(self.id)), inner: inner, } } @@ -2659,6 +2686,7 @@ impl Clean for doctree::Macro { source: self.whence.clean(cx), visibility: hir::Public.clean(cx), stability: self.stab.clean(cx), + deprecation: self.depr.clean(cx), def_id: cx.map.local_def_id(self.id), inner: MacroItem(Macro { source: format!("macro_rules! {} {{\n{}}}", @@ -2680,6 +2708,12 @@ pub struct Stability { pub issue: Option } +#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +pub struct Deprecation { + pub since: String, + pub note: String, +} + impl Clean for attr::Stability { fn clean(&self, _: &DocContext) -> Stability { Stability { @@ -2716,6 +2750,15 @@ impl<'a> Clean for &'a attr::Stability { } } +impl Clean for attr::Deprecation { + fn clean(&self, _: &DocContext) -> Deprecation { + Deprecation { + since: self.since.as_ref().map_or("".to_string(), |s| s.to_string()), + note: self.note.as_ref().map_or("".to_string(), |s| s.to_string()), + } + } +} + impl<'tcx> Clean for ty::AssociatedConst<'tcx> { fn clean(&self, cx: &DocContext) -> Item { Item { @@ -2726,6 +2769,7 @@ impl<'tcx> Clean for ty::AssociatedConst<'tcx> { visibility: None, def_id: self.def_id, stability: None, + deprecation: None, } } } @@ -2783,6 +2827,7 @@ impl<'tcx> Clean for ty::AssociatedType<'tcx> { visibility: self.vis.clean(cx), def_id: self.def_id, stability: stability::lookup_stability(cx.tcx(), self.def_id).clean(cx), + deprecation: stability::lookup_deprecation(cx.tcx(), self.def_id).clean(cx), } } } diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index 0129ab43cefd8..d1030a6fcb07c 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -40,6 +40,7 @@ pub struct Module { pub traits: Vec, pub vis: hir::Visibility, pub stab: Option, + pub depr: Option, pub impls: Vec, pub def_traits: Vec, pub foreigns: Vec, @@ -54,6 +55,7 @@ impl Module { id: 0, vis: hir::Inherited, stab: None, + depr: None, where_outer: syntax::codemap::DUMMY_SP, where_inner: syntax::codemap::DUMMY_SP, attrs : Vec::new(), @@ -96,6 +98,7 @@ pub enum TypeBound { pub struct Struct { pub vis: hir::Visibility, pub stab: Option, + pub depr: Option, pub id: NodeId, pub struct_type: StructType, pub name: Name, @@ -108,6 +111,7 @@ pub struct Struct { pub struct Enum { pub vis: hir::Visibility, pub stab: Option, + pub depr: Option, pub variants: Vec, pub generics: hir::Generics, pub attrs: Vec, @@ -121,6 +125,7 @@ pub struct Variant { pub attrs: Vec, pub def: hir::VariantData, pub stab: Option, + pub depr: Option, pub whence: Span, } @@ -131,6 +136,7 @@ pub struct Function { pub name: Name, pub vis: hir::Visibility, pub stab: Option, + pub depr: Option, pub unsafety: hir::Unsafety, pub constness: hir::Constness, pub whence: Span, @@ -147,6 +153,7 @@ pub struct Typedef { pub whence: Span, pub vis: hir::Visibility, pub stab: Option, + pub depr: Option, } #[derive(Debug)] @@ -158,6 +165,7 @@ pub struct Static { pub attrs: Vec, pub vis: hir::Visibility, pub stab: Option, + pub depr: Option, pub id: ast::NodeId, pub whence: Span, } @@ -169,6 +177,7 @@ pub struct Constant { pub attrs: Vec, pub vis: hir::Visibility, pub stab: Option, + pub depr: Option, pub id: ast::NodeId, pub whence: Span, } @@ -184,6 +193,7 @@ pub struct Trait { pub whence: Span, pub vis: hir::Visibility, pub stab: Option, + pub depr: Option, } pub struct Impl { @@ -197,6 +207,7 @@ pub struct Impl { pub whence: Span, pub vis: hir::Visibility, pub stab: Option, + pub depr: Option, pub id: ast::NodeId, } @@ -215,6 +226,7 @@ pub struct Macro { pub whence: Span, pub matchers: Vec, pub stab: Option, + pub depr: Option, pub imported_from: Option, } diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index 0a1860c66f273..5a4f95d1a1a5a 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -19,7 +19,7 @@ pub trait DocFolder : Sized { /// don't override! fn fold_item_recur(&mut self, item: Item) -> Option { - let Item { attrs, name, source, visibility, def_id, inner, stability } = item; + let Item { attrs, name, source, visibility, def_id, inner, stability, deprecation } = item; let inner = inner; let inner = match inner { StructItem(mut i) => { @@ -66,7 +66,8 @@ pub trait DocFolder : Sized { }; Some(Item { attrs: attrs, name: name, source: source, inner: inner, - visibility: visibility, stability: stability, def_id: def_id }) + visibility: visibility, stability: stability, deprecation: deprecation, + def_id: def_id }) } fn fold_mod(&mut self, m: Module) -> Module { diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 45969ed0c644d..09a2a1b1c0269 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1801,7 +1801,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, } fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Option { - item.stability.as_ref().and_then(|stab| { + let mut result = item.stability.as_ref().and_then(|stab| { let reason = if show_reason && !stab.reason.is_empty() { format!(": {}", stab.reason) } else { @@ -1836,7 +1836,27 @@ fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Optio }; Some(format!("{}", item.stability_class(), text)) - }) + }); + + if result.is_none() { + result = item.deprecation.as_ref().and_then(|depr| { + let note = if show_reason && !depr.note.is_empty() { + format!(": {}", depr.note) + } else { + String::new() + }; + let since = if show_reason && !depr.since.is_empty() { + format!(" since {}", Escape(&depr.since)) + } else { + String::new() + }; + + let text = format!("Deprecated{}{}", since, Markdown(¬e)); + Some(format!("{}", text)) + }); + } + + result } struct Initializer<'a>(&'a str); diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 89527c54a3851..d95a4553bf1f5 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -69,6 +69,13 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { }) } + fn deprecation(&self, id: ast::NodeId) -> Option { + self.cx.tcx_opt().and_then(|tcx| { + self.cx.map.opt_local_def_id(id) + .and_then(|def_id| stability::lookup_deprecation(tcx, def_id)) + }) + } + pub fn visit(&mut self, krate: &hir::Crate) { self.attrs = krate.attrs.clone(); @@ -95,6 +102,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { name: name, vis: item.vis, stab: self.stability(item.id), + depr: self.deprecation(item.id), attrs: item.attrs.clone(), generics: generics.clone(), fields: sd.fields().iter().cloned().collect(), @@ -112,11 +120,13 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { name: v.node.name, attrs: v.node.attrs.clone(), stab: self.stability(v.node.data.id()), + depr: self.deprecation(v.node.data.id()), def: v.node.data.clone(), whence: v.span, }).collect(), vis: it.vis, stab: self.stability(it.id), + depr: self.deprecation(it.id), generics: params.clone(), attrs: it.attrs.clone(), id: it.id, @@ -135,6 +145,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { id: item.id, vis: item.vis, stab: self.stability(item.id), + depr: self.deprecation(item.id), attrs: item.attrs.clone(), decl: fd.clone(), name: name, @@ -156,6 +167,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { om.attrs = attrs; om.vis = vis; om.stab = self.stability(id); + om.depr = self.deprecation(id); om.id = id; for i in &m.item_ids { let item = self.cx.map.expect_item(i.id); @@ -314,6 +326,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { whence: item.span, vis: item.vis, stab: self.stability(item.id), + depr: self.deprecation(item.id), }; om.typedefs.push(t); }, @@ -328,6 +341,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { whence: item.span, vis: item.vis, stab: self.stability(item.id), + depr: self.deprecation(item.id), }; om.statics.push(s); }, @@ -341,6 +355,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { whence: item.span, vis: item.vis, stab: self.stability(item.id), + depr: self.deprecation(item.id), }; om.constants.push(s); }, @@ -356,6 +371,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { whence: item.span, vis: item.vis, stab: self.stability(item.id), + depr: self.deprecation(item.id), }; om.traits.push(t); }, @@ -372,6 +388,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { whence: item.span, vis: item.vis, stab: self.stability(item.id), + depr: self.deprecation(item.id), }; // Don't duplicate impls when inlining glob imports, we'll pick // them up regardless of where they're located. @@ -410,6 +427,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { whence: def.span, matchers: matchers, stab: self.stability(def.id), + depr: self.deprecation(def.id), imported_from: def.imported_from, } } diff --git a/src/test/rustdoc/deprecated.rs b/src/test/rustdoc/deprecated.rs new file mode 100644 index 0000000000000..744304a62c216 --- /dev/null +++ b/src/test/rustdoc/deprecated.rs @@ -0,0 +1,16 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(deprecated)] + +// @has deprecated/struct.S.html '//*[@class="stab deprecated"]' \ +// 'Deprecated since 1.0.0: text' +#[deprecated(since = "1.0.0", note = "text")] +pub struct S;