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.
16 use metadata::cstore::LOCAL_CRATE;
18 use middle::def_id::{CRATE_DEF_INDEX, DefId};
20 use middle::privacy::PublicItems;
21 use metadata::csearch;
22 use syntax::parse::token::InternedString;
23 use syntax::codemap::{Span, DUMMY_SP};
25 use syntax::ast::{NodeId, Attribute};
26 use syntax::feature_gate::{GateIssue, emit_feature_err};
27 use syntax::attr::{self, Stability, AttrMetaMethods};
28 use util::nodemap::{DefIdMap, FnvHashSet, FnvHashMap};
31 use rustc_front::hir::{FnDecl, Block, Crate, Item, Generics, StructField, Variant};
32 use rustc_front::visit::{self, FnKind, Visitor};
34 use std::mem::replace;
35 use std::cmp::Ordering;
37 /// A stability index, giving the stability level for items and methods.
38 pub struct Index<'tcx> {
39 /// This is mostly a cache, except the stabilities of local items
40 /// are filled by the annotator.
41 map: DefIdMap<Option<&'tcx Stability>>,
43 /// Maps for each crate whether it is part of the staged API.
44 staged_api: FnvHashMap<ast::CrateNum, bool>
47 // A private tree-walker for producing an Index.
48 struct Annotator<'a, 'tcx: 'a> {
49 tcx: &'a ty::ctxt<'tcx>,
50 index: &'a mut Index<'tcx>,
51 parent: Option<&'tcx Stability>,
52 export_map: &'a PublicItems,
55 impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
56 // Determine the stability for a node based on its attributes and inherited
57 // stability. The stability is recorded in the index and used as the parent.
58 fn annotate<F>(&mut self, id: NodeId, use_parent: bool,
59 attrs: &Vec<Attribute>, item_sp: Span, f: F, required: bool) where
60 F: FnOnce(&mut Annotator),
62 if self.index.staged_api[&LOCAL_CRATE] {
63 debug!("annotate(id = {:?}, attrs = {:?})", id, attrs);
64 match attr::find_stability(self.tcx.sess.diagnostic(), attrs, item_sp) {
66 debug!("annotate: found {:?}", stab);
67 // if parent is deprecated and we're not, inherit this by merging
68 // deprecated_since and its reason.
69 if let Some(parent_stab) = self.parent {
70 if parent_stab.deprecated_since.is_some()
71 && stab.deprecated_since.is_none() {
72 stab.deprecated_since = parent_stab.deprecated_since.clone();
73 stab.reason = parent_stab.reason.clone();
77 let stab = self.tcx.intern_stability(stab);
79 // Check if deprecated_since < stable_since. If it is,
80 // this is *almost surely* an accident.
81 let deprecated_predates_stable = match (stab.deprecated_since.as_ref(),
82 stab.since.as_ref()) {
83 (Some(dep_since), Some(stab_since)) => {
84 // explicit version of iter::order::lt to handle parse errors properly
85 let mut is_less = false;
86 for (dep_v, stab_v) in dep_since.split(".").zip(stab_since.split(".")) {
87 match (dep_v.parse::<u64>(), stab_v.parse::<u64>()) {
88 (Ok(dep_v), Ok(stab_v)) => match dep_v.cmp(&stab_v) {
93 Ordering::Equal => { continue; }
94 Ordering::Greater => { break; }
97 self.tcx.sess.span_err(item_sp,
98 "Invalid stability or deprecation version found");
99 // act like it isn't less because the question is now
100 // nonsensical, and this makes us not do anything else
111 if deprecated_predates_stable {
112 self.tcx.sess.span_err(item_sp,
113 "An API can't be stabilized after it is deprecated");
116 let def_id = self.tcx.map.local_def_id(id);
117 self.index.map.insert(def_id, Some(stab));
119 // Don't inherit #[stable(feature = "rust1", since = "1.0.0")]
120 if stab.level != attr::Stable {
121 let parent = replace(&mut self.parent, Some(stab));
123 self.parent = parent;
129 debug!("annotate: not found, use_parent = {:?}, parent = {:?}",
130 use_parent, self.parent);
132 if let Some(stab) = self.parent {
133 let def_id = self.tcx.map.local_def_id(id);
134 self.index.map.insert(def_id, Some(stab));
135 } else if self.index.staged_api[&LOCAL_CRATE] && required
136 && self.export_map.contains(&id)
137 && !self.tcx.sess.opts.test {
138 self.tcx.sess.span_err(item_sp,
139 "This node does not \
140 have a stability attribute");
147 // Emit warnings for non-staged-api crates. These should be errors.
149 let tag = attr.name();
150 if tag == "unstable" || tag == "stable" || tag == "deprecated" {
151 attr::mark_used(attr);
152 self.tcx.sess.span_err(attr.span(),
153 "stability attributes may not be used outside \
154 of the standard library");
162 impl<'a, 'tcx, 'v> Visitor<'v> for Annotator<'a, 'tcx> {
163 fn visit_item(&mut self, i: &Item) {
164 // FIXME (#18969): the following is a hack around the fact
165 // that we cannot currently annotate the stability of
166 // `deriving`. Basically, we do *not* allow stability
167 // inheritance on trait implementations, so that derived
168 // implementations appear to be unannotated. This then allows
169 // derived implementations to be automatically tagged with the
170 // stability of the trait. This is WRONG, but expedient to get
171 // libstd stabilized for the 1.0 release.
172 let use_parent = match i.node {
173 hir::ItemImpl(_, _, _, Some(_), _, _) => false,
177 // In case of a `pub use <mod>;`, we should not error since the stability
178 // is inherited from the module itself
179 let required = match i.node {
180 hir::ItemUse(_) => i.vis != hir::Public,
184 self.annotate(i.id, use_parent, &i.attrs, i.span,
185 |v| visit::walk_item(v, i), required);
187 if let hir::ItemStruct(ref sd, _) = i.node {
188 sd.ctor_id.map(|id| {
189 self.annotate(id, true, &i.attrs, i.span, |_| {}, true)
194 fn visit_fn(&mut self, _: FnKind<'v>, _: &'v FnDecl,
195 _: &'v Block, _: Span, _: NodeId) {
196 // Items defined in a function body have no reason to have
197 // a stability attribute, so we don't recurse.
200 fn visit_trait_item(&mut self, ti: &hir::TraitItem) {
201 self.annotate(ti.id, true, &ti.attrs, ti.span,
202 |v| visit::walk_trait_item(v, ti), true);
205 fn visit_impl_item(&mut self, ii: &hir::ImplItem) {
206 self.annotate(ii.id, true, &ii.attrs, ii.span,
207 |v| visit::walk_impl_item(v, ii), true);
210 fn visit_variant(&mut self, var: &Variant, g: &'v Generics) {
211 self.annotate(var.node.id, true, &var.node.attrs, var.span,
212 |v| visit::walk_variant(v, var, g), true)
215 fn visit_struct_field(&mut self, s: &StructField) {
216 self.annotate(s.node.id, true, &s.node.attrs, s.span,
217 |v| visit::walk_struct_field(v, s), true);
220 fn visit_foreign_item(&mut self, i: &hir::ForeignItem) {
221 self.annotate(i.id, true, &i.attrs, i.span, |_| {}, true);
225 impl<'tcx> Index<'tcx> {
226 /// Construct the stability index for a crate being compiled.
227 pub fn build(&mut self, tcx: &ty::ctxt<'tcx>, krate: &Crate, export_map: &PublicItems) {
228 let mut annotator = Annotator {
232 export_map: export_map,
234 annotator.annotate(ast::CRATE_NODE_ID, true, &krate.attrs, krate.span,
235 |v| visit::walk_crate(v, krate), true);
238 pub fn new(krate: &Crate) -> Index {
239 let mut is_staged_api = false;
240 for attr in &krate.attrs {
241 if &attr.name()[..] == "staged_api" {
242 match attr.node.value.node {
243 ast::MetaWord(_) => {
244 attr::mark_used(attr);
245 is_staged_api = true;
251 let mut staged_api = FnvHashMap();
252 staged_api.insert(LOCAL_CRATE, is_staged_api);
254 staged_api: staged_api,
260 /// Cross-references the feature names of unstable APIs with enabled
261 /// features and possibly prints errors. Returns a list of all
263 pub fn check_unstable_api_usage(tcx: &ty::ctxt)
264 -> FnvHashMap<InternedString, attr::StabilityLevel> {
265 let ref active_lib_features = tcx.sess.features.borrow().declared_lib_features;
267 // Put the active features into a map for quick lookup
268 let active_features = active_lib_features.iter().map(|&(ref s, _)| s.clone()).collect();
270 let mut checker = Checker {
272 active_features: active_features,
273 used_features: FnvHashMap()
276 let krate = tcx.map.krate();
277 visit::walk_crate(&mut checker, krate);
279 let used_features = checker.used_features;
280 return used_features;
283 struct Checker<'a, 'tcx: 'a> {
284 tcx: &'a ty::ctxt<'tcx>,
285 active_features: FnvHashSet<InternedString>,
286 used_features: FnvHashMap<InternedString, attr::StabilityLevel>
289 impl<'a, 'tcx> Checker<'a, 'tcx> {
290 fn check(&mut self, id: DefId, span: Span, stab: &Option<&Stability>) {
291 // Only the cross-crate scenario matters when checking unstable APIs
292 let cross_crate = !id.is_local();
293 if !cross_crate { return }
296 Some(&Stability { level: attr::Unstable, ref feature, ref reason, issue, .. }) => {
297 self.used_features.insert(feature.clone(), attr::Unstable);
299 if !self.active_features.contains(feature) {
300 let msg = match *reason {
301 Some(ref r) => format!("use of unstable library feature '{}': {}",
303 None => format!("use of unstable library feature '{}'", &feature)
306 emit_feature_err(&self.tcx.sess.parse_sess.span_diagnostic,
307 &feature, span, GateIssue::Library(issue), &msg);
310 Some(&Stability { level, ref feature, .. }) => {
311 self.used_features.insert(feature.clone(), level);
313 // Stable APIs are always ok to call and deprecated APIs are
314 // handled by a lint.
317 // This is an 'unmarked' API, which should not exist
318 // in the standard library.
319 if self.tcx.sess.features.borrow().unmarked_api {
320 self.tcx.sess.span_warn(span, "use of unmarked library feature");
321 self.tcx.sess.span_note(span, "this is either a bug in the library you are \
322 using or a bug in the compiler - please \
323 report it in both places");
325 self.tcx.sess.span_err(span, "use of unmarked library feature");
326 self.tcx.sess.span_note(span, "this is either a bug in the library you are \
327 using or a bug in the compiler - please \
328 report it in both places");
329 self.tcx.sess.span_note(span, "use #![feature(unmarked_api)] in the \
330 crate attributes to override this");
337 impl<'a, 'v, 'tcx> Visitor<'v> for Checker<'a, 'tcx> {
338 fn visit_item(&mut self, item: &hir::Item) {
339 // When compiling with --test we don't enforce stability on the
340 // compiler-generated test module, demarcated with `DUMMY_SP` plus the
342 if item.span == DUMMY_SP && item.name.as_str() == "__test" { return }
344 check_item(self.tcx, item, true,
345 &mut |id, sp, stab| self.check(id, sp, stab));
346 visit::walk_item(self, item);
349 fn visit_expr(&mut self, ex: &hir::Expr) {
350 check_expr(self.tcx, ex,
351 &mut |id, sp, stab| self.check(id, sp, stab));
352 visit::walk_expr(self, ex);
355 fn visit_path(&mut self, path: &hir::Path, id: ast::NodeId) {
356 check_path(self.tcx, path, id,
357 &mut |id, sp, stab| self.check(id, sp, stab));
358 visit::walk_path(self, path)
361 fn visit_path_list_item(&mut self, prefix: &hir::Path, item: &hir::PathListItem) {
362 check_path_list_item(self.tcx, item,
363 &mut |id, sp, stab| self.check(id, sp, stab));
364 visit::walk_path_list_item(self, prefix, item)
367 fn visit_pat(&mut self, pat: &hir::Pat) {
368 check_pat(self.tcx, pat,
369 &mut |id, sp, stab| self.check(id, sp, stab));
370 visit::walk_pat(self, pat)
374 /// Helper for discovering nodes to check for stability
375 pub fn check_item(tcx: &ty::ctxt, item: &hir::Item, warn_about_defns: bool,
376 cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
378 hir::ItemExternCrate(_) => {
379 // compiler-generated `extern crate` items have a dummy span.
380 if item.span == DUMMY_SP { return }
382 let cnum = match tcx.sess.cstore.find_extern_mod_stmt_cnum(item.id) {
386 let id = DefId { krate: cnum, index: CRATE_DEF_INDEX };
387 maybe_do_stability_check(tcx, id, item.span, cb);
390 // For implementations of traits, check the stability of each item
391 // individually as it's possible to have a stable trait with unstable
393 hir::ItemImpl(_, _, _, Some(ref t), _, ref impl_items) => {
394 let trait_did = tcx.def_map.borrow().get(&t.ref_id).unwrap().def_id();
395 let trait_items = tcx.trait_items(trait_did);
397 for impl_item in impl_items {
398 let item = trait_items.iter().find(|item| {
399 item.name() == impl_item.name
401 if warn_about_defns {
402 maybe_do_stability_check(tcx, item.def_id(), impl_item.span, cb);
411 /// Helper for discovering nodes to check for stability
412 pub fn check_expr(tcx: &ty::ctxt, e: &hir::Expr,
413 cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
415 let id = match e.node {
416 hir::ExprMethodCall(i, _, _) => {
418 let method_call = ty::MethodCall::expr(e.id);
419 tcx.tables.borrow().method_map[&method_call].def_id
421 hir::ExprField(ref base_e, ref field) => {
423 match tcx.expr_ty_adjusted(base_e).sty {
424 ty::TyStruct(def, _) => def.struct_variant().field_named(field.node).did,
425 _ => tcx.sess.span_bug(e.span,
426 "stability::check_expr: named field access on non-struct")
429 hir::ExprTupField(ref base_e, ref field) => {
431 match tcx.expr_ty_adjusted(base_e).sty {
432 ty::TyStruct(def, _) => def.struct_variant().fields[field.node].did,
433 ty::TyTuple(..) => return,
434 _ => tcx.sess.span_bug(e.span,
435 "stability::check_expr: unnamed field access on \
436 something other than a tuple or struct")
439 hir::ExprStruct(_, ref expr_fields, _) => {
440 let type_ = tcx.expr_ty(e);
442 ty::TyStruct(def, _) => {
443 // check the stability of each field that appears
444 // in the construction expression.
445 for field in expr_fields {
446 let did = def.struct_variant()
447 .field_named(field.name.node)
449 maybe_do_stability_check(tcx, did, field.span, cb);
455 // we don't look at stability attributes on
456 // struct-like enums (yet...), but it's definitely not
457 // a bug to have construct one.
458 ty::TyEnum(..) => return,
460 tcx.sess.span_bug(e.span,
461 &format!("stability::check_expr: struct construction \
462 of non-struct, type {:?}",
470 maybe_do_stability_check(tcx, id, span, cb);
473 pub fn check_path(tcx: &ty::ctxt, path: &hir::Path, id: ast::NodeId,
474 cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
475 match tcx.def_map.borrow().get(&id).map(|d| d.full_def()) {
476 Some(def::DefPrimTy(..)) => {}
477 Some(def::DefSelfTy(..)) => {}
479 maybe_do_stability_check(tcx, def.def_id(), path.span, cb);
485 pub fn check_path_list_item(tcx: &ty::ctxt, item: &hir::PathListItem,
486 cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
487 match tcx.def_map.borrow().get(&item.node.id()).map(|d| d.full_def()) {
488 Some(def::DefPrimTy(..)) => {}
490 maybe_do_stability_check(tcx, def.def_id(), item.span, cb);
496 pub fn check_pat(tcx: &ty::ctxt, pat: &hir::Pat,
497 cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
498 debug!("check_pat(pat = {:?})", pat);
499 if is_internal(tcx, pat.span) { return; }
501 let v = match tcx.pat_ty_opt(pat) {
502 Some(&ty::TyS { sty: ty::TyStruct(def, _), .. }) => def.struct_variant(),
503 Some(_) | None => return,
507 hir::PatEnum(_, Some(ref pat_fields)) => {
508 for (field, struct_field) in pat_fields.iter().zip(&v.fields) {
509 // a .. pattern is fine, but anything positional is
511 if let hir::PatWild(hir::PatWildMulti) = field.node {
514 maybe_do_stability_check(tcx, struct_field.did, field.span, cb)
518 hir::PatStruct(_, ref pat_fields, _) => {
519 for field in pat_fields {
520 let did = v.field_named(field.node.name).did;
521 maybe_do_stability_check(tcx, did, field.span, cb);
524 // everything else is fine.
529 fn maybe_do_stability_check(tcx: &ty::ctxt, id: DefId, span: Span,
530 cb: &mut FnMut(DefId, Span, &Option<&Stability>)) {
531 if !is_staged_api(tcx, id) {
532 debug!("maybe_do_stability_check: \
533 skipping id={:?} since it is not staged_api", id);
536 if is_internal(tcx, span) {
537 debug!("maybe_do_stability_check: \
538 skipping span={:?} since it is internal", span);
541 let ref stability = lookup(tcx, id);
542 debug!("maybe_do_stability_check: \
543 inspecting id={:?} span={:?} of stability={:?}", id, span, stability);
544 cb(id, span, stability);
547 fn is_internal(tcx: &ty::ctxt, span: Span) -> bool {
548 tcx.sess.codemap().span_allows_unstable(span)
551 fn is_staged_api(tcx: &ty::ctxt, id: DefId) -> bool {
552 match tcx.trait_item_of_item(id) {
553 Some(ty::MethodTraitItemId(trait_method_id))
554 if trait_method_id != id => {
555 is_staged_api(tcx, trait_method_id)
558 *tcx.stability.borrow_mut().staged_api.entry(id.krate).or_insert_with(
559 || csearch::is_staged_api(&tcx.sess.cstore, id.krate))
564 /// Lookup the stability for a node, loading external crate
565 /// metadata as necessary.
566 pub fn lookup<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> {
567 if let Some(st) = tcx.stability.borrow().map.get(&id) {
571 let st = lookup_uncached(tcx, id);
572 tcx.stability.borrow_mut().map.insert(id, st);
576 fn lookup_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> {
577 debug!("lookup(id={:?})", id);
579 // is this definition the implementation of a trait method?
580 match tcx.trait_item_of_item(id) {
581 Some(ty::MethodTraitItemId(trait_method_id)) if trait_method_id != id => {
582 debug!("lookup: trait_method_id={:?}", trait_method_id);
583 return lookup(tcx, trait_method_id)
588 let item_stab = if id.is_local() {
589 None // The stability cache is filled partially lazily
591 csearch::get_stability(&tcx.sess.cstore, id).map(|st| tcx.intern_stability(st))
594 item_stab.or_else(|| {
596 if let Some(trait_id) = tcx.trait_id_of_impl(id) {
597 // FIXME (#18969): for the time being, simply use the
598 // stability of the trait to determine the stability of any
599 // unmarked impls for it. See FIXME above for more details.
601 debug!("lookup: trait_id={:?}", trait_id);
602 return lookup(tcx, trait_id);
609 /// Given the list of enabled features that were not language features (i.e. that
610 /// were expected to be library features), and the list of features used from
611 /// libraries, identify activated features that don't exist and error about them.
612 pub fn check_unused_or_stable_features(sess: &Session,
613 lib_features_used: &FnvHashMap<InternedString,
614 attr::StabilityLevel>) {
615 let ref declared_lib_features = sess.features.borrow().declared_lib_features;
616 let mut remaining_lib_features: FnvHashMap<InternedString, Span>
617 = declared_lib_features.clone().into_iter().collect();
619 let stable_msg = "this feature is stable. attribute no longer needed";
621 for &span in &sess.features.borrow().declared_stable_lang_features {
622 sess.add_lint(lint::builtin::STABLE_FEATURES,
625 stable_msg.to_string());
628 for (used_lib_feature, level) in lib_features_used {
629 match remaining_lib_features.remove(used_lib_feature) {
631 if *level == attr::Stable {
632 sess.add_lint(lint::builtin::STABLE_FEATURES,
635 stable_msg.to_string());
638 None => ( /* used but undeclared, handled during the previous ast visit */ )
642 for &span in remaining_lib_features.values() {
643 sess.add_lint(lint::builtin::UNUSED_FEATURES,
646 "unused or unknown feature".to_string());