Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement #[deprecated] attribute (RFC 1270) #30206

Merged
merged 3 commits into from
Dec 16, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/doc/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/middle/cstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ pub enum FoundAst<'ast> {
pub trait CrateStore<'tcx> : Any {
// item info
fn stability(&self, def: DefId) -> Option<attr::Stability>;
fn deprecation(&self, def: DefId) -> Option<attr::Deprecation>;
fn closure_kind(&self, tcx: &ty::ctxt<'tcx>, def_id: DefId)
-> ty::ClosureKind;
fn closure_ty(&self, tcx: &ty::ctxt<'tcx>, def_id: DefId)
Expand Down Expand Up @@ -292,6 +293,7 @@ pub struct DummyCrateStore;
impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
// item info
fn stability(&self, def: DefId) -> Option<attr::Stability> { unimplemented!() }
fn deprecation(&self, def: DefId) -> Option<attr::Deprecation> { 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)
Expand Down
158 changes: 92 additions & 66 deletions src/librustc/middle/stability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Option<&'tcx Stability>>,
stab_map: DefIdMap<Option<&'tcx Stability>>,
depr_map: DefIdMap<Option<Deprecation>>,

/// Maps for each crate whether it is part of the staged API.
staged_api: FnvHashMap<ast::CrateNum, bool>
Expand All @@ -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<Deprecation>,
access_levels: &'a AccessLevels,
in_trait_impl: bool,
in_enum: bool,
Expand All @@ -86,31 +88,35 @@ 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()
}
}

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::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::<u64>(), stab_v.parse()) {
Expand All @@ -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;
}
}
Expand All @@ -167,7 +173,26 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
outside of the standard library");
}
}
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);
}
}
}
}
Expand Down Expand Up @@ -269,7 +294,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,
Expand All @@ -291,7 +317,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(),
}
}
}
Expand Down Expand Up @@ -327,7 +354,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<Deprecation>) {
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 {
Expand Down Expand Up @@ -395,31 +426,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)
}

Expand All @@ -441,7 +472,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<Deprecation>)) {
match item.node {
hir::ItemExternCrate(_) => {
// compiler-generated `extern crate` items have a dummy span.
Expand Down Expand Up @@ -478,7 +509,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<Deprecation>)) {
let span;
let id = match e.node {
hir::ExprMethodCall(i, _, _) => {
Expand Down Expand Up @@ -539,7 +570,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<Deprecation>)) {
match tcx.def_map.borrow().get(&id).map(|d| d.full_def()) {
Some(def::DefPrimTy(..)) => {}
Some(def::DefSelfTy(..)) => {}
Expand All @@ -551,7 +582,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<Deprecation>)) {
match tcx.def_map.borrow().get(&item.node.id()).map(|d| d.full_def()) {
Some(def::DefPrimTy(..)) => {}
Some(def) => {
Expand All @@ -562,7 +593,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<Deprecation>)) {
debug!("check_pat(pat = {:?})", pat);
if is_internal(tcx, pat.span) { return; }

Expand Down Expand Up @@ -591,21 +622,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<Deprecation>)) {
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 {
Expand All @@ -627,47 +658,42 @@ 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> {
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)
}
_ => {}
pub fn lookup_deprecation<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<Deprecation> {
if let Some(depr) = tcx.stability.borrow().depr_map.get(&id) {
return depr.clone();
}

let item_stab = if id.is_local() {
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);
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(tcx, trait_id);
}
}
None
})
fn lookup_deprecation_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<Deprecation> {
debug!("lookup(id={:?})", id);
if id.is_local() {
None // The stability cache is filled partially lazily
} else {
tcx.sess.cstore.deprecation(id)
}
}

/// Given the list of enabled features that were not language features (i.e. that
Expand Down
Loading