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.
17 use middle
::def_id
::{DefId, LOCAL_CRATE}
;
19 use middle
::privacy
::PublicItems
;
20 use metadata
::csearch
;
21 use syntax
::parse
::token
::InternedString
;
22 use syntax
::codemap
::{Span, DUMMY_SP}
;
24 use syntax
::ast
::NodeId
;
25 use syntax
::feature_gate
::{GateIssue, emit_feature_err}
;
26 use util
::nodemap
::{DefIdMap, FnvHashSet, FnvHashMap}
;
29 use rustc_front
::hir
::{FnDecl, Attribute, Block, Crate, Item, Generics, StructField, Variant}
;
30 use rustc_front
::attr
::{self, Stability, AttrMetaMethods}
;
31 use rustc_front
::visit
::{self, FnKind, Visitor}
;
33 use std
::mem
::replace
;
34 use std
::cmp
::Ordering
;
36 /// A stability index, giving the stability level for items and methods.
37 pub struct Index
<'tcx
> {
38 /// This is mostly a cache, except the stabilities of local items
39 /// are filled by the annotator.
40 map
: DefIdMap
<Option
<&'tcx Stability
>>,
42 /// Maps for each crate whether it is part of the staged API.
43 staged_api
: FnvHashMap
<ast
::CrateNum
, bool
>
46 // A private tree-walker for producing an Index.
47 struct Annotator
<'a
, 'tcx
: 'a
> {
48 tcx
: &'a ty
::ctxt
<'tcx
>,
49 index
: &'a
mut Index
<'tcx
>,
50 parent
: Option
<&'tcx Stability
>,
51 export_map
: &'a PublicItems
,
54 impl<'a
, 'tcx
: 'a
> Annotator
<'a
, 'tcx
> {
55 // Determine the stability for a node based on its attributes and inherited
56 // stability. The stability is recorded in the index and used as the parent.
57 fn annotate
<F
>(&mut self, id
: NodeId
, use_parent
: bool
,
58 attrs
: &Vec
<Attribute
>, item_sp
: Span
, f
: F
, required
: bool
) where
59 F
: FnOnce(&mut Annotator
),
61 if self.index
.staged_api
[&LOCAL_CRATE
] {
62 debug
!("annotate(id = {:?}, attrs = {:?})", id
, attrs
);
63 match attr
::find_stability(self.tcx
.sess
.diagnostic(), attrs
, item_sp
) {
65 debug
!("annotate: found {:?}", stab
);
66 // if parent is deprecated and we're not, inherit this by merging
67 // deprecated_since and its reason.
68 if let Some(parent_stab
) = self.parent
{
69 if parent_stab
.deprecated_since
.is_some()
70 && stab
.deprecated_since
.is_none() {
71 stab
.deprecated_since
= parent_stab
.deprecated_since
.clone();
72 stab
.reason
= parent_stab
.reason
.clone();
76 let stab
= self.tcx
.intern_stability(stab
);
78 // Check if deprecated_since < stable_since. If it is,
79 // this is *almost surely* an accident.
80 let deprecated_predates_stable
= match (stab
.deprecated_since
.as_ref(),
81 stab
.since
.as_ref()) {
82 (Some(dep_since
), Some(stab_since
)) => {
83 // explicit version of iter::order::lt to handle parse errors properly
84 let mut is_less
= false;
85 for (dep_v
, stab_v
) in dep_since
.split(".").zip(stab_since
.split(".")) {
86 match (dep_v
.parse
::<u64>(), stab_v
.parse
::<u64>()) {
87 (Ok(dep_v
), Ok(stab_v
)) => match dep_v
.cmp(&stab_v
) {
92 Ordering
::Equal
=> { continue; }
93 Ordering
::Greater
=> { break; }
96 self.tcx
.sess
.span_err(item_sp
,
97 "Invalid stability or deprecation version found");
98 // act like it isn't less because the question is now
99 // nonsensical, and this makes us not do anything else
110 if deprecated_predates_stable
{
111 self.tcx
.sess
.span_err(item_sp
,
112 "An API can't be stabilized after it is deprecated");
115 self.index
.map
.insert(DefId
::local(id
), Some(stab
));
117 // Don't inherit #[stable(feature = "rust1", since = "1.0.0")]
118 if stab
.level
!= attr
::Stable
{
119 let parent
= replace(&mut self.parent
, Some(stab
));
121 self.parent
= parent
;
127 debug
!("annotate: not found, use_parent = {:?}, parent = {:?}",
128 use_parent
, self.parent
);
130 if let Some(stab
) = self.parent
{
131 self.index
.map
.insert(DefId
::local(id
), Some(stab
));
132 } else if self.index
.staged_api
[&LOCAL_CRATE
] && required
133 && self.export_map
.contains(&id
)
134 && !self.tcx
.sess
.opts
.test
{
135 self.tcx
.sess
.span_err(item_sp
,
136 "This node does not \
137 have a stability attribute");
144 // Emit warnings for non-staged-api crates. These should be errors.
146 let tag
= attr
.name();
147 if tag
== "unstable" || tag
== "stable" || tag
== "deprecated" {
148 attr
::mark_used(attr
);
149 self.tcx
.sess
.span_err(attr
.span(),
150 "stability attributes may not be used outside \
151 of the standard library");
159 impl<'a
, 'tcx
, 'v
> Visitor
<'v
> for Annotator
<'a
, 'tcx
> {
160 fn visit_item(&mut self, i
: &Item
) {
161 // FIXME (#18969): the following is a hack around the fact
162 // that we cannot currently annotate the stability of
163 // `deriving`. Basically, we do *not* allow stability
164 // inheritance on trait implementations, so that derived
165 // implementations appear to be unannotated. This then allows
166 // derived implementations to be automatically tagged with the
167 // stability of the trait. This is WRONG, but expedient to get
168 // libstd stabilized for the 1.0 release.
169 let use_parent
= match i
.node
{
170 hir
::ItemImpl(_
, _
, _
, Some(_
), _
, _
) => false,
174 // In case of a `pub use <mod>;`, we should not error since the stability
175 // is inherited from the module itself
176 let required
= match i
.node
{
177 hir
::ItemUse(_
) => i
.vis
!= hir
::Public
,
181 self.annotate(i
.id
, use_parent
, &i
.attrs
, i
.span
,
182 |v
| visit
::walk_item(v
, i
), required
);
184 if let hir
::ItemStruct(ref sd
, _
) = i
.node
{
185 sd
.ctor_id
.map(|id
| {
186 self.annotate(id
, true, &i
.attrs
, i
.span
, |_
| {}
, true)
191 fn visit_fn(&mut self, _
: FnKind
<'v
>, _
: &'v FnDecl
,
192 _
: &'v Block
, _
: Span
, _
: NodeId
) {
193 // Items defined in a function body have no reason to have
194 // a stability attribute, so we don't recurse.
197 fn visit_trait_item(&mut self, ti
: &hir
::TraitItem
) {
198 self.annotate(ti
.id
, true, &ti
.attrs
, ti
.span
,
199 |v
| visit
::walk_trait_item(v
, ti
), true);
202 fn visit_impl_item(&mut self, ii
: &hir
::ImplItem
) {
203 self.annotate(ii
.id
, true, &ii
.attrs
, ii
.span
,
204 |v
| visit
::walk_impl_item(v
, ii
), true);
207 fn visit_variant(&mut self, var
: &Variant
, g
: &'v Generics
) {
208 self.annotate(var
.node
.id
, true, &var
.node
.attrs
, var
.span
,
209 |v
| visit
::walk_variant(v
, var
, g
), true)
212 fn visit_struct_field(&mut self, s
: &StructField
) {
213 self.annotate(s
.node
.id
, true, &s
.node
.attrs
, s
.span
,
214 |v
| visit
::walk_struct_field(v
, s
), true);
217 fn visit_foreign_item(&mut self, i
: &hir
::ForeignItem
) {
218 self.annotate(i
.id
, true, &i
.attrs
, i
.span
, |_
| {}
, true);
222 impl<'tcx
> Index
<'tcx
> {
223 /// Construct the stability index for a crate being compiled.
224 pub fn build(&mut self, tcx
: &ty
::ctxt
<'tcx
>, krate
: &Crate
, export_map
: &PublicItems
) {
225 let mut annotator
= Annotator
{
229 export_map
: export_map
,
231 annotator
.annotate(ast
::CRATE_NODE_ID
, true, &krate
.attrs
, krate
.span
,
232 |v
| visit
::walk_crate(v
, krate
), true);
235 pub fn new(krate
: &Crate
) -> Index
{
236 let mut is_staged_api
= false;
237 for attr
in &krate
.attrs
{
238 if &attr
.name()[..] == "staged_api" {
239 match attr
.node
.value
.node
{
240 hir
::MetaWord(_
) => {
241 attr
::mark_used(attr
);
242 is_staged_api
= true;
248 let mut staged_api
= FnvHashMap();
249 staged_api
.insert(LOCAL_CRATE
, is_staged_api
);
251 staged_api
: staged_api
,
257 /// Cross-references the feature names of unstable APIs with enabled
258 /// features and possibly prints errors. Returns a list of all
260 pub fn check_unstable_api_usage(tcx
: &ty
::ctxt
)
261 -> FnvHashMap
<InternedString
, attr
::StabilityLevel
> {
262 let ref active_lib_features
= tcx
.sess
.features
.borrow().declared_lib_features
;
264 // Put the active features into a map for quick lookup
265 let active_features
= active_lib_features
.iter().map(|&(ref s
, _
)| s
.clone()).collect();
267 let mut checker
= Checker
{
269 active_features
: active_features
,
270 used_features
: FnvHashMap()
273 let krate
= tcx
.map
.krate();
274 visit
::walk_crate(&mut checker
, krate
);
276 let used_features
= checker
.used_features
;
277 return used_features
;
280 struct Checker
<'a
, 'tcx
: 'a
> {
281 tcx
: &'a ty
::ctxt
<'tcx
>,
282 active_features
: FnvHashSet
<InternedString
>,
283 used_features
: FnvHashMap
<InternedString
, attr
::StabilityLevel
>
286 impl<'a
, 'tcx
> Checker
<'a
, 'tcx
> {
287 fn check(&mut self, id
: DefId
, span
: Span
, stab
: &Option
<&Stability
>) {
288 // Only the cross-crate scenario matters when checking unstable APIs
289 let cross_crate
= !id
.is_local();
290 if !cross_crate { return }
293 Some(&Stability { level: attr::Unstable, ref feature, ref reason, issue, .. }
) => {
294 self.used_features
.insert(feature
.clone(), attr
::Unstable
);
296 if !self.active_features
.contains(feature
) {
297 let msg
= match *reason
{
298 Some(ref r
) => format
!("use of unstable library feature '{}': {}",
300 None
=> format
!("use of unstable library feature '{}'", &feature
)
303 emit_feature_err(&self.tcx
.sess
.parse_sess
.span_diagnostic
,
304 &feature
, span
, GateIssue
::Library(issue
), &msg
);
307 Some(&Stability { level, ref feature, .. }
) => {
308 self.used_features
.insert(feature
.clone(), level
);
310 // Stable APIs are always ok to call and deprecated APIs are
311 // handled by a lint.
314 // This is an 'unmarked' API, which should not exist
315 // in the standard library.
316 if self.tcx
.sess
.features
.borrow().unmarked_api
{
317 self.tcx
.sess
.span_warn(span
, "use of unmarked library feature");
318 self.tcx
.sess
.span_note(span
, "this is either a bug in the library you are \
319 using or a bug in the compiler - please \
320 report it in both places");
322 self.tcx
.sess
.span_err(span
, "use of unmarked library feature");
323 self.tcx
.sess
.span_note(span
, "this is either a bug in the library you are \
324 using or a bug in the compiler - please \
325 report it in both places");
326 self.tcx
.sess
.span_note(span
, "use #![feature(unmarked_api)] in the \
327 crate attributes to override this");
334 impl<'a
, 'v
, 'tcx
> Visitor
<'v
> for Checker
<'a
, 'tcx
> {
335 fn visit_item(&mut self, item
: &hir
::Item
) {
336 // When compiling with --test we don't enforce stability on the
337 // compiler-generated test module, demarcated with `DUMMY_SP` plus the
339 if item
.span
== DUMMY_SP
&& item
.ident
.name
== "__test" { return }
341 check_item(self.tcx
, item
, true,
342 &mut |id
, sp
, stab
| self.check(id
, sp
, stab
));
343 visit
::walk_item(self, item
);
346 fn visit_expr(&mut self, ex
: &hir
::Expr
) {
347 check_expr(self.tcx
, ex
,
348 &mut |id
, sp
, stab
| self.check(id
, sp
, stab
));
349 visit
::walk_expr(self, ex
);
352 fn visit_path(&mut self, path
: &hir
::Path
, id
: ast
::NodeId
) {
353 check_path(self.tcx
, path
, id
,
354 &mut |id
, sp
, stab
| self.check(id
, sp
, stab
));
355 visit
::walk_path(self, path
)
358 fn visit_pat(&mut self, pat
: &hir
::Pat
) {
359 check_pat(self.tcx
, pat
,
360 &mut |id
, sp
, stab
| self.check(id
, sp
, stab
));
361 visit
::walk_pat(self, pat
)
365 /// Helper for discovering nodes to check for stability
366 pub fn check_item(tcx
: &ty
::ctxt
, item
: &hir
::Item
, warn_about_defns
: bool
,
367 cb
: &mut FnMut(DefId
, Span
, &Option
<&Stability
>)) {
369 hir
::ItemExternCrate(_
) => {
370 // compiler-generated `extern crate` items have a dummy span.
371 if item
.span
== DUMMY_SP { return }
373 let cnum
= match tcx
.sess
.cstore
.find_extern_mod_stmt_cnum(item
.id
) {
377 let id
= DefId { krate: cnum, node: ast::CRATE_NODE_ID }
;
378 maybe_do_stability_check(tcx
, id
, item
.span
, cb
);
381 // For implementations of traits, check the stability of each item
382 // individually as it's possible to have a stable trait with unstable
384 hir
::ItemImpl(_
, _
, _
, Some(ref t
), _
, ref impl_items
) => {
385 let trait_did
= tcx
.def_map
.borrow().get(&t
.ref_id
).unwrap().def_id();
386 let trait_items
= tcx
.trait_items(trait_did
);
388 for impl_item
in impl_items
{
389 let item
= trait_items
.iter().find(|item
| {
390 item
.name() == impl_item
.ident
.name
392 if warn_about_defns
{
393 maybe_do_stability_check(tcx
, item
.def_id(), impl_item
.span
, cb
);
402 /// Helper for discovering nodes to check for stability
403 pub fn check_expr(tcx
: &ty
::ctxt
, e
: &hir
::Expr
,
404 cb
: &mut FnMut(DefId
, Span
, &Option
<&Stability
>)) {
406 let id
= match e
.node
{
407 hir
::ExprMethodCall(i
, _
, _
) => {
409 let method_call
= ty
::MethodCall
::expr(e
.id
);
410 tcx
.tables
.borrow().method_map
[&method_call
].def_id
412 hir
::ExprField(ref base_e
, ref field
) => {
414 match tcx
.expr_ty_adjusted(base_e
).sty
{
415 ty
::TyStruct(def
, _
) => def
.struct_variant().field_named(field
.node
.name
).did
,
416 _
=> tcx
.sess
.span_bug(e
.span
,
417 "stability::check_expr: named field access on non-struct")
420 hir
::ExprTupField(ref base_e
, ref field
) => {
422 match tcx
.expr_ty_adjusted(base_e
).sty
{
423 ty
::TyStruct(def
, _
) => def
.struct_variant().fields
[field
.node
].did
,
424 ty
::TyTuple(..) => return,
425 _
=> tcx
.sess
.span_bug(e
.span
,
426 "stability::check_expr: unnamed field access on \
427 something other than a tuple or struct")
430 hir
::ExprStruct(_
, ref expr_fields
, _
) => {
431 let type_
= tcx
.expr_ty(e
);
433 ty
::TyStruct(def
, _
) => {
434 // check the stability of each field that appears
435 // in the construction expression.
436 for field
in expr_fields
{
437 let did
= def
.struct_variant()
438 .field_named(field
.ident
.node
.name
)
440 maybe_do_stability_check(tcx
, did
, field
.span
, cb
);
446 // we don't look at stability attributes on
447 // struct-like enums (yet...), but it's definitely not
448 // a bug to have construct one.
449 ty
::TyEnum(..) => return,
451 tcx
.sess
.span_bug(e
.span
,
452 &format
!("stability::check_expr: struct construction \
453 of non-struct, type {:?}",
461 maybe_do_stability_check(tcx
, id
, span
, cb
);
464 pub fn check_path(tcx
: &ty
::ctxt
, path
: &hir
::Path
, id
: ast
::NodeId
,
465 cb
: &mut FnMut(DefId
, Span
, &Option
<&Stability
>)) {
466 match tcx
.def_map
.borrow().get(&id
).map(|d
| d
.full_def()) {
467 Some(def
::DefPrimTy(..)) => {}
469 maybe_do_stability_check(tcx
, def
.def_id(), path
.span
, cb
);
476 pub fn check_pat(tcx
: &ty
::ctxt
, pat
: &hir
::Pat
,
477 cb
: &mut FnMut(DefId
, Span
, &Option
<&Stability
>)) {
478 debug
!("check_pat(pat = {:?})", pat
);
479 if is_internal(tcx
, pat
.span
) { return; }
481 let v
= match tcx
.pat_ty_opt(pat
) {
482 Some(&ty
::TyS { sty: ty::TyStruct(def, _), .. }
) => def
.struct_variant(),
483 Some(_
) | None
=> return,
487 hir
::PatEnum(_
, Some(ref pat_fields
)) => {
488 for (field
, struct_field
) in pat_fields
.iter().zip(&v
.fields
) {
489 // a .. pattern is fine, but anything positional is
491 if let hir
::PatWild(hir
::PatWildMulti
) = field
.node
{
494 maybe_do_stability_check(tcx
, struct_field
.did
, field
.span
, cb
)
498 hir
::PatStruct(_
, ref pat_fields
, _
) => {
499 for field
in pat_fields
{
500 let did
= v
.field_named(field
.node
.ident
.name
).did
;
501 maybe_do_stability_check(tcx
, did
, field
.span
, cb
);
504 // everything else is fine.
509 fn maybe_do_stability_check(tcx
: &ty
::ctxt
, id
: DefId
, span
: Span
,
510 cb
: &mut FnMut(DefId
, Span
, &Option
<&Stability
>)) {
511 if !is_staged_api(tcx
, id
) {
512 debug
!("maybe_do_stability_check: \
513 skipping id={:?} since it is not staged_api", id
);
516 if is_internal(tcx
, span
) {
517 debug
!("maybe_do_stability_check: \
518 skipping span={:?} since it is internal", span
);
521 let ref stability
= lookup(tcx
, id
);
522 debug
!("maybe_do_stability_check: \
523 inspecting id={:?} span={:?} of stability={:?}", id
, span
, stability
);
524 cb(id
, span
, stability
);
527 fn is_internal(tcx
: &ty
::ctxt
, span
: Span
) -> bool
{
528 tcx
.sess
.codemap().span_allows_unstable(span
)
531 fn is_staged_api(tcx
: &ty
::ctxt
, id
: DefId
) -> bool
{
532 match tcx
.trait_item_of_item(id
) {
533 Some(ty
::MethodTraitItemId(trait_method_id
))
534 if trait_method_id
!= id
=> {
535 is_staged_api(tcx
, trait_method_id
)
538 *tcx
.stability
.borrow_mut().staged_api
.entry(id
.krate
).or_insert_with(
539 || csearch
::is_staged_api(&tcx
.sess
.cstore
, id
.krate
))
544 /// Lookup the stability for a node, loading external crate
545 /// metadata as necessary.
546 pub fn lookup
<'tcx
>(tcx
: &ty
::ctxt
<'tcx
>, id
: DefId
) -> Option
<&'tcx Stability
> {
547 if let Some(st
) = tcx
.stability
.borrow().map
.get(&id
) {
551 let st
= lookup_uncached(tcx
, id
);
552 tcx
.stability
.borrow_mut().map
.insert(id
, st
);
556 fn lookup_uncached
<'tcx
>(tcx
: &ty
::ctxt
<'tcx
>, id
: DefId
) -> Option
<&'tcx Stability
> {
557 debug
!("lookup(id={:?})", id
);
559 // is this definition the implementation of a trait method?
560 match tcx
.trait_item_of_item(id
) {
561 Some(ty
::MethodTraitItemId(trait_method_id
)) if trait_method_id
!= id
=> {
562 debug
!("lookup: trait_method_id={:?}", trait_method_id
);
563 return lookup(tcx
, trait_method_id
)
568 let item_stab
= if id
.is_local() {
569 None
// The stability cache is filled partially lazily
571 csearch
::get_stability(&tcx
.sess
.cstore
, id
).map(|st
| tcx
.intern_stability(st
))
574 item_stab
.or_else(|| {
576 if let Some(trait_id
) = tcx
.trait_id_of_impl(id
) {
577 // FIXME (#18969): for the time being, simply use the
578 // stability of the trait to determine the stability of any
579 // unmarked impls for it. See FIXME above for more details.
581 debug
!("lookup: trait_id={:?}", trait_id
);
582 return lookup(tcx
, trait_id
);
589 /// Given the list of enabled features that were not language features (i.e. that
590 /// were expected to be library features), and the list of features used from
591 /// libraries, identify activated features that don't exist and error about them.
592 pub fn check_unused_or_stable_features(sess
: &Session
,
593 lib_features_used
: &FnvHashMap
<InternedString
,
594 attr
::StabilityLevel
>) {
595 let ref declared_lib_features
= sess
.features
.borrow().declared_lib_features
;
596 let mut remaining_lib_features
: FnvHashMap
<InternedString
, Span
>
597 = declared_lib_features
.clone().into_iter().collect();
599 let stable_msg
= "this feature is stable. attribute no longer needed";
601 for &span
in &sess
.features
.borrow().declared_stable_lang_features
{
602 sess
.add_lint(lint
::builtin
::STABLE_FEATURES
,
605 stable_msg
.to_string());
608 for (used_lib_feature
, level
) in lib_features_used
{
609 match remaining_lib_features
.remove(used_lib_feature
) {
611 if *level
== attr
::Stable
{
612 sess
.add_lint(lint
::builtin
::STABLE_FEATURES
,
615 stable_msg
.to_string());
618 None
=> ( /* used but undeclared, handled during the previous ast visit */ )
622 for &span
in remaining_lib_features
.values() {
623 sess
.add_lint(lint
::builtin
::UNUSED_FEATURES
,
626 "unused or unknown feature".to_string());