1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! A pass that annotates every item and method with its stability level,
12 //! propagating default levels lexically from parent to children ast nodes.
14 pub use self::StabilityLevel::*;
16 use dep_graph::DepNode;
17 use hir::map as hir_map;
21 use hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId, DefIndex, LOCAL_CRATE};
22 use ty::{self, TyCtxt, AdtKind};
23 use middle::privacy::AccessLevels;
24 use syntax::symbol::Symbol;
25 use syntax_pos::{Span, DUMMY_SP};
27 use syntax::ast::{NodeId, Attribute};
28 use syntax::feature_gate::{GateIssue, emit_feature_err, find_lang_feature_accepted_version};
29 use syntax::attr::{self, Stability, Deprecation};
30 use util::nodemap::{DefIdMap, FxHashSet, FxHashMap};
33 use hir::{Item, Generics, StructField, Variant, PatKind};
34 use hir::intravisit::{self, Visitor};
35 use hir::pat_util::EnumerateAndAdjustIterator;
37 use std::mem::replace;
38 use std::cmp::Ordering;
40 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Copy, Debug, Eq, Hash)]
41 pub enum StabilityLevel {
47 pub fn from_attr_level(level: &attr::StabilityLevel) -> Self {
48 if level.is_stable() { Stable } else { Unstable }
54 // Annotation is required if not inherited from unstable parents
56 // Annotation is useless, reject it
58 // Annotation itself is useless, but it can be propagated to children
62 /// An entry in the `depr_map`.
64 pub struct DeprecationEntry {
65 /// The metadata of the attribute associated with this entry.
66 pub attr: Deprecation,
67 /// The def id where the attr was originally attached. `None` for non-local
69 origin: Option<DefIndex>,
72 impl DeprecationEntry {
73 fn local(attr: Deprecation, id: DefId) -> DeprecationEntry {
74 assert!(id.is_local());
77 origin: Some(id.index),
81 fn external(attr: Deprecation) -> DeprecationEntry {
88 pub fn same_origin(&self, other: &DeprecationEntry) -> bool {
89 match (self.origin, other.origin) {
90 (Some(o1), Some(o2)) => o1 == o2,
96 /// A stability index, giving the stability level for items and methods.
97 pub struct Index<'tcx> {
98 /// This is mostly a cache, except the stabilities of local items
99 /// are filled by the annotator.
100 stab_map: DefIdMap<Option<&'tcx Stability>>,
101 depr_map: DefIdMap<Option<DeprecationEntry>>,
103 /// Maps for each crate whether it is part of the staged API.
104 staged_api: FxHashMap<CrateNum, bool>
107 // A private tree-walker for producing an Index.
108 struct Annotator<'a, 'tcx: 'a> {
109 tcx: TyCtxt<'a, 'tcx, 'tcx>,
110 index: &'a mut Index<'tcx>,
111 parent_stab: Option<&'tcx Stability>,
112 parent_depr: Option<DeprecationEntry>,
113 access_levels: &'a AccessLevels,
117 impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
118 // Determine the stability for a node based on its attributes and inherited
119 // stability. The stability is recorded in the index and used as the parent.
120 fn annotate<F>(&mut self, id: NodeId, attrs: &[Attribute],
121 item_sp: Span, kind: AnnotationKind, visit_children: F)
122 where F: FnOnce(&mut Self)
124 if self.index.staged_api[&LOCAL_CRATE] && self.tcx.sess.features.borrow().staged_api {
125 debug!("annotate(id = {:?}, attrs = {:?})", id, attrs);
126 if let Some(..) = attr::find_deprecation(self.tcx.sess.diagnostic(), attrs, item_sp) {
127 self.tcx.sess.span_err(item_sp, "`#[deprecated]` cannot be used in staged api, \
128 use `#[rustc_deprecated]` instead");
130 if let Some(mut stab) = attr::find_stability(self.tcx.sess.diagnostic(),
132 // Error if prohibited, or can't inherit anything from a container
133 if kind == AnnotationKind::Prohibited ||
134 (kind == AnnotationKind::Container &&
135 stab.level.is_stable() &&
136 stab.rustc_depr.is_none()) {
137 self.tcx.sess.span_err(item_sp, "This stability annotation is useless");
140 debug!("annotate: found {:?}", stab);
141 // If parent is deprecated and we're not, inherit this by merging
142 // deprecated_since and its reason.
143 if let Some(parent_stab) = self.parent_stab {
144 if parent_stab.rustc_depr.is_some() && stab.rustc_depr.is_none() {
145 stab.rustc_depr = parent_stab.rustc_depr.clone()
149 let stab = self.tcx.intern_stability(stab);
151 // Check if deprecated_since < stable_since. If it is,
152 // this is *almost surely* an accident.
153 if let (&Some(attr::RustcDeprecation {since: dep_since, ..}),
154 &attr::Stable {since: stab_since}) = (&stab.rustc_depr, &stab.level) {
155 // Explicit version of iter::order::lt to handle parse errors properly
156 for (dep_v, stab_v) in
157 dep_since.as_str().split(".").zip(stab_since.as_str().split(".")) {
158 if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::<u64>(), stab_v.parse()) {
159 match dep_v.cmp(&stab_v) {
161 self.tcx.sess.span_err(item_sp, "An API can't be stabilized \
162 after it is deprecated");
165 Ordering::Equal => continue,
166 Ordering::Greater => break,
169 // Act like it isn't less because the question is now nonsensical,
170 // and this makes us not do anything else interesting.
171 self.tcx.sess.span_err(item_sp, "Invalid stability or deprecation \
178 let def_id = self.tcx.map.local_def_id(id);
179 self.index.stab_map.insert(def_id, Some(stab));
181 let orig_parent_stab = replace(&mut self.parent_stab, Some(stab));
182 visit_children(self);
183 self.parent_stab = orig_parent_stab;
185 debug!("annotate: not found, parent = {:?}", self.parent_stab);
186 let mut is_error = kind == AnnotationKind::Required &&
187 self.access_levels.is_reachable(id) &&
188 !self.tcx.sess.opts.test;
189 if let Some(stab) = self.parent_stab {
190 if stab.level.is_unstable() {
191 let def_id = self.tcx.map.local_def_id(id);
192 self.index.stab_map.insert(def_id, Some(stab));
197 self.tcx.sess.span_err(item_sp, "This node does not have \
198 a stability attribute");
200 visit_children(self);
203 // Emit errors for non-staged-api crates.
205 let tag = attr.name();
206 if tag == "unstable" || tag == "stable" || tag == "rustc_deprecated" {
207 attr::mark_used(attr);
208 self.tcx.sess.span_err(attr.span(), "stability attributes may not be used \
209 outside of the standard library");
213 if let Some(depr) = attr::find_deprecation(self.tcx.sess.diagnostic(), attrs, item_sp) {
214 if kind == AnnotationKind::Prohibited {
215 self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless");
218 // `Deprecation` is just two pointers, no need to intern it
219 let def_id = self.tcx.map.local_def_id(id);
220 let depr_entry = Some(DeprecationEntry::local(depr, def_id));
221 self.index.depr_map.insert(def_id, depr_entry.clone());
223 let orig_parent_depr = replace(&mut self.parent_depr, depr_entry);
224 visit_children(self);
225 self.parent_depr = orig_parent_depr;
226 } else if let parent_depr @ Some(_) = self.parent_depr.clone() {
227 let def_id = self.tcx.map.local_def_id(id);
228 self.index.depr_map.insert(def_id, parent_depr);
229 visit_children(self);
231 visit_children(self);
237 impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> {
238 /// Because stability levels are scoped lexically, we want to walk
239 /// nested items in the context of the outer item, so enable
241 fn nested_visit_map(&mut self) -> Option<&hir::map::Map<'tcx>> {
245 fn visit_item(&mut self, i: &'tcx Item) {
246 let orig_in_trait_impl = self.in_trait_impl;
247 let mut kind = AnnotationKind::Required;
249 // Inherent impls and foreign modules serve only as containers for other items,
250 // they don't have their own stability. They still can be annotated as unstable
251 // and propagate this unstability to children, but this annotation is completely
252 // optional. They inherit stability from their parents when unannotated.
253 hir::ItemImpl(.., None, _, _) | hir::ItemForeignMod(..) => {
254 self.in_trait_impl = false;
255 kind = AnnotationKind::Container;
257 hir::ItemImpl(.., Some(_), _, _) => {
258 self.in_trait_impl = true;
260 hir::ItemStruct(ref sd, _) => {
262 self.annotate(sd.id(), &i.attrs, i.span, AnnotationKind::Required, |_| {})
268 self.annotate(i.id, &i.attrs, i.span, kind, |v| {
269 intravisit::walk_item(v, i)
271 self.in_trait_impl = orig_in_trait_impl;
274 fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem) {
275 self.annotate(ti.id, &ti.attrs, ti.span, AnnotationKind::Required, |v| {
276 intravisit::walk_trait_item(v, ti);
280 fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem) {
281 let kind = if self.in_trait_impl {
282 AnnotationKind::Prohibited
284 AnnotationKind::Required
286 self.annotate(ii.id, &ii.attrs, ii.span, kind, |v| {
287 intravisit::walk_impl_item(v, ii);
291 fn visit_variant(&mut self, var: &'tcx Variant, g: &'tcx Generics, item_id: NodeId) {
292 self.annotate(var.node.data.id(), &var.node.attrs, var.span, AnnotationKind::Required, |v| {
293 intravisit::walk_variant(v, var, g, item_id);
297 fn visit_struct_field(&mut self, s: &'tcx StructField) {
298 self.annotate(s.id, &s.attrs, s.span, AnnotationKind::Required, |v| {
299 intravisit::walk_struct_field(v, s);
303 fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem) {
304 self.annotate(i.id, &i.attrs, i.span, AnnotationKind::Required, |v| {
305 intravisit::walk_foreign_item(v, i);
309 fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef) {
310 if md.imported_from.is_none() {
311 self.annotate(md.id, &md.attrs, md.span, AnnotationKind::Required, |_| {});
316 impl<'a, 'tcx> Index<'tcx> {
317 /// Construct the stability index for a crate being compiled.
318 pub fn build(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, access_levels: &AccessLevels) {
319 let _task = tcx.dep_graph.in_task(DepNode::StabilityIndex);
320 let krate = tcx.map.krate();
321 let mut annotator = Annotator {
326 access_levels: access_levels,
327 in_trait_impl: false,
329 annotator.annotate(ast::CRATE_NODE_ID, &krate.attrs, krate.span, AnnotationKind::Required,
330 |v| intravisit::walk_crate(v, krate));
333 pub fn new(hir_map: &hir_map::Map) -> Index<'tcx> {
334 let _task = hir_map.dep_graph.in_task(DepNode::StabilityIndex);
335 let krate = hir_map.krate();
337 let mut is_staged_api = false;
338 for attr in &krate.attrs {
339 if attr.name() == "stable" || attr.name() == "unstable" {
340 is_staged_api = true;
345 let mut staged_api = FxHashMap();
346 staged_api.insert(LOCAL_CRATE, is_staged_api);
348 staged_api: staged_api,
349 stab_map: DefIdMap(),
350 depr_map: DefIdMap(),
355 /// Cross-references the feature names of unstable APIs with enabled
356 /// features and possibly prints errors. Returns a list of all
358 pub fn check_unstable_api_usage<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>)
359 -> FxHashMap<Symbol, attr::StabilityLevel> {
360 let _task = tcx.dep_graph.in_task(DepNode::StabilityCheck);
361 let ref active_lib_features = tcx.sess.features.borrow().declared_lib_features;
363 // Put the active features into a map for quick lookup
364 let active_features = active_lib_features.iter().map(|&(ref s, _)| s.clone()).collect();
366 let mut checker = Checker {
368 active_features: active_features,
369 used_features: FxHashMap(),
372 intravisit::walk_crate(&mut checker, tcx.map.krate());
374 checker.used_features
377 struct Checker<'a, 'tcx: 'a> {
378 tcx: TyCtxt<'a, 'tcx, 'tcx>,
379 active_features: FxHashSet<Symbol>,
380 used_features: FxHashMap<Symbol, attr::StabilityLevel>,
381 // Within a block where feature gate checking can be skipped.
385 impl<'a, 'tcx> Checker<'a, 'tcx> {
386 fn check(&mut self, id: DefId, span: Span,
387 stab: &Option<&Stability>, _depr: &Option<DeprecationEntry>) {
388 if !is_staged_api(self.tcx, id) {
391 // Only the cross-crate scenario matters when checking unstable APIs
392 let cross_crate = !id.is_local();
397 // We don't need to check for stability - presumably compiler generated code.
398 if self.in_skip_block > 0 {
403 Some(&Stability { level: attr::Unstable {ref reason, issue}, ref feature, .. }) => {
404 self.used_features.insert(feature.clone(),
405 attr::Unstable { reason: reason.clone(), issue: issue });
407 if !self.active_features.contains(feature) {
408 let msg = match *reason {
409 Some(ref r) => format!("use of unstable library feature '{}': {}",
410 &feature.as_str(), &r),
411 None => format!("use of unstable library feature '{}'", &feature)
413 emit_feature_err(&self.tcx.sess.parse_sess, &feature.as_str(), span,
414 GateIssue::Library(Some(issue)), &msg);
417 Some(&Stability { ref level, ref feature, .. }) => {
418 self.used_features.insert(feature.clone(), level.clone());
420 // Stable APIs are always ok to call and deprecated APIs are
421 // handled by a lint.
424 // This is an 'unmarked' API, which should not exist
425 // in the standard library.
426 if self.tcx.sess.features.borrow().unmarked_api {
427 self.tcx.sess.struct_span_warn(span, "use of unmarked library feature")
428 .span_note(span, "this is either a bug in the library you are \
429 using or a bug in the compiler - please \
430 report it in both places")
433 self.tcx.sess.struct_span_err(span, "use of unmarked library feature")
434 .span_note(span, "this is either a bug in the library you are \
435 using or a bug in the compiler - please \
436 report it in both places")
437 .span_note(span, "use #![feature(unmarked_api)] in the \
438 crate attributes to override this")
446 impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
447 /// Because stability levels are scoped lexically, we want to walk
448 /// nested items in the context of the outer item, so enable
450 fn nested_visit_map(&mut self) -> Option<&hir::map::Map<'tcx>> {
454 fn visit_item(&mut self, item: &'tcx hir::Item) {
455 // When compiling with --test we don't enforce stability on the
456 // compiler-generated test module, demarcated with `DUMMY_SP` plus the
458 if item.span == DUMMY_SP && item.name == "__test" { return }
460 check_item(self.tcx, item, true,
461 &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
462 intravisit::walk_item(self, item);
465 fn visit_expr(&mut self, ex: &'tcx hir::Expr) {
466 check_expr(self.tcx, ex,
467 &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
468 intravisit::walk_expr(self, ex);
471 fn visit_path(&mut self, path: &'tcx hir::Path, id: ast::NodeId) {
472 check_path(self.tcx, path, id,
473 &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
474 intravisit::walk_path(self, path)
477 fn visit_pat(&mut self, pat: &'tcx hir::Pat) {
478 check_pat(self.tcx, pat,
479 &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
480 intravisit::walk_pat(self, pat)
483 fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
484 check_ty(self.tcx, ty,
485 &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
486 intravisit::walk_ty(self, ty)
489 fn visit_block(&mut self, b: &'tcx hir::Block) {
490 let old_skip_count = self.in_skip_block;
492 hir::BlockCheckMode::PushUnstableBlock => {
493 self.in_skip_block += 1;
495 hir::BlockCheckMode::PopUnstableBlock => {
496 self.in_skip_block = self.in_skip_block.checked_sub(1).unwrap();
500 intravisit::walk_block(self, b);
501 self.in_skip_block = old_skip_count;
505 /// Helper for discovering nodes to check for stability
506 pub fn check_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
508 warn_about_defns: bool,
509 cb: &mut FnMut(DefId, Span,
511 &Option<DeprecationEntry>)) {
513 hir::ItemExternCrate(_) => {
514 // compiler-generated `extern crate` items have a dummy span.
515 if item.span == DUMMY_SP { return }
517 let cnum = match tcx.sess.cstore.extern_mod_stmt_cnum(item.id) {
521 let id = DefId { krate: cnum, index: CRATE_DEF_INDEX };
522 maybe_do_stability_check(tcx, id, item.span, cb);
525 // For implementations of traits, check the stability of each item
526 // individually as it's possible to have a stable trait with unstable
528 hir::ItemImpl(.., Some(ref t), _, ref impl_item_refs) => {
529 let trait_did = tcx.expect_def(t.ref_id).def_id();
530 for impl_item_ref in impl_item_refs {
531 let impl_item = tcx.map.impl_item(impl_item_ref.id);
532 let item = tcx.associated_items(trait_did)
533 .find(|item| item.name == impl_item.name).unwrap();
534 if warn_about_defns {
535 maybe_do_stability_check(tcx, item.def_id, impl_item.span, cb);
544 /// Helper for discovering nodes to check for stability
545 pub fn check_expr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, e: &hir::Expr,
546 cb: &mut FnMut(DefId, Span,
548 &Option<DeprecationEntry>)) {
550 let id = match e.node {
551 hir::ExprMethodCall(i, ..) => {
553 let method_call = ty::MethodCall::expr(e.id);
554 tcx.tables().method_map[&method_call].def_id
556 hir::ExprPath(hir::QPath::TypeRelative(..)) => {
558 tcx.expect_def(e.id).def_id()
560 hir::ExprField(ref base_e, ref field) => {
562 match tcx.tables().expr_ty_adjusted(base_e).sty {
563 ty::TyAdt(def, _) => {
564 def.struct_variant().field_named(field.node).did
566 _ => span_bug!(e.span,
567 "stability::check_expr: named field access on non-ADT")
570 hir::ExprTupField(ref base_e, ref field) => {
572 match tcx.tables().expr_ty_adjusted(base_e).sty {
573 ty::TyAdt(def, _) => {
574 def.struct_variant().fields[field.node].did
576 ty::TyTuple(..) => return,
577 _ => span_bug!(e.span,
578 "stability::check_expr: unnamed field access on \
579 something other than a tuple or struct")
582 hir::ExprStruct(_, ref expr_fields, _) => {
583 match tcx.tables().expr_ty(e).sty {
584 ty::TyAdt(adt, ..) => match adt.adt_kind() {
585 AdtKind::Struct | AdtKind::Union => {
586 // check the stability of each field that appears
587 // in the construction expression.
588 for field in expr_fields {
589 let did = adt.struct_variant().field_named(field.name.node).did;
590 maybe_do_stability_check(tcx, did, field.span, cb);
597 // we don't look at stability attributes on
598 // struct-like enums (yet...), but it's definitely not
599 // a bug to have construct one.
603 ref ty => span_bug!(e.span, "stability::check_expr: struct \
604 construction of non-ADT type: {:?}", ty)
610 maybe_do_stability_check(tcx, id, span, cb);
613 pub fn check_path<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
614 path: &hir::Path, id: ast::NodeId,
615 cb: &mut FnMut(DefId, Span,
617 &Option<DeprecationEntry>)) {
618 // Paths in import prefixes may have no resolution.
619 match tcx.expect_def_or_none(id) {
620 None | Some(Def::PrimTy(..)) | Some(Def::SelfTy(..)) => {}
621 Some(def) => maybe_do_stability_check(tcx, def.def_id(), path.span, cb)
625 pub fn check_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pat: &hir::Pat,
626 cb: &mut FnMut(DefId, Span,
628 &Option<DeprecationEntry>)) {
629 debug!("check_pat(pat = {:?})", pat);
630 if is_internal(tcx, pat.span) { return; }
632 if let PatKind::Path(hir::QPath::TypeRelative(..)) = pat.node {
633 let def_id = tcx.expect_def(pat.id).def_id();
634 maybe_do_stability_check(tcx, def_id, pat.span, cb)
637 let v = match tcx.tables().pat_ty_opt(pat).map(|ty| &ty.sty) {
638 Some(&ty::TyAdt(adt, _)) if !adt.is_enum() => adt.struct_variant(),
643 PatKind::TupleStruct(_, ref pat_fields, ddpos) => {
644 for (i, field) in pat_fields.iter().enumerate_and_adjust(v.fields.len(), ddpos) {
645 maybe_do_stability_check(tcx, v.fields[i].did, field.span, cb)
649 PatKind::Struct(_, ref pat_fields, _) => {
650 for field in pat_fields {
651 let did = v.field_named(field.node.name).did;
652 maybe_do_stability_check(tcx, did, field.span, cb);
655 // everything else is fine.
660 pub fn check_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: &hir::Ty,
661 cb: &mut FnMut(DefId, Span,
663 &Option<DeprecationEntry>)) {
664 debug!("check_ty(ty = {:?})", ty);
665 if is_internal(tcx, ty.span) { return; }
667 if let hir::TyPath(hir::QPath::TypeRelative(..)) = ty.node {
668 let def_id = tcx.expect_def(ty.id).def_id();
669 maybe_do_stability_check(tcx, def_id, ty.span, cb);
673 fn maybe_do_stability_check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
674 id: DefId, span: Span,
675 cb: &mut FnMut(DefId, Span,
677 &Option<DeprecationEntry>)) {
678 if is_internal(tcx, span) {
679 debug!("maybe_do_stability_check: \
680 skipping span={:?} since it is internal", span);
683 let (stability, deprecation) = if is_staged_api(tcx, id) {
684 (tcx.lookup_stability(id), None)
686 (None, tcx.lookup_deprecation_entry(id))
688 debug!("maybe_do_stability_check: \
689 inspecting id={:?} span={:?} of stability={:?}", id, span, stability);
690 cb(id, span, &stability, &deprecation);
693 fn is_internal<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, span: Span) -> bool {
694 tcx.sess.codemap().span_allows_unstable(span)
697 fn is_staged_api<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> bool {
698 *tcx.stability.borrow_mut().staged_api.entry(id.krate).or_insert_with(
699 || tcx.sess.cstore.is_staged_api(id.krate))
702 impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
703 /// Lookup the stability for a node, loading external crate
704 /// metadata as necessary.
705 pub fn lookup_stability(self, id: DefId) -> Option<&'tcx Stability> {
706 if let Some(st) = self.stability.borrow().stab_map.get(&id) {
710 let st = self.lookup_stability_uncached(id);
711 self.stability.borrow_mut().stab_map.insert(id, st);
715 pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
716 self.lookup_deprecation_entry(id).map(|depr| depr.attr)
719 pub fn lookup_deprecation_entry(self, id: DefId) -> Option<DeprecationEntry> {
720 if let Some(depr) = self.stability.borrow().depr_map.get(&id) {
724 let depr = self.lookup_deprecation_uncached(id);
725 self.stability.borrow_mut().depr_map.insert(id, depr.clone());
729 fn lookup_stability_uncached(self, id: DefId) -> Option<&'tcx Stability> {
730 debug!("lookup(id={:?})", id);
732 None // The stability cache is filled partially lazily
734 self.sess.cstore.stability(id).map(|st| self.intern_stability(st))
738 fn lookup_deprecation_uncached(self, id: DefId) -> Option<DeprecationEntry> {
739 debug!("lookup(id={:?})", id);
741 None // The stability cache is filled partially lazily
743 self.sess.cstore.deprecation(id).map(DeprecationEntry::external)
748 /// Given the list of enabled features that were not language features (i.e. that
749 /// were expected to be library features), and the list of features used from
750 /// libraries, identify activated features that don't exist and error about them.
751 pub fn check_unused_or_stable_features(sess: &Session,
752 lib_features_used: &FxHashMap<Symbol,
753 attr::StabilityLevel>) {
754 let ref declared_lib_features = sess.features.borrow().declared_lib_features;
755 let mut remaining_lib_features: FxHashMap<Symbol, Span>
756 = declared_lib_features.clone().into_iter().collect();
758 fn format_stable_since_msg(version: &str) -> String {
759 format!("this feature has been stable since {}. Attribute no longer needed", version)
762 for &(ref stable_lang_feature, span) in &sess.features.borrow().declared_stable_lang_features {
763 let version = find_lang_feature_accepted_version(&stable_lang_feature.as_str())
764 .expect("unexpectedly couldn't find version feature was stabilized");
765 sess.add_lint(lint::builtin::STABLE_FEATURES,
768 format_stable_since_msg(version));
771 for (used_lib_feature, level) in lib_features_used {
772 match remaining_lib_features.remove(used_lib_feature) {
774 if let &attr::StabilityLevel::Stable { since: ref version } = level {
775 sess.add_lint(lint::builtin::STABLE_FEATURES,
778 format_stable_since_msg(&version.as_str()));
781 None => ( /* used but undeclared, handled during the previous ast visit */ )
785 for &span in remaining_lib_features.values() {
786 sess.add_lint(lint::builtin::UNUSED_FEATURES,
789 "unused or unknown feature".to_string());