]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/stability.rs
Auto merge of #30532 - nikomatsakis:cross-item-dependencies, r=mw
[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 session::Session;
18 use lint;
19 use middle::cstore::{CrateStore, LOCAL_CRATE};
20 use middle::def;
21 use middle::def_id::{CRATE_DEF_INDEX, DefId};
22 use middle::ty;
23 use middle::privacy::AccessLevels;
24 use syntax::parse::token::InternedString;
25 use syntax::codemap::{Span, DUMMY_SP};
26 use syntax::ast;
27 use syntax::ast::{NodeId, Attribute};
28 use syntax::feature_gate::{GateIssue, emit_feature_err};
29 use syntax::attr::{self, Stability, Deprecation, AttrMetaMethods};
30 use util::nodemap::{DefIdMap, FnvHashSet, FnvHashMap};
31
32 use rustc_front::hir;
33 use rustc_front::hir::{Crate, Item, Generics, StructField, Variant};
34 use rustc_front::intravisit::{self, Visitor};
35
36 use std::mem::replace;
37 use std::cmp::Ordering;
38
39 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Copy, Debug, Eq, Hash)]
40 pub enum StabilityLevel {
41     Unstable,
42     Stable,
43 }
44
45 impl StabilityLevel {
46     pub fn from_attr_level(level: &attr::StabilityLevel) -> Self {
47         if level.is_stable() { Stable } else { Unstable }
48     }
49 }
50
51 #[derive(PartialEq)]
52 enum AnnotationKind {
53     // Annotation is required if not inherited from unstable parents
54     Required,
55     // Annotation is useless, reject it
56     Prohibited,
57     // Annotation itself is useless, but it can be propagated to children
58     Container,
59 }
60
61 /// A stability index, giving the stability level for items and methods.
62 pub struct Index<'tcx> {
63     /// This is mostly a cache, except the stabilities of local items
64     /// are filled by the annotator.
65     stab_map: DefIdMap<Option<&'tcx Stability>>,
66     depr_map: DefIdMap<Option<Deprecation>>,
67
68     /// Maps for each crate whether it is part of the staged API.
69     staged_api: FnvHashMap<ast::CrateNum, bool>
70 }
71
72 // A private tree-walker for producing an Index.
73 struct Annotator<'a, 'tcx: 'a> {
74     tcx: &'a ty::ctxt<'tcx>,
75     index: &'a mut Index<'tcx>,
76     parent_stab: Option<&'tcx Stability>,
77     parent_depr: Option<Deprecation>,
78     access_levels: &'a AccessLevels,
79     in_trait_impl: bool,
80     in_enum: bool,
81 }
82
83 impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
84     // Determine the stability for a node based on its attributes and inherited
85     // stability. The stability is recorded in the index and used as the parent.
86     fn annotate<F>(&mut self, id: NodeId, attrs: &[Attribute],
87                    item_sp: Span, kind: AnnotationKind, visit_children: F)
88         where F: FnOnce(&mut Annotator)
89     {
90         if self.index.staged_api[&LOCAL_CRATE] && self.tcx.sess.features.borrow().staged_api {
91             debug!("annotate(id = {:?}, attrs = {:?})", id, attrs);
92             if let Some(..) = attr::find_deprecation(self.tcx.sess.diagnostic(), attrs, item_sp) {
93                 self.tcx.sess.span_err(item_sp, "`#[deprecated]` cannot be used in staged api, \
94                                                  use `#[rustc_deprecated]` instead");
95             }
96             if let Some(mut stab) = attr::find_stability(self.tcx.sess.diagnostic(),
97                                                          attrs, item_sp) {
98                 // Error if prohibited, or can't inherit anything from a container
99                 if kind == AnnotationKind::Prohibited ||
100                    (kind == AnnotationKind::Container &&
101                     stab.level.is_stable() &&
102                     stab.rustc_depr.is_none()) {
103                     self.tcx.sess.span_err(item_sp, "This stability annotation is useless");
104                 }
105
106                 debug!("annotate: found {:?}", stab);
107                 // If parent is deprecated and we're not, inherit this by merging
108                 // deprecated_since and its reason.
109                 if let Some(parent_stab) = self.parent_stab {
110                     if parent_stab.rustc_depr.is_some() && stab.rustc_depr.is_none() {
111                         stab.rustc_depr = parent_stab.rustc_depr.clone()
112                     }
113                 }
114
115                 let stab = self.tcx.intern_stability(stab);
116
117                 // Check if deprecated_since < stable_since. If it is,
118                 // this is *almost surely* an accident.
119                 if let (&Some(attr::RustcDeprecation {since: ref dep_since, ..}),
120                         &attr::Stable {since: ref stab_since}) = (&stab.rustc_depr, &stab.level) {
121                     // Explicit version of iter::order::lt to handle parse errors properly
122                     for (dep_v, stab_v) in dep_since.split(".").zip(stab_since.split(".")) {
123                         if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::<u64>(), stab_v.parse()) {
124                             match dep_v.cmp(&stab_v) {
125                                 Ordering::Less => {
126                                     self.tcx.sess.span_err(item_sp, "An API can't be stabilized \
127                                                                      after it is deprecated");
128                                     break
129                                 }
130                                 Ordering::Equal => continue,
131                                 Ordering::Greater => break,
132                             }
133                         } else {
134                             // Act like it isn't less because the question is now nonsensical,
135                             // and this makes us not do anything else interesting.
136                             self.tcx.sess.span_err(item_sp, "Invalid stability or deprecation \
137                                                              version found");
138                             break
139                         }
140                     }
141                 }
142
143                 let def_id = self.tcx.map.local_def_id(id);
144                 self.index.stab_map.insert(def_id, Some(stab));
145
146                 let orig_parent_stab = replace(&mut self.parent_stab, Some(stab));
147                 visit_children(self);
148                 self.parent_stab = orig_parent_stab;
149             } else {
150                 debug!("annotate: not found, parent = {:?}", self.parent_stab);
151                 let mut is_error = kind == AnnotationKind::Required &&
152                                    self.access_levels.is_reachable(id) &&
153                                    !self.tcx.sess.opts.test;
154                 if let Some(stab) = self.parent_stab {
155                     if stab.level.is_unstable() {
156                         let def_id = self.tcx.map.local_def_id(id);
157                         self.index.stab_map.insert(def_id, Some(stab));
158                         is_error = false;
159                     }
160                 }
161                 if is_error {
162                     self.tcx.sess.span_err(item_sp, "This node does not have \
163                                                      a stability attribute");
164                 }
165                 visit_children(self);
166             }
167         } else {
168             // Emit errors for non-staged-api crates.
169             for attr in attrs {
170                 let tag = attr.name();
171                 if tag == "unstable" || tag == "stable" || tag == "rustc_deprecated" {
172                     attr::mark_used(attr);
173                     self.tcx.sess.span_err(attr.span(), "stability attributes may not be used \
174                                                          outside of the standard library");
175                 }
176             }
177
178             if let Some(depr) = attr::find_deprecation(self.tcx.sess.diagnostic(), attrs, item_sp) {
179                 if kind == AnnotationKind::Prohibited {
180                     self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless");
181                 }
182
183                 // `Deprecation` is just two pointers, no need to intern it
184                 let def_id = self.tcx.map.local_def_id(id);
185                 self.index.depr_map.insert(def_id, Some(depr.clone()));
186
187                 let orig_parent_depr = replace(&mut self.parent_depr, Some(depr));
188                 visit_children(self);
189                 self.parent_depr = orig_parent_depr;
190             } else if let Some(depr) = self.parent_depr.clone() {
191                 let def_id = self.tcx.map.local_def_id(id);
192                 self.index.depr_map.insert(def_id, Some(depr));
193                 visit_children(self);
194             } else {
195                 visit_children(self);
196             }
197         }
198     }
199 }
200
201 impl<'a, 'tcx, 'v> Visitor<'v> for Annotator<'a, 'tcx> {
202     /// Because stability levels are scoped lexically, we want to walk
203     /// nested items in the context of the outer item, so enable
204     /// deep-walking.
205     fn visit_nested_item(&mut self, item: hir::ItemId) {
206         self.visit_item(self.tcx.map.expect_item(item.id))
207     }
208
209     fn visit_item(&mut self, i: &Item) {
210         let orig_in_trait_impl = self.in_trait_impl;
211         let orig_in_enum = self.in_enum;
212         let mut kind = AnnotationKind::Required;
213         match i.node {
214             // Inherent impls and foreign modules serve only as containers for other items,
215             // they don't have their own stability. They still can be annotated as unstable
216             // and propagate this unstability to children, but this annotation is completely
217             // optional. They inherit stability from their parents when unannotated.
218             hir::ItemImpl(_, _, _, None, _, _) | hir::ItemForeignMod(..) => {
219                 self.in_trait_impl = false;
220                 kind = AnnotationKind::Container;
221             }
222             hir::ItemImpl(_, _, _, Some(_), _, _) => {
223                 self.in_trait_impl = true;
224             }
225             hir::ItemStruct(ref sd, _) => {
226                 self.in_enum = false;
227                 if !sd.is_struct() {
228                     self.annotate(sd.id(), &i.attrs, i.span, AnnotationKind::Required, |_| {})
229                 }
230             }
231             hir::ItemEnum(..) => {
232                 self.in_enum = true;
233             }
234             _ => {}
235         }
236
237         self.annotate(i.id, &i.attrs, i.span, kind, |v| {
238             intravisit::walk_item(v, i)
239         });
240         self.in_trait_impl = orig_in_trait_impl;
241         self.in_enum = orig_in_enum;
242     }
243
244     fn visit_trait_item(&mut self, ti: &hir::TraitItem) {
245         self.annotate(ti.id, &ti.attrs, ti.span, AnnotationKind::Required, |v| {
246             intravisit::walk_trait_item(v, ti);
247         });
248     }
249
250     fn visit_impl_item(&mut self, ii: &hir::ImplItem) {
251         let kind = if self.in_trait_impl {
252             AnnotationKind::Prohibited
253         } else {
254             AnnotationKind::Required
255         };
256         self.annotate(ii.id, &ii.attrs, ii.span, kind, |v| {
257             intravisit::walk_impl_item(v, ii);
258         });
259     }
260
261     fn visit_variant(&mut self, var: &Variant, g: &'v Generics, item_id: NodeId) {
262         self.annotate(var.node.data.id(), &var.node.attrs, var.span, AnnotationKind::Required, |v| {
263             intravisit::walk_variant(v, var, g, item_id);
264         })
265     }
266
267     fn visit_struct_field(&mut self, s: &StructField) {
268         // FIXME: This is temporary, can't use attributes with tuple variant fields until snapshot
269         let kind = if self.in_enum && s.node.kind.is_unnamed() {
270             AnnotationKind::Prohibited
271         } else {
272             AnnotationKind::Required
273         };
274         self.annotate(s.node.id, &s.node.attrs, s.span, kind, |v| {
275             intravisit::walk_struct_field(v, s);
276         });
277     }
278
279     fn visit_foreign_item(&mut self, i: &hir::ForeignItem) {
280         self.annotate(i.id, &i.attrs, i.span, AnnotationKind::Required, |v| {
281             intravisit::walk_foreign_item(v, i);
282         });
283     }
284
285     fn visit_macro_def(&mut self, md: &'v hir::MacroDef) {
286         if md.imported_from.is_none() {
287             self.annotate(md.id, &md.attrs, md.span, AnnotationKind::Required, |_| {});
288         }
289     }
290 }
291
292 impl<'tcx> Index<'tcx> {
293     /// Construct the stability index for a crate being compiled.
294     pub fn build(&mut self, tcx: &ty::ctxt<'tcx>, krate: &Crate, access_levels: &AccessLevels) {
295         let mut annotator = Annotator {
296             tcx: tcx,
297             index: self,
298             parent_stab: None,
299             parent_depr: None,
300             access_levels: access_levels,
301             in_trait_impl: false,
302             in_enum: false,
303         };
304         annotator.annotate(ast::CRATE_NODE_ID, &krate.attrs, krate.span, AnnotationKind::Required,
305                            |v| intravisit::walk_crate(v, krate));
306     }
307
308     pub fn new(krate: &Crate) -> Index<'tcx> {
309         let mut is_staged_api = false;
310         for attr in &krate.attrs {
311             if attr.name() == "stable" || attr.name() == "unstable" {
312                 is_staged_api = true;
313                 break
314             }
315         }
316
317         let mut staged_api = FnvHashMap();
318         staged_api.insert(LOCAL_CRATE, is_staged_api);
319         Index {
320             staged_api: staged_api,
321             stab_map: DefIdMap(),
322             depr_map: DefIdMap(),
323         }
324     }
325 }
326
327 /// Cross-references the feature names of unstable APIs with enabled
328 /// features and possibly prints errors. Returns a list of all
329 /// features used.
330 pub fn check_unstable_api_usage(tcx: &ty::ctxt)
331                                 -> FnvHashMap<InternedString, StabilityLevel> {
332     let _task = tcx.dep_graph.in_task(DepNode::StabilityCheck);
333     let ref active_lib_features = tcx.sess.features.borrow().declared_lib_features;
334
335     // Put the active features into a map for quick lookup
336     let active_features = active_lib_features.iter().map(|&(ref s, _)| s.clone()).collect();
337
338     let mut checker = Checker {
339         tcx: tcx,
340         active_features: active_features,
341         used_features: FnvHashMap(),
342         in_skip_block: 0,
343     };
344     intravisit::walk_crate(&mut checker, tcx.map.krate());
345
346     checker.used_features
347 }
348
349 struct Checker<'a, 'tcx: 'a> {
350     tcx: &'a ty::ctxt<'tcx>,
351     active_features: FnvHashSet<InternedString>,
352     used_features: FnvHashMap<InternedString, StabilityLevel>,
353     // Within a block where feature gate checking can be skipped.
354     in_skip_block: u32,
355 }
356
357 impl<'a, 'tcx> Checker<'a, 'tcx> {
358     fn check(&mut self, id: DefId, span: Span,
359              stab: &Option<&Stability>, _depr: &Option<Deprecation>) {
360         if !is_staged_api(self.tcx, id) {
361             return;
362         }
363         // Only the cross-crate scenario matters when checking unstable APIs
364         let cross_crate = !id.is_local();
365         if !cross_crate {
366             return
367         }
368
369         // We don't need to check for stability - presumably compiler generated code.
370         if self.in_skip_block > 0 {
371             return;
372         }
373
374         match *stab {
375             Some(&Stability { level: attr::Unstable {ref reason, issue}, ref feature, .. }) => {
376                 self.used_features.insert(feature.clone(), Unstable);
377
378                 if !self.active_features.contains(feature) {
379                     let msg = match *reason {
380                         Some(ref r) => format!("use of unstable library feature '{}': {}",
381                                                &feature, &r),
382                         None => format!("use of unstable library feature '{}'", &feature)
383                     };
384                     emit_feature_err(&self.tcx.sess.parse_sess.span_diagnostic,
385                                       &feature, span, GateIssue::Library(Some(issue)), &msg);
386                 }
387             }
388             Some(&Stability { ref level, ref feature, .. }) => {
389                 self.used_features.insert(feature.clone(), StabilityLevel::from_attr_level(level));
390
391                 // Stable APIs are always ok to call and deprecated APIs are
392                 // handled by a lint.
393             }
394             None => {
395                 // This is an 'unmarked' API, which should not exist
396                 // in the standard library.
397                 if self.tcx.sess.features.borrow().unmarked_api {
398                     self.tcx.sess.struct_span_warn(span, "use of unmarked library feature")
399                                  .span_note(span, "this is either a bug in the library you are \
400                                                    using or a bug in the compiler - please \
401                                                    report it in both places")
402                                  .emit()
403                 } else {
404                     self.tcx.sess.struct_span_err(span, "use of unmarked library feature")
405                                  .span_note(span, "this is either a bug in the library you are \
406                                                    using or a bug in the compiler - please \
407                                                    report it in both places")
408                                  .span_note(span, "use #![feature(unmarked_api)] in the \
409                                                    crate attributes to override this")
410                                  .emit()
411                 }
412             }
413         }
414     }
415 }
416
417 impl<'a, 'v, 'tcx> Visitor<'v> for Checker<'a, 'tcx> {
418     /// Because stability levels are scoped lexically, we want to walk
419     /// nested items in the context of the outer item, so enable
420     /// deep-walking.
421     fn visit_nested_item(&mut self, item: hir::ItemId) {
422         self.visit_item(self.tcx.map.expect_item(item.id))
423     }
424
425     fn visit_item(&mut self, item: &hir::Item) {
426         // When compiling with --test we don't enforce stability on the
427         // compiler-generated test module, demarcated with `DUMMY_SP` plus the
428         // name `__test`
429         if item.span == DUMMY_SP && item.name.as_str() == "__test" { return }
430
431         check_item(self.tcx, item, true,
432                    &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
433         intravisit::walk_item(self, item);
434     }
435
436     fn visit_expr(&mut self, ex: &hir::Expr) {
437         check_expr(self.tcx, ex,
438                    &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
439         intravisit::walk_expr(self, ex);
440     }
441
442     fn visit_path(&mut self, path: &hir::Path, id: ast::NodeId) {
443         check_path(self.tcx, path, id,
444                    &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
445         intravisit::walk_path(self, path)
446     }
447
448     fn visit_path_list_item(&mut self, prefix: &hir::Path, item: &hir::PathListItem) {
449         check_path_list_item(self.tcx, item,
450                    &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
451         intravisit::walk_path_list_item(self, prefix, item)
452     }
453
454     fn visit_pat(&mut self, pat: &hir::Pat) {
455         check_pat(self.tcx, pat,
456                   &mut |id, sp, stab, depr| self.check(id, sp, stab, depr));
457         intravisit::walk_pat(self, pat)
458     }
459
460     fn visit_block(&mut self, b: &hir::Block) {
461         let old_skip_count = self.in_skip_block;
462         match b.rules {
463             hir::BlockCheckMode::PushUnstableBlock => {
464                 self.in_skip_block += 1;
465             }
466             hir::BlockCheckMode::PopUnstableBlock => {
467                 self.in_skip_block = self.in_skip_block.checked_sub(1).unwrap();
468             }
469             _ => {}
470         }
471         intravisit::walk_block(self, b);
472         self.in_skip_block = old_skip_count;
473     }
474 }
475
476 /// Helper for discovering nodes to check for stability
477 pub fn check_item(tcx: &ty::ctxt, item: &hir::Item, warn_about_defns: bool,
478                   cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) {
479     match item.node {
480         hir::ItemExternCrate(_) => {
481             // compiler-generated `extern crate` items have a dummy span.
482             if item.span == DUMMY_SP { return }
483
484             let cnum = match tcx.sess.cstore.extern_mod_stmt_cnum(item.id) {
485                 Some(cnum) => cnum,
486                 None => return,
487             };
488             let id = DefId { krate: cnum, index: CRATE_DEF_INDEX };
489             maybe_do_stability_check(tcx, id, item.span, cb);
490         }
491
492         // For implementations of traits, check the stability of each item
493         // individually as it's possible to have a stable trait with unstable
494         // items.
495         hir::ItemImpl(_, _, _, Some(ref t), _, ref impl_items) => {
496             let trait_did = tcx.def_map.borrow().get(&t.ref_id).unwrap().def_id();
497             let trait_items = tcx.trait_items(trait_did);
498
499             for impl_item in impl_items {
500                 let item = trait_items.iter().find(|item| {
501                     item.name() == impl_item.name
502                 }).unwrap();
503                 if warn_about_defns {
504                     maybe_do_stability_check(tcx, item.def_id(), impl_item.span, cb);
505                 }
506             }
507         }
508
509         _ => (/* pass */)
510     }
511 }
512
513 /// Helper for discovering nodes to check for stability
514 pub fn check_expr(tcx: &ty::ctxt, e: &hir::Expr,
515                   cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) {
516     let span;
517     let id = match e.node {
518         hir::ExprMethodCall(i, _, _) => {
519             span = i.span;
520             let method_call = ty::MethodCall::expr(e.id);
521             tcx.tables.borrow().method_map[&method_call].def_id
522         }
523         hir::ExprField(ref base_e, ref field) => {
524             span = field.span;
525             match tcx.expr_ty_adjusted(base_e).sty {
526                 ty::TyStruct(def, _) => def.struct_variant().field_named(field.node).did,
527                 _ => tcx.sess.span_bug(e.span,
528                                        "stability::check_expr: named field access on non-struct")
529             }
530         }
531         hir::ExprTupField(ref base_e, ref field) => {
532             span = field.span;
533             match tcx.expr_ty_adjusted(base_e).sty {
534                 ty::TyStruct(def, _) => def.struct_variant().fields[field.node].did,
535                 ty::TyTuple(..) => return,
536                 _ => tcx.sess.span_bug(e.span,
537                                        "stability::check_expr: unnamed field access on \
538                                         something other than a tuple or struct")
539             }
540         }
541         hir::ExprStruct(_, ref expr_fields, _) => {
542             let type_ = tcx.expr_ty(e);
543             match type_.sty {
544                 ty::TyStruct(def, _) => {
545                     // check the stability of each field that appears
546                     // in the construction expression.
547                     for field in expr_fields {
548                         let did = def.struct_variant()
549                             .field_named(field.name.node)
550                             .did;
551                         maybe_do_stability_check(tcx, did, field.span, cb);
552                     }
553
554                     // we're done.
555                     return
556                 }
557                 // we don't look at stability attributes on
558                 // struct-like enums (yet...), but it's definitely not
559                 // a bug to have construct one.
560                 ty::TyEnum(..) => return,
561                 _ => {
562                     tcx.sess.span_bug(e.span,
563                                       &format!("stability::check_expr: struct construction \
564                                                 of non-struct, type {:?}",
565                                                type_));
566                 }
567             }
568         }
569         _ => return
570     };
571
572     maybe_do_stability_check(tcx, id, span, cb);
573 }
574
575 pub fn check_path(tcx: &ty::ctxt, path: &hir::Path, id: ast::NodeId,
576                   cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) {
577     match tcx.def_map.borrow().get(&id).map(|d| d.full_def()) {
578         Some(def::DefPrimTy(..)) => {}
579         Some(def::DefSelfTy(..)) => {}
580         Some(def) => {
581             maybe_do_stability_check(tcx, def.def_id(), path.span, cb);
582         }
583         None => {}
584     }
585 }
586
587 pub fn check_path_list_item(tcx: &ty::ctxt, item: &hir::PathListItem,
588                   cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) {
589     match tcx.def_map.borrow().get(&item.node.id()).map(|d| d.full_def()) {
590         Some(def::DefPrimTy(..)) => {}
591         Some(def) => {
592             maybe_do_stability_check(tcx, def.def_id(), item.span, cb);
593         }
594         None => {}
595     }
596 }
597
598 pub fn check_pat(tcx: &ty::ctxt, pat: &hir::Pat,
599                  cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) {
600     debug!("check_pat(pat = {:?})", pat);
601     if is_internal(tcx, pat.span) { return; }
602
603     let v = match tcx.pat_ty_opt(pat) {
604         Some(&ty::TyS { sty: ty::TyStruct(def, _), .. }) => def.struct_variant(),
605         Some(_) | None => return,
606     };
607     match pat.node {
608         // Foo(a, b, c)
609         // A Variant(..) pattern `hir::PatEnum(_, None)` doesn't have to be recursed into.
610         hir::PatEnum(_, Some(ref pat_fields)) => {
611             for (field, struct_field) in pat_fields.iter().zip(&v.fields) {
612                 maybe_do_stability_check(tcx, struct_field.did, field.span, cb)
613             }
614         }
615         // Foo { a, b, c }
616         hir::PatStruct(_, ref pat_fields, _) => {
617             for field in pat_fields {
618                 let did = v.field_named(field.node.name).did;
619                 maybe_do_stability_check(tcx, did, field.span, cb);
620             }
621         }
622         // everything else is fine.
623         _ => {}
624     }
625 }
626
627 fn maybe_do_stability_check(tcx: &ty::ctxt, id: DefId, span: Span,
628                             cb: &mut FnMut(DefId, Span,
629                                            &Option<&Stability>, &Option<Deprecation>)) {
630     if is_internal(tcx, span) {
631         debug!("maybe_do_stability_check: \
632                 skipping span={:?} since it is internal", span);
633         return;
634     }
635     let (stability, deprecation) = if is_staged_api(tcx, id) {
636         (lookup_stability(tcx, id), None)
637     } else {
638         (None, lookup_deprecation(tcx, id))
639     };
640     debug!("maybe_do_stability_check: \
641             inspecting id={:?} span={:?} of stability={:?}", id, span, stability);
642     cb(id, span, &stability, &deprecation);
643 }
644
645 fn is_internal(tcx: &ty::ctxt, span: Span) -> bool {
646     tcx.sess.codemap().span_allows_unstable(span)
647 }
648
649 fn is_staged_api(tcx: &ty::ctxt, id: DefId) -> bool {
650     match tcx.trait_item_of_item(id) {
651         Some(ty::MethodTraitItemId(trait_method_id))
652             if trait_method_id != id => {
653                 is_staged_api(tcx, trait_method_id)
654             }
655         _ => {
656             *tcx.stability.borrow_mut().staged_api.entry(id.krate).or_insert_with(
657                 || tcx.sess.cstore.is_staged_api(id.krate))
658         }
659     }
660 }
661
662 /// Lookup the stability for a node, loading external crate
663 /// metadata as necessary.
664 pub fn lookup_stability<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> {
665     if let Some(st) = tcx.stability.borrow().stab_map.get(&id) {
666         return *st;
667     }
668
669     let st = lookup_stability_uncached(tcx, id);
670     tcx.stability.borrow_mut().stab_map.insert(id, st);
671     st
672 }
673
674 pub fn lookup_deprecation<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<Deprecation> {
675     if let Some(depr) = tcx.stability.borrow().depr_map.get(&id) {
676         return depr.clone();
677     }
678
679     let depr = lookup_deprecation_uncached(tcx, id);
680     tcx.stability.borrow_mut().depr_map.insert(id, depr.clone());
681     depr
682 }
683
684 fn lookup_stability_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> {
685     debug!("lookup(id={:?})", id);
686     if id.is_local() {
687         None // The stability cache is filled partially lazily
688     } else {
689         tcx.sess.cstore.stability(id).map(|st| tcx.intern_stability(st))
690     }
691 }
692
693 fn lookup_deprecation_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<Deprecation> {
694     debug!("lookup(id={:?})", id);
695     if id.is_local() {
696         None // The stability cache is filled partially lazily
697     } else {
698         tcx.sess.cstore.deprecation(id)
699     }
700 }
701
702 /// Given the list of enabled features that were not language features (i.e. that
703 /// were expected to be library features), and the list of features used from
704 /// libraries, identify activated features that don't exist and error about them.
705 pub fn check_unused_or_stable_features(sess: &Session,
706                                        lib_features_used: &FnvHashMap<InternedString,
707                                                                       StabilityLevel>) {
708     let ref declared_lib_features = sess.features.borrow().declared_lib_features;
709     let mut remaining_lib_features: FnvHashMap<InternedString, Span>
710         = declared_lib_features.clone().into_iter().collect();
711
712     let stable_msg = "this feature is stable. attribute no longer needed";
713
714     for &span in &sess.features.borrow().declared_stable_lang_features {
715         sess.add_lint(lint::builtin::STABLE_FEATURES,
716                       ast::CRATE_NODE_ID,
717                       span,
718                       stable_msg.to_string());
719     }
720
721     for (used_lib_feature, level) in lib_features_used {
722         match remaining_lib_features.remove(used_lib_feature) {
723             Some(span) => {
724                 if *level == Stable {
725                     sess.add_lint(lint::builtin::STABLE_FEATURES,
726                                   ast::CRATE_NODE_ID,
727                                   span,
728                                   stable_msg.to_string());
729                 }
730             }
731             None => ( /* used but undeclared, handled during the previous ast visit */ )
732         }
733     }
734
735     for &span in remaining_lib_features.values() {
736         sess.add_lint(lint::builtin::UNUSED_FEATURES,
737                       ast::CRATE_NODE_ID,
738                       span,
739                       "unused or unknown feature".to_string());
740     }
741 }