]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/stability.rs
Use ast attributes every where (remove HIR attributes).
[rust.git] / src / librustc / middle / stability.rs
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.
4 //
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.
10
11 //! A pass that annotates every item and method with its stability level,
12 //! propagating default levels lexically from parent to children ast nodes.
13
14 use session::Session;
15 use lint;
16 use middle::def;
17 use middle::def_id::{DefId, LOCAL_CRATE};
18 use middle::ty;
19 use middle::privacy::PublicItems;
20 use metadata::csearch;
21 use syntax::parse::token::InternedString;
22 use syntax::codemap::{Span, DUMMY_SP};
23 use syntax::ast;
24 use syntax::ast::{NodeId, Attribute};
25 use syntax::feature_gate::{GateIssue, emit_feature_err};
26 use syntax::attr::{self, Stability, AttrMetaMethods};
27 use util::nodemap::{DefIdMap, FnvHashSet, FnvHashMap};
28
29 use rustc_front::hir;
30 use rustc_front::hir::{FnDecl, Block, Crate, Item, Generics, StructField, Variant};
31 use rustc_front::visit::{self, FnKind, Visitor};
32
33 use std::mem::replace;
34 use std::cmp::Ordering;
35
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>>,
41
42     /// Maps for each crate whether it is part of the staged API.
43     staged_api: FnvHashMap<ast::CrateNum, bool>
44 }
45
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,
52 }
53
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),
60     {
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) {
64                 Some(mut stab) => {
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();
73                         }
74                     }
75
76                     let stab = self.tcx.intern_stability(stab);
77
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) {
88                                         Ordering::Less => {
89                                             is_less = true;
90                                             break;
91                                         }
92                                         Ordering::Equal => { continue; }
93                                         Ordering::Greater => { break; }
94                                     },
95                                     _ => {
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
100                                         // interesting.
101                                         break;
102                                     }
103                                 }
104                             }
105                             is_less
106                         },
107                         _ => false,
108                     };
109
110                     if deprecated_predates_stable {
111                         self.tcx.sess.span_err(item_sp,
112                             "An API can't be stabilized after it is deprecated");
113                     }
114
115                     self.index.map.insert(DefId::local(id), Some(stab));
116
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));
120                         f(self);
121                         self.parent = parent;
122                     } else {
123                         f(self);
124                     }
125                 }
126                 None => {
127                     debug!("annotate: not found, use_parent = {:?}, parent = {:?}",
128                            use_parent, self.parent);
129                     if use_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");
138                             }
139                     }
140                     f(self);
141                 }
142             }
143         } else {
144             // Emit warnings for non-staged-api crates. These should be errors.
145             for attr in attrs {
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");
152                 }
153             }
154             f(self);
155         }
156     }
157 }
158
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,
171             _ => true,
172         };
173
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,
178             _ => true
179         };
180
181         self.annotate(i.id, use_parent, &i.attrs, i.span,
182                       |v| visit::walk_item(v, i), required);
183
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)
187             });
188         }
189     }
190
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.
195     }
196
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);
200     }
201
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);
205     }
206
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)
210     }
211
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);
215     }
216
217     fn visit_foreign_item(&mut self, i: &hir::ForeignItem) {
218         self.annotate(i.id, true, &i.attrs, i.span, |_| {}, true);
219     }
220 }
221
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 {
226             tcx: tcx,
227             index: self,
228             parent: None,
229             export_map: export_map,
230         };
231         annotator.annotate(ast::CRATE_NODE_ID, true, &krate.attrs, krate.span,
232                            |v| visit::walk_crate(v, krate), true);
233     }
234
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                     ast::MetaWord(_) => {
241                         attr::mark_used(attr);
242                         is_staged_api = true;
243                     }
244                     _ => (/*pass*/)
245                 }
246             }
247         }
248         let mut staged_api = FnvHashMap();
249         staged_api.insert(LOCAL_CRATE, is_staged_api);
250         Index {
251             staged_api: staged_api,
252             map: DefIdMap(),
253         }
254     }
255 }
256
257 /// Cross-references the feature names of unstable APIs with enabled
258 /// features and possibly prints errors. Returns a list of all
259 /// features used.
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;
263
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();
266
267     let mut checker = Checker {
268         tcx: tcx,
269         active_features: active_features,
270         used_features: FnvHashMap()
271     };
272
273     let krate = tcx.map.krate();
274     visit::walk_crate(&mut checker, krate);
275
276     let used_features = checker.used_features;
277     return used_features;
278 }
279
280 struct Checker<'a, 'tcx: 'a> {
281     tcx: &'a ty::ctxt<'tcx>,
282     active_features: FnvHashSet<InternedString>,
283     used_features: FnvHashMap<InternedString, attr::StabilityLevel>
284 }
285
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 }
291
292         match *stab {
293             Some(&Stability { level: attr::Unstable, ref feature, ref reason, issue, .. }) => {
294                 self.used_features.insert(feature.clone(), attr::Unstable);
295
296                 if !self.active_features.contains(feature) {
297                     let msg = match *reason {
298                         Some(ref r) => format!("use of unstable library feature '{}': {}",
299                                                &feature, &r),
300                         None => format!("use of unstable library feature '{}'", &feature)
301                     };
302
303                     emit_feature_err(&self.tcx.sess.parse_sess.span_diagnostic,
304                                       &feature, span, GateIssue::Library(issue), &msg);
305                 }
306             }
307             Some(&Stability { level, ref feature, .. }) => {
308                 self.used_features.insert(feature.clone(), level);
309
310                 // Stable APIs are always ok to call and deprecated APIs are
311                 // handled by a lint.
312             }
313             None => {
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");
321                 } else {
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");
328                 }
329             }
330         }
331     }
332 }
333
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
338         // name `__test`
339         if item.span == DUMMY_SP && item.ident.name == "__test" { return }
340
341         check_item(self.tcx, item, true,
342                    &mut |id, sp, stab| self.check(id, sp, stab));
343         visit::walk_item(self, item);
344     }
345
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);
350     }
351
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)
356     }
357
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)
362     }
363 }
364
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>)) {
368     match item.node {
369         hir::ItemExternCrate(_) => {
370             // compiler-generated `extern crate` items have a dummy span.
371             if item.span == DUMMY_SP { return }
372
373             let cnum = match tcx.sess.cstore.find_extern_mod_stmt_cnum(item.id) {
374                 Some(cnum) => cnum,
375                 None => return,
376             };
377             let id = DefId { krate: cnum, node: ast::CRATE_NODE_ID };
378             maybe_do_stability_check(tcx, id, item.span, cb);
379         }
380
381         // For implementations of traits, check the stability of each item
382         // individually as it's possible to have a stable trait with unstable
383         // items.
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);
387
388             for impl_item in impl_items {
389                 let item = trait_items.iter().find(|item| {
390                     item.name() == impl_item.ident.name
391                 }).unwrap();
392                 if warn_about_defns {
393                     maybe_do_stability_check(tcx, item.def_id(), impl_item.span, cb);
394                 }
395             }
396         }
397
398         _ => (/* pass */)
399     }
400 }
401
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>)) {
405     let span;
406     let id = match e.node {
407         hir::ExprMethodCall(i, _, _) => {
408             span = i.span;
409             let method_call = ty::MethodCall::expr(e.id);
410             tcx.tables.borrow().method_map[&method_call].def_id
411         }
412         hir::ExprField(ref base_e, ref field) => {
413             span = field.span;
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")
418             }
419         }
420         hir::ExprTupField(ref base_e, ref field) => {
421             span = field.span;
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")
428             }
429         }
430         hir::ExprStruct(_, ref expr_fields, _) => {
431             let type_ = tcx.expr_ty(e);
432             match type_.sty {
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)
439                             .did;
440                         maybe_do_stability_check(tcx, did, field.span, cb);
441                     }
442
443                     // we're done.
444                     return
445                 }
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,
450                 _ => {
451                     tcx.sess.span_bug(e.span,
452                                       &format!("stability::check_expr: struct construction \
453                                                 of non-struct, type {:?}",
454                                                type_));
455                 }
456             }
457         }
458         _ => return
459     };
460
461     maybe_do_stability_check(tcx, id, span, cb);
462 }
463
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(..)) => {}
468         Some(def) => {
469             maybe_do_stability_check(tcx, def.def_id(), path.span, cb);
470         }
471         None => {}
472     }
473
474 }
475
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; }
480
481     let v = match tcx.pat_ty_opt(pat) {
482         Some(&ty::TyS { sty: ty::TyStruct(def, _), .. }) => def.struct_variant(),
483         Some(_) | None => return,
484     };
485     match pat.node {
486         // Foo(a, b, c)
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
490                 // not.
491                 if let hir::PatWild(hir::PatWildMulti) = field.node {
492                     continue
493                 }
494                 maybe_do_stability_check(tcx, struct_field.did, field.span, cb)
495             }
496         }
497         // Foo { a, b, c }
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);
502             }
503         }
504         // everything else is fine.
505         _ => {}
506     }
507 }
508
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);
514         return;
515     }
516     if is_internal(tcx, span) {
517         debug!("maybe_do_stability_check: \
518                 skipping span={:?} since it is internal", span);
519         return;
520     }
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);
525 }
526
527 fn is_internal(tcx: &ty::ctxt, span: Span) -> bool {
528     tcx.sess.codemap().span_allows_unstable(span)
529 }
530
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)
536             }
537         _ => {
538             *tcx.stability.borrow_mut().staged_api.entry(id.krate).or_insert_with(
539                 || csearch::is_staged_api(&tcx.sess.cstore, id.krate))
540         }
541     }
542 }
543
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) {
548         return *st;
549     }
550
551     let st = lookup_uncached(tcx, id);
552     tcx.stability.borrow_mut().map.insert(id, st);
553     st
554 }
555
556 fn lookup_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> {
557     debug!("lookup(id={:?})", id);
558
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)
564         }
565         _ => {}
566     }
567
568     let item_stab = if id.is_local() {
569         None // The stability cache is filled partially lazily
570     } else {
571         csearch::get_stability(&tcx.sess.cstore, id).map(|st| tcx.intern_stability(st))
572     };
573
574     item_stab.or_else(|| {
575         if tcx.is_impl(id) {
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.
580
581                 debug!("lookup: trait_id={:?}", trait_id);
582                 return lookup(tcx, trait_id);
583             }
584         }
585         None
586     })
587 }
588
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();
598
599     let stable_msg = "this feature is stable. attribute no longer needed";
600
601     for &span in &sess.features.borrow().declared_stable_lang_features {
602         sess.add_lint(lint::builtin::STABLE_FEATURES,
603                       ast::CRATE_NODE_ID,
604                       span,
605                       stable_msg.to_string());
606     }
607
608     for (used_lib_feature, level) in lib_features_used {
609         match remaining_lib_features.remove(used_lib_feature) {
610             Some(span) => {
611                 if *level == attr::Stable {
612                     sess.add_lint(lint::builtin::STABLE_FEATURES,
613                                   ast::CRATE_NODE_ID,
614                                   span,
615                                   stable_msg.to_string());
616                 }
617             }
618             None => ( /* used but undeclared, handled during the previous ast visit */ )
619         }
620     }
621
622     for &span in remaining_lib_features.values() {
623         sess.add_lint(lint::builtin::UNUSED_FEATURES,
624                       ast::CRATE_NODE_ID,
625                       span,
626                       "unused or unknown feature".to_string());
627     }
628 }