1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use rustc_ast::{Attribute, MetaItem, MetaItemKind};
use rustc_errors::struct_span_err;
use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
use rustc_middle::hir::map::Map;
use rustc_middle::middle::lib_features::LibFeatures;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::Symbol;
use rustc_span::{sym, Span};
fn new_lib_features() -> LibFeatures {
LibFeatures { stable: Default::default(), unstable: Default::default() }
}
pub struct LibFeatureCollector<'tcx> {
tcx: TyCtxt<'tcx>,
lib_features: LibFeatures,
}
impl LibFeatureCollector<'tcx> {
fn new(tcx: TyCtxt<'tcx>) -> LibFeatureCollector<'tcx> {
LibFeatureCollector { tcx, lib_features: new_lib_features() }
}
fn extract(&self, attr: &Attribute) -> Option<(Symbol, Option<Symbol>, Span)> {
let stab_attrs = [sym::stable, sym::unstable, sym::rustc_const_unstable];
if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| attr.has_name(**stab_attr)) {
let meta_item = attr.meta();
if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta_item {
let mut feature = None;
let mut since = None;
for meta in metas {
if let Some(mi) = meta.meta_item() {
match (mi.name_or_empty(), mi.value_str()) {
(sym::feature, val) => feature = val,
(sym::since, val) => since = val,
_ => {}
}
}
}
if let Some(feature) = feature {
if *stab_attr != sym::stable || since.is_some() {
return Some((feature, since, attr.span));
}
}
}
}
None
}
fn collect_feature(&mut self, feature: Symbol, since: Option<Symbol>, span: Span) {
let already_in_stable = self.lib_features.stable.contains_key(&feature);
let already_in_unstable = self.lib_features.unstable.contains(&feature);
match (since, already_in_stable, already_in_unstable) {
(Some(since), _, false) => {
if let Some(prev_since) = self.lib_features.stable.get(&feature) {
if *prev_since != since {
self.span_feature_error(
span,
&format!(
"feature `{}` is declared stable since {}, \
but was previously declared stable since {}",
feature, since, prev_since,
),
);
return;
}
}
self.lib_features.stable.insert(feature, since);
}
(None, false, _) => {
self.lib_features.unstable.insert(feature);
}
(Some(_), _, true) | (None, true, _) => {
self.span_feature_error(
span,
&format!(
"feature `{}` is declared {}, but was previously declared {}",
feature,
if since.is_some() { "stable" } else { "unstable" },
if since.is_none() { "stable" } else { "unstable" },
),
);
}
}
}
fn span_feature_error(&self, span: Span, msg: &str) {
struct_span_err!(self.tcx.sess, span, E0711, "{}", &msg).emit();
}
}
impl Visitor<'tcx> for LibFeatureCollector<'tcx> {
type Map = Map<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::All(self.tcx.hir())
}
fn visit_attribute(&mut self, _: rustc_hir::HirId, attr: &'tcx Attribute) {
if let Some((feature, stable, span)) = self.extract(attr) {
self.collect_feature(feature, stable, span);
}
}
}
fn get_lib_features(tcx: TyCtxt<'_>, (): ()) -> LibFeatures {
let mut collector = LibFeatureCollector::new(tcx);
tcx.hir().walk_attributes(&mut collector);
collector.lib_features
}
pub fn provide(providers: &mut Providers) {
providers.get_lib_features = get_lib_features;
}