]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/lib_features.rs
ee8dd9e58b5ff9b2babee032a9575bfad06aeac1
[rust.git] / src / librustc / middle / lib_features.rs
1 // Detecting lib features (i.e., features that are not lang features).
2 //
3 // These are declared using stability attributes (e.g., `#[stable (..)]`
4 // and `#[unstable (..)]`), but are not declared in one single location
5 // (unlike lang features), which means we need to collect them instead.
6
7 use crate::ty::TyCtxt;
8 use crate::hir::intravisit::{self, NestedVisitorMap, Visitor};
9 use syntax::symbol::Symbol;
10 use syntax::ast::{Attribute, MetaItem, MetaItemKind};
11 use syntax_pos::Span;
12 use rustc_data_structures::fx::{FxHashSet, FxHashMap};
13 use rustc_macros::HashStable;
14 use errors::DiagnosticId;
15
16 #[derive(HashStable)]
17 pub struct LibFeatures {
18     // A map from feature to stabilisation version.
19     pub stable: FxHashMap<Symbol, Symbol>,
20     pub unstable: FxHashSet<Symbol>,
21 }
22
23 impl LibFeatures {
24     fn new() -> LibFeatures {
25         LibFeatures {
26             stable: Default::default(),
27             unstable: Default::default(),
28         }
29     }
30
31     pub fn to_vec(&self) -> Vec<(Symbol, Option<Symbol>)> {
32         let mut all_features: Vec<_> = self.stable.iter().map(|(f, s)| (*f, Some(*s)))
33             .chain(self.unstable.iter().map(|f| (*f, None)))
34             .collect();
35         all_features.sort_unstable_by_key(|f| f.0.as_str());
36         all_features
37     }
38 }
39
40 pub struct LibFeatureCollector<'a, 'tcx: 'a> {
41     tcx: TyCtxt<'a, 'tcx, 'tcx>,
42     lib_features: LibFeatures,
43 }
44
45 impl<'a, 'tcx> LibFeatureCollector<'a, 'tcx> {
46     fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> LibFeatureCollector<'a, 'tcx> {
47         LibFeatureCollector {
48             tcx,
49             lib_features: LibFeatures::new(),
50         }
51     }
52
53     fn extract(&self, attr: &Attribute) -> Option<(Symbol, Option<Symbol>, Span)> {
54         let stab_attrs = vec!["stable", "unstable", "rustc_const_unstable"];
55
56         // Find a stability attribute (i.e., `#[stable (..)]`, `#[unstable (..)]`,
57         // `#[rustc_const_unstable (..)]`).
58         if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| {
59             attr.check_name(stab_attr)
60         }) {
61             let meta_item = attr.meta();
62             if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta_item {
63                 let mut feature = None;
64                 let mut since = None;
65                 for meta in metas {
66                     if let Some(mi) = meta.meta_item() {
67                         // Find the `feature = ".."` meta-item.
68                         match (mi.name_or_empty().get(), mi.value_str()) {
69                             ("feature", val) => feature = val,
70                             ("since", val) => since = val,
71                             _ => {}
72                         }
73                     }
74                 }
75                 if let Some(feature) = feature {
76                     // This additional check for stability is to make sure we
77                     // don't emit additional, irrelevant errors for malformed
78                     // attributes.
79                     if *stab_attr != "stable" || since.is_some() {
80                         return Some((feature, since, attr.span));
81                     }
82                 }
83                 // We need to iterate over the other attributes, because
84                 // `rustc_const_unstable` is not mutually exclusive with
85                 // the other stability attributes, so we can't just `break`
86                 // here.
87             }
88         }
89
90         None
91     }
92
93     fn collect_feature(&mut self, feature: Symbol, since: Option<Symbol>, span: Span) {
94         let already_in_stable = self.lib_features.stable.contains_key(&feature);
95         let already_in_unstable = self.lib_features.unstable.contains(&feature);
96
97         match (since, already_in_stable, already_in_unstable) {
98             (Some(since), _, false) => {
99                 if let Some(prev_since) = self.lib_features.stable.get(&feature) {
100                     if *prev_since != since {
101                         let msg = format!(
102                             "feature `{}` is declared stable since {}, \
103                              but was previously declared stable since {}",
104                             feature,
105                             since,
106                             prev_since,
107                         );
108                         self.tcx.sess.struct_span_err_with_code(span, &msg,
109                             DiagnosticId::Error("E0711".into())).emit();
110                         return;
111                     }
112                 }
113
114                 self.lib_features.stable.insert(feature, since);
115             }
116             (None, false, _) => {
117                 self.lib_features.unstable.insert(feature);
118             }
119             (Some(_), _, true) | (None, true, _) => {
120                 let msg = format!(
121                     "feature `{}` is declared {}, but was previously declared {}",
122                     feature,
123                     if since.is_some() { "stable" } else { "unstable" },
124                     if since.is_none() { "stable" } else { "unstable" },
125                 );
126                 self.tcx.sess.struct_span_err_with_code(span, &msg,
127                     DiagnosticId::Error("E0711".into())).emit();
128             }
129         }
130     }
131 }
132
133 impl<'a, 'tcx> Visitor<'tcx> for LibFeatureCollector<'a, 'tcx> {
134     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
135         NestedVisitorMap::All(&self.tcx.hir())
136     }
137
138     fn visit_attribute(&mut self, attr: &'tcx Attribute) {
139         if let Some((feature, stable, span)) = self.extract(attr) {
140             self.collect_feature(feature, stable, span);
141         }
142     }
143 }
144
145 pub fn collect<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> LibFeatures {
146     let mut collector = LibFeatureCollector::new(tcx);
147     intravisit::walk_crate(&mut collector, tcx.hir().krate());
148     collector.lib_features
149 }