]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/stability.rs
rustc: desugar `use a::{b,c};` into `use a::b; use a::c;` in HIR.
[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 pub use self::StabilityLevel::*;
15
16 use dep_graph::DepNode;
17 use hir::map as hir_map;
18 use session::Session;
19 use lint;
20 use hir::def::Def;
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};
26 use syntax::ast;
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};
31
32 use hir;
33 use hir::{Item, Generics, StructField, Variant, PatKind};
34 use hir::intravisit::{self, Visitor};
35 use hir::pat_util::EnumerateAndAdjustIterator;
36
37 use std::mem::replace;
38 use std::cmp::Ordering;
39
40 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Copy, Debug, Eq, Hash)]
41 pub enum StabilityLevel {
42     Unstable,
43     Stable,
44 }
45
46 impl StabilityLevel {
47     pub fn from_attr_level(level: &attr::StabilityLevel) -> Self {
48         if level.is_stable() { Stable } else { Unstable }
49     }
50 }
51
52 #[derive(PartialEq)]
53 enum AnnotationKind {
54     // Annotation is required if not inherited from unstable parents
55     Required,
56     // Annotation is useless, reject it
57     Prohibited,
58     // Annotation itself is useless, but it can be propagated to children
59     Container,
60 }
61
62 /// An entry in the `depr_map`.
63 #[derive(Clone)]
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
68     /// `DefId`'s.
69     origin: Option<DefIndex>,
70 }
71
72 impl DeprecationEntry {
73     fn local(attr: Deprecation, id: DefId) -> DeprecationEntry {
74         assert!(id.is_local());
75         DeprecationEntry {
76             attr: attr,
77             origin: Some(id.index),
78         }
79     }
80
81     fn external(attr: Deprecation) -> DeprecationEntry {
82         DeprecationEntry {
83             attr: attr,
84             origin: None,
85         }
86     }
87
88     pub fn same_origin(&self, other: &DeprecationEntry) -> bool {
89         match (self.origin, other.origin) {
90             (Some(o1), Some(o2)) => o1 == o2,
91             _ => false
92         }
93     }
94 }
95
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>>,
102
103     /// Maps for each crate whether it is part of the staged API.
104     staged_api: FxHashMap<CrateNum, bool>
105 }
106
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,
114     in_trait_impl: bool,
115 }
116
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)
123     {
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");
129             }
130             if let Some(mut stab) = attr::find_stability(self.tcx.sess.diagnostic(),
131                                                          attrs, item_sp) {
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");
138                 }
139
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()
146                     }
147                 }
148
149                 let stab = self.tcx.intern_stability(stab);
150
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) {
160                                 Ordering::Less => {
161                                     self.tcx.sess.span_err(item_sp, "An API can't be stabilized \
162                                                                      after it is deprecated");
163                                     break
164                                 }
165                                 Ordering::Equal => continue,
166                                 Ordering::Greater => break,
167                             }
168                         } else {
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 \
172                                                              version found");
173                             break
174                         }
175                     }
176                 }
177
178                 let def_id = self.tcx.map.local_def_id(id);
179                 self.index.stab_map.insert(def_id, Some(stab));
180
181                 let orig_parent_stab = replace(&mut self.parent_stab, Some(stab));
182                 visit_children(self);
183                 self.parent_stab = orig_parent_stab;
184             } else {
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));
193                         is_error = false;
194                     }
195                 }
196                 if is_error {
197                     self.tcx.sess.span_err(item_sp, "This node does not have \
198                                                      a stability attribute");
199                 }
200                 visit_children(self);
201             }
202         } else {
203             // Emit errors for non-staged-api crates.
204             for attr in attrs {
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");
210                 }
211             }
212
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");
216                 }
217
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());
222
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);
230             } else {
231                 visit_children(self);
232             }
233         }
234     }
235 }
236
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
240     /// deep-walking.
241     fn nested_visit_map(&mut self) -> Option<&hir::map::Map<'tcx>> {
242         Some(&self.tcx.map)
243     }
244
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;
248         match i.node {
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;
256             }
257             hir::ItemImpl(.., Some(_), _, _) => {
258                 self.in_trait_impl = true;
259             }
260             hir::ItemStruct(ref sd, _) => {
261                 if !sd.is_struct() {
262                     self.annotate(sd.id(), &i.attrs, i.span, AnnotationKind::Required, |_| {})
263                 }
264             }
265             _ => {}
266         }
267
268         self.annotate(i.id, &i.attrs, i.span, kind, |v| {
269             intravisit::walk_item(v, i)
270         });
271         self.in_trait_impl = orig_in_trait_impl;
272     }
273
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);
277         });
278     }
279
280     fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem) {
281         let kind = if self.in_trait_impl {
282             AnnotationKind::Prohibited
283         } else {
284             AnnotationKind::Required
285         };
286         self.annotate(ii.id, &ii.attrs, ii.span, kind, |v| {
287             intravisit::walk_impl_item(v, ii);
288         });
289     }
290
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);
294         })
295     }
296
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);
300         });
301     }
302
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);
306         });
307     }
308
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, |_| {});
312         }
313     }
314 }
315
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 {
322             tcx: tcx,
323             index: self,
324             parent_stab: None,
325             parent_depr: None,
326             access_levels: access_levels,
327             in_trait_impl: false,
328         };
329         annotator.annotate(ast::CRATE_NODE_ID, &krate.attrs, krate.span, AnnotationKind::Required,
330                            |v| intravisit::walk_crate(v, krate));
331     }
332
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();
336
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;
341                 break
342             }
343         }
344
345         let mut staged_api = FxHashMap();
346         staged_api.insert(LOCAL_CRATE, is_staged_api);
347         Index {
348             staged_api: staged_api,
349             stab_map: DefIdMap(),
350             depr_map: DefIdMap(),
351         }
352     }
353 }
354
355 /// Cross-references the feature names of unstable APIs with enabled
356 /// features and possibly prints errors. Returns a list of all
357 /// features used.
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;
362
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();
365
366     let mut checker = Checker {
367         tcx: tcx,
368         active_features: active_features,
369         used_features: FxHashMap(),
370         in_skip_block: 0,
371     };
372     intravisit::walk_crate(&mut checker, tcx.map.krate());
373
374     checker.used_features
375 }
376
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.
382     in_skip_block: u32,
383 }
384
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) {
389             return;
390         }
391         // Only the cross-crate scenario matters when checking unstable APIs
392         let cross_crate = !id.is_local();
393         if !cross_crate {
394             return
395         }
396
397         // We don't need to check for stability - presumably compiler generated code.
398         if self.in_skip_block > 0 {
399             return;
400         }
401
402         match *stab {
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 });
406
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)
412                     };
413                     emit_feature_err(&self.tcx.sess.parse_sess, &feature.as_str(), span,
414                                      GateIssue::Library(Some(issue)), &msg);
415                 }
416             }
417             Some(&Stability { ref level, ref feature, .. }) => {
418                 self.used_features.insert(feature.clone(), level.clone());
419
420                 // Stable APIs are always ok to call and deprecated APIs are
421                 // handled by a lint.
422             }
423             None => {
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")
431                                  .emit()
432                 } else {
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")
439                                  .emit()
440                 }
441             }
442         }
443     }
444 }
445
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
449     /// deep-walking.
450     fn nested_visit_map(&mut self) -> Option<&hir::map::Map<'tcx>> {
451         Some(&self.tcx.map)
452     }
453
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
457         // name `__test`
458         if item.span == DUMMY_SP && item.name == "__test" { return }
459
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);
463     }
464
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);
469     }
470
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)
475     }
476
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)
481     }
482
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)
487     }
488
489     fn visit_block(&mut self, b: &'tcx hir::Block) {
490         let old_skip_count = self.in_skip_block;
491         match b.rules {
492             hir::BlockCheckMode::PushUnstableBlock => {
493                 self.in_skip_block += 1;
494             }
495             hir::BlockCheckMode::PopUnstableBlock => {
496                 self.in_skip_block = self.in_skip_block.checked_sub(1).unwrap();
497             }
498             _ => {}
499         }
500         intravisit::walk_block(self, b);
501         self.in_skip_block = old_skip_count;
502     }
503 }
504
505 /// Helper for discovering nodes to check for stability
506 pub fn check_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
507                             item: &hir::Item,
508                             warn_about_defns: bool,
509                             cb: &mut FnMut(DefId, Span,
510                                            &Option<&Stability>,
511                                            &Option<DeprecationEntry>)) {
512     match item.node {
513         hir::ItemExternCrate(_) => {
514             // compiler-generated `extern crate` items have a dummy span.
515             if item.span == DUMMY_SP { return }
516
517             let cnum = match tcx.sess.cstore.extern_mod_stmt_cnum(item.id) {
518                 Some(cnum) => cnum,
519                 None => return,
520             };
521             let id = DefId { krate: cnum, index: CRATE_DEF_INDEX };
522             maybe_do_stability_check(tcx, id, item.span, cb);
523         }
524
525         // For implementations of traits, check the stability of each item
526         // individually as it's possible to have a stable trait with unstable
527         // items.
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);
536                 }
537             }
538         }
539
540         _ => (/* pass */)
541     }
542 }
543
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,
547                                            &Option<&Stability>,
548                                            &Option<DeprecationEntry>)) {
549     let span;
550     let id = match e.node {
551         hir::ExprMethodCall(i, ..) => {
552             span = i.span;
553             let method_call = ty::MethodCall::expr(e.id);
554             tcx.tables().method_map[&method_call].def_id
555         }
556         hir::ExprPath(hir::QPath::TypeRelative(..)) => {
557             span = e.span;
558             tcx.expect_def(e.id).def_id()
559         }
560         hir::ExprField(ref base_e, ref field) => {
561             span = field.span;
562             match tcx.tables().expr_ty_adjusted(base_e).sty {
563                 ty::TyAdt(def, _) => {
564                     def.struct_variant().field_named(field.node).did
565                 }
566                 _ => span_bug!(e.span,
567                                "stability::check_expr: named field access on non-ADT")
568             }
569         }
570         hir::ExprTupField(ref base_e, ref field) => {
571             span = field.span;
572             match tcx.tables().expr_ty_adjusted(base_e).sty {
573                 ty::TyAdt(def, _) => {
574                     def.struct_variant().fields[field.node].did
575                 }
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")
580             }
581         }
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);
591                         }
592
593                         // we're done.
594                         return
595                     }
596                     AdtKind::Enum => {
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.
600                         return
601                     }
602                 },
603                 ref ty => span_bug!(e.span, "stability::check_expr: struct \
604                                          construction of non-ADT type: {:?}", ty)
605             }
606         }
607         _ => return
608     };
609
610     maybe_do_stability_check(tcx, id, span, cb);
611 }
612
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,
616                                            &Option<&Stability>,
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)
622     }
623 }
624
625 pub fn check_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pat: &hir::Pat,
626                            cb: &mut FnMut(DefId, Span,
627                                           &Option<&Stability>,
628                                           &Option<DeprecationEntry>)) {
629     debug!("check_pat(pat = {:?})", pat);
630     if is_internal(tcx, pat.span) { return; }
631
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)
635     }
636
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(),
639         _ => return,
640     };
641     match pat.node {
642         // Foo(a, b, c)
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)
646             }
647         }
648         // Foo { a, b, c }
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);
653             }
654         }
655         // everything else is fine.
656         _ => {}
657     }
658 }
659
660 pub fn check_ty<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: &hir::Ty,
661                           cb: &mut FnMut(DefId, Span,
662                                          &Option<&Stability>,
663                                          &Option<DeprecationEntry>)) {
664     debug!("check_ty(ty = {:?})", ty);
665     if is_internal(tcx, ty.span) { return; }
666
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);
670     }
671 }
672
673 fn maybe_do_stability_check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
674                                       id: DefId, span: Span,
675                                       cb: &mut FnMut(DefId, Span,
676                                                      &Option<&Stability>,
677                                                      &Option<DeprecationEntry>)) {
678     if is_internal(tcx, span) {
679         debug!("maybe_do_stability_check: \
680                 skipping span={:?} since it is internal", span);
681         return;
682     }
683     let (stability, deprecation) = if is_staged_api(tcx, id) {
684         (tcx.lookup_stability(id), None)
685     } else {
686         (None, tcx.lookup_deprecation_entry(id))
687     };
688     debug!("maybe_do_stability_check: \
689             inspecting id={:?} span={:?} of stability={:?}", id, span, stability);
690     cb(id, span, &stability, &deprecation);
691 }
692
693 fn is_internal<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, span: Span) -> bool {
694     tcx.sess.codemap().span_allows_unstable(span)
695 }
696
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))
700 }
701
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) {
707             return *st;
708         }
709
710         let st = self.lookup_stability_uncached(id);
711         self.stability.borrow_mut().stab_map.insert(id, st);
712         st
713     }
714
715     pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> {
716         self.lookup_deprecation_entry(id).map(|depr| depr.attr)
717     }
718
719     pub fn lookup_deprecation_entry(self, id: DefId) -> Option<DeprecationEntry> {
720         if let Some(depr) = self.stability.borrow().depr_map.get(&id) {
721             return depr.clone();
722         }
723
724         let depr = self.lookup_deprecation_uncached(id);
725         self.stability.borrow_mut().depr_map.insert(id, depr.clone());
726         depr
727     }
728
729     fn lookup_stability_uncached(self, id: DefId) -> Option<&'tcx Stability> {
730         debug!("lookup(id={:?})", id);
731         if id.is_local() {
732             None // The stability cache is filled partially lazily
733         } else {
734             self.sess.cstore.stability(id).map(|st| self.intern_stability(st))
735         }
736     }
737
738     fn lookup_deprecation_uncached(self, id: DefId) -> Option<DeprecationEntry> {
739         debug!("lookup(id={:?})", id);
740         if id.is_local() {
741             None // The stability cache is filled partially lazily
742         } else {
743             self.sess.cstore.deprecation(id).map(DeprecationEntry::external)
744         }
745     }
746 }
747
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();
757
758     fn format_stable_since_msg(version: &str) -> String {
759         format!("this feature has been stable since {}. Attribute no longer needed", version)
760     }
761
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,
766                       ast::CRATE_NODE_ID,
767                       span,
768                       format_stable_since_msg(version));
769     }
770
771     for (used_lib_feature, level) in lib_features_used {
772         match remaining_lib_features.remove(used_lib_feature) {
773             Some(span) => {
774                 if let &attr::StabilityLevel::Stable { since: ref version } = level {
775                     sess.add_lint(lint::builtin::STABLE_FEATURES,
776                                   ast::CRATE_NODE_ID,
777                                   span,
778                                   format_stable_since_msg(&version.as_str()));
779                 }
780             }
781             None => ( /* used but undeclared, handled during the previous ast visit */ )
782         }
783     }
784
785     for &span in remaining_lib_features.values() {
786         sess.add_lint(lint::builtin::UNUSED_FEATURES,
787                       ast::CRATE_NODE_ID,
788                       span,
789                       "unused or unknown feature".to_string());
790     }
791 }