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