]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | //! Detecting lib features (i.e., features that are not lang features). |
2 | //! | |
3 | //! These are declared using stability attributes (e.g., `#[stable (..)]` and `#[unstable (..)]`), | |
4 | //! but are not declared in one single location (unlike lang features), which means we need to | |
5 | //! collect them instead. | |
b7449926 | 6 | |
a2a8927a | 7 | use rustc_ast::{Attribute, MetaItemKind}; |
f2b60f7d | 8 | use rustc_attr::{rust_version_symbol, VERSION_PLACEHOLDER}; |
5099ac24 FG |
9 | use rustc_hir::intravisit::Visitor; |
10 | use rustc_middle::hir::nested_filter; | |
ba9703b0 XL |
11 | use rustc_middle::middle::lib_features::LibFeatures; |
12 | use rustc_middle::ty::query::Providers; | |
13 | use rustc_middle::ty::TyCtxt; | |
dfeec247 XL |
14 | use rustc_span::symbol::Symbol; |
15 | use rustc_span::{sym, Span}; | |
60c5eb7d | 16 | |
2b03887a FG |
17 | use crate::errors::{FeaturePreviouslyDeclared, FeatureStableTwice}; |
18 | ||
dfeec247 XL |
19 | fn new_lib_features() -> LibFeatures { |
20 | LibFeatures { stable: Default::default(), unstable: Default::default() } | |
b7449926 XL |
21 | } |
22 | ||
dc9dc135 XL |
23 | pub struct LibFeatureCollector<'tcx> { |
24 | tcx: TyCtxt<'tcx>, | |
b7449926 XL |
25 | lib_features: LibFeatures, |
26 | } | |
27 | ||
a2a8927a | 28 | impl<'tcx> LibFeatureCollector<'tcx> { |
dc9dc135 | 29 | fn new(tcx: TyCtxt<'tcx>) -> LibFeatureCollector<'tcx> { |
dfeec247 | 30 | LibFeatureCollector { tcx, lib_features: new_lib_features() } |
b7449926 XL |
31 | } |
32 | ||
33 | fn extract(&self, attr: &Attribute) -> Option<(Symbol, Option<Symbol>, Span)> { | |
f2b60f7d FG |
34 | let stab_attrs = [ |
35 | sym::stable, | |
36 | sym::unstable, | |
37 | sym::rustc_const_stable, | |
38 | sym::rustc_const_unstable, | |
39 | sym::rustc_default_body_unstable, | |
40 | ]; | |
b7449926 | 41 | |
5e7ed085 | 42 | // Find a stability attribute: one of #[stable(…)], #[unstable(…)], |
f2b60f7d | 43 | // #[rustc_const_stable(…)], #[rustc_const_unstable(…)] or #[rustc_default_body_unstable]. |
94222f64 | 44 | if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| attr.has_name(**stab_attr)) { |
a2a8927a XL |
45 | let meta_kind = attr.meta_kind(); |
46 | if let Some(MetaItemKind::List(ref metas)) = meta_kind { | |
b7449926 XL |
47 | let mut feature = None; |
48 | let mut since = None; | |
49 | for meta in metas { | |
50 | if let Some(mi) = meta.meta_item() { | |
51 | // Find the `feature = ".."` meta-item. | |
48663c56 XL |
52 | match (mi.name_or_empty(), mi.value_str()) { |
53 | (sym::feature, val) => feature = val, | |
54 | (sym::since, val) => since = val, | |
b7449926 XL |
55 | _ => {} |
56 | } | |
57 | } | |
58 | } | |
f2b60f7d FG |
59 | |
60 | if let Some(s) = since && s.as_str() == VERSION_PLACEHOLDER { | |
61 | since = Some(rust_version_symbol()); | |
62 | } | |
63 | ||
b7449926 XL |
64 | if let Some(feature) = feature { |
65 | // This additional check for stability is to make sure we | |
66 | // don't emit additional, irrelevant errors for malformed | |
67 | // attributes. | |
f2b60f7d FG |
68 | let is_unstable = matches!( |
69 | *stab_attr, | |
70 | sym::unstable | |
71 | | sym::rustc_const_unstable | |
72 | | sym::rustc_default_body_unstable | |
73 | ); | |
5e7ed085 | 74 | if since.is_some() || is_unstable { |
b7449926 XL |
75 | return Some((feature, since, attr.span)); |
76 | } | |
77 | } | |
78 | // We need to iterate over the other attributes, because | |
79 | // `rustc_const_unstable` is not mutually exclusive with | |
80 | // the other stability attributes, so we can't just `break` | |
81 | // here. | |
82 | } | |
83 | } | |
84 | ||
85 | None | |
86 | } | |
87 | ||
88 | fn collect_feature(&mut self, feature: Symbol, since: Option<Symbol>, span: Span) { | |
89 | let already_in_stable = self.lib_features.stable.contains_key(&feature); | |
064997fb | 90 | let already_in_unstable = self.lib_features.unstable.contains_key(&feature); |
b7449926 XL |
91 | |
92 | match (since, already_in_stable, already_in_unstable) { | |
93 | (Some(since), _, false) => { | |
064997fb | 94 | if let Some((prev_since, _)) = self.lib_features.stable.get(&feature) { |
b7449926 | 95 | if *prev_since != since { |
2b03887a | 96 | self.tcx.sess.emit_err(FeatureStableTwice { |
60c5eb7d | 97 | span, |
2b03887a FG |
98 | feature, |
99 | since, | |
100 | prev_since: *prev_since, | |
101 | }); | |
b7449926 XL |
102 | return; |
103 | } | |
104 | } | |
105 | ||
064997fb | 106 | self.lib_features.stable.insert(feature, (since, span)); |
b7449926 XL |
107 | } |
108 | (None, false, _) => { | |
064997fb | 109 | self.lib_features.unstable.insert(feature, span); |
b7449926 XL |
110 | } |
111 | (Some(_), _, true) | (None, true, _) => { | |
2b03887a FG |
112 | let declared = if since.is_some() { "stable" } else { "unstable" }; |
113 | let prev_declared = if since.is_none() { "stable" } else { "unstable" }; | |
114 | self.tcx.sess.emit_err(FeaturePreviouslyDeclared { | |
60c5eb7d | 115 | span, |
2b03887a FG |
116 | feature, |
117 | declared, | |
118 | prev_declared, | |
119 | }); | |
b7449926 XL |
120 | } |
121 | } | |
122 | } | |
123 | } | |
124 | ||
a2a8927a | 125 | impl<'tcx> Visitor<'tcx> for LibFeatureCollector<'tcx> { |
5099ac24 | 126 | type NestedFilter = nested_filter::All; |
dfeec247 | 127 | |
5099ac24 FG |
128 | fn nested_visit_map(&mut self) -> Self::Map { |
129 | self.tcx.hir() | |
b7449926 XL |
130 | } |
131 | ||
923072b8 | 132 | fn visit_attribute(&mut self, attr: &'tcx Attribute) { |
b7449926 XL |
133 | if let Some((feature, stable, span)) = self.extract(attr) { |
134 | self.collect_feature(feature, stable, span); | |
135 | } | |
136 | } | |
137 | } | |
138 | ||
a2a8927a | 139 | fn lib_features(tcx: TyCtxt<'_>, (): ()) -> LibFeatures { |
9c376795 FG |
140 | // If `staged_api` is not enabled then we aren't allowed to define lib |
141 | // features; there is no point collecting them. | |
142 | if !tcx.features().staged_api { | |
143 | return new_lib_features(); | |
144 | } | |
145 | ||
b7449926 | 146 | let mut collector = LibFeatureCollector::new(tcx); |
c295e0f8 | 147 | tcx.hir().walk_attributes(&mut collector); |
b7449926 XL |
148 | collector.lib_features |
149 | } | |
dfeec247 | 150 | |
f035d41b | 151 | pub fn provide(providers: &mut Providers) { |
a2a8927a | 152 | providers.lib_features = lib_features; |
dfeec247 | 153 | } |