]>
Commit | Line | Data |
---|---|---|
dfeec247 XL |
1 | //! A pass that annotates every item and method with its stability level, |
2 | //! propagating default levels lexically from parent to children ast nodes. | |
3 | ||
74b04a01 XL |
4 | use rustc_ast::ast::Attribute; |
5 | use rustc_attr::{self as attr, ConstStability, Stability}; | |
dfeec247 XL |
6 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
7 | use rustc_errors::struct_span_err; | |
8 | use rustc_hir as hir; | |
9 | use rustc_hir::def::{DefKind, Res}; | |
f035d41b | 10 | use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; |
dfeec247 XL |
11 | use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; |
12 | use rustc_hir::{Generics, HirId, Item, StructField, Variant}; | |
ba9703b0 XL |
13 | use rustc_middle::hir::map::Map; |
14 | use rustc_middle::middle::privacy::AccessLevels; | |
15 | use rustc_middle::middle::stability::{DeprecationEntry, Index}; | |
16 | use rustc_middle::ty::query::Providers; | |
17 | use rustc_middle::ty::TyCtxt; | |
18 | use rustc_session::lint; | |
19 | use rustc_session::parse::feature_err; | |
20 | use rustc_session::Session; | |
dfeec247 XL |
21 | use rustc_span::symbol::{sym, Symbol}; |
22 | use rustc_span::Span; | |
ba9703b0 | 23 | use rustc_trait_selection::traits::misc::can_type_implement_copy; |
dfeec247 XL |
24 | |
25 | use std::cmp::Ordering; | |
26 | use std::mem::replace; | |
27 | use std::num::NonZeroU32; | |
28 | ||
29 | #[derive(PartialEq)] | |
30 | enum AnnotationKind { | |
31 | // Annotation is required if not inherited from unstable parents | |
32 | Required, | |
33 | // Annotation is useless, reject it | |
34 | Prohibited, | |
35 | // Annotation itself is useless, but it can be propagated to children | |
36 | Container, | |
37 | } | |
38 | ||
39 | // A private tree-walker for producing an Index. | |
40 | struct Annotator<'a, 'tcx> { | |
41 | tcx: TyCtxt<'tcx>, | |
42 | index: &'a mut Index<'tcx>, | |
43 | parent_stab: Option<&'tcx Stability>, | |
74b04a01 | 44 | parent_const_stab: Option<&'tcx ConstStability>, |
dfeec247 XL |
45 | parent_depr: Option<DeprecationEntry>, |
46 | in_trait_impl: bool, | |
47 | } | |
48 | ||
49 | impl<'a, 'tcx> Annotator<'a, 'tcx> { | |
50 | // Determine the stability for a node based on its attributes and inherited | |
51 | // stability. The stability is recorded in the index and used as the parent. | |
52 | fn annotate<F>( | |
53 | &mut self, | |
54 | hir_id: HirId, | |
55 | attrs: &[Attribute], | |
56 | item_sp: Span, | |
57 | kind: AnnotationKind, | |
58 | visit_children: F, | |
59 | ) where | |
60 | F: FnOnce(&mut Self), | |
61 | { | |
74b04a01 XL |
62 | if !self.tcx.features().staged_api { |
63 | self.forbid_staged_api_attrs(hir_id, attrs, item_sp, kind, visit_children); | |
64 | return; | |
65 | } | |
66 | ||
67 | // This crate explicitly wants staged API. | |
68 | ||
69 | debug!("annotate(id = {:?}, attrs = {:?})", hir_id, attrs); | |
70 | if let Some(..) = attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp) { | |
71 | self.tcx.sess.span_err( | |
72 | item_sp, | |
73 | "`#[deprecated]` cannot be used in staged API; \ | |
74 | use `#[rustc_deprecated]` instead", | |
75 | ); | |
76 | } | |
77 | ||
78 | let (stab, const_stab) = attr::find_stability(&self.tcx.sess.parse_sess, attrs, item_sp); | |
79 | ||
80 | let const_stab = const_stab.map(|const_stab| { | |
81 | let const_stab = self.tcx.intern_const_stability(const_stab); | |
82 | self.index.const_stab_map.insert(hir_id, const_stab); | |
83 | const_stab | |
84 | }); | |
85 | ||
86 | if const_stab.is_none() { | |
87 | debug!("annotate: const_stab not found, parent = {:?}", self.parent_const_stab); | |
88 | if let Some(parent) = self.parent_const_stab { | |
89 | if parent.level.is_unstable() { | |
90 | self.index.const_stab_map.insert(hir_id, parent); | |
91 | } | |
dfeec247 | 92 | } |
74b04a01 XL |
93 | } |
94 | ||
95 | let stab = stab.map(|mut stab| { | |
96 | // Error if prohibited, or can't inherit anything from a container. | |
97 | if kind == AnnotationKind::Prohibited | |
98 | || (kind == AnnotationKind::Container | |
99 | && stab.level.is_stable() | |
100 | && stab.rustc_depr.is_none()) | |
101 | { | |
102 | self.tcx.sess.span_err(item_sp, "This stability annotation is useless"); | |
dfeec247 | 103 | } |
dfeec247 | 104 | |
74b04a01 XL |
105 | debug!("annotate: found {:?}", stab); |
106 | // If parent is deprecated and we're not, inherit this by merging | |
107 | // deprecated_since and its reason. | |
108 | if let Some(parent_stab) = self.parent_stab { | |
109 | if parent_stab.rustc_depr.is_some() && stab.rustc_depr.is_none() { | |
110 | stab.rustc_depr = parent_stab.rustc_depr | |
dfeec247 | 111 | } |
74b04a01 | 112 | } |
dfeec247 | 113 | |
74b04a01 XL |
114 | let stab = self.tcx.intern_stability(stab); |
115 | ||
116 | // Check if deprecated_since < stable_since. If it is, | |
117 | // this is *almost surely* an accident. | |
118 | if let ( | |
119 | &Some(attr::RustcDeprecation { since: dep_since, .. }), | |
120 | &attr::Stable { since: stab_since }, | |
121 | ) = (&stab.rustc_depr, &stab.level) | |
122 | { | |
123 | // Explicit version of iter::order::lt to handle parse errors properly | |
124 | for (dep_v, stab_v) in | |
125 | dep_since.as_str().split('.').zip(stab_since.as_str().split('.')) | |
dfeec247 | 126 | { |
74b04a01 XL |
127 | if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::<u64>(), stab_v.parse()) { |
128 | match dep_v.cmp(&stab_v) { | |
129 | Ordering::Less => { | |
130 | self.tcx.sess.span_err( | |
131 | item_sp, | |
132 | "An API can't be stabilized \ | |
133 | after it is deprecated", | |
134 | ); | |
135 | break; | |
dfeec247 | 136 | } |
74b04a01 XL |
137 | Ordering::Equal => continue, |
138 | Ordering::Greater => break, | |
dfeec247 | 139 | } |
74b04a01 XL |
140 | } else { |
141 | // Act like it isn't less because the question is now nonsensical, | |
142 | // and this makes us not do anything else interesting. | |
143 | self.tcx.sess.span_err( | |
144 | item_sp, | |
145 | "Invalid stability or deprecation \ | |
146 | version found", | |
147 | ); | |
148 | break; | |
dfeec247 XL |
149 | } |
150 | } | |
dfeec247 XL |
151 | } |
152 | ||
74b04a01 XL |
153 | self.index.stab_map.insert(hir_id, stab); |
154 | stab | |
155 | }); | |
156 | ||
157 | if stab.is_none() { | |
158 | debug!("annotate: stab not found, parent = {:?}", self.parent_stab); | |
dfeec247 XL |
159 | if let Some(stab) = self.parent_stab { |
160 | if stab.level.is_unstable() { | |
161 | self.index.stab_map.insert(hir_id, stab); | |
162 | } | |
163 | } | |
74b04a01 | 164 | } |
dfeec247 | 165 | |
74b04a01 XL |
166 | self.recurse_with_stability_attrs(stab, const_stab, visit_children); |
167 | } | |
168 | ||
169 | fn recurse_with_stability_attrs( | |
170 | &mut self, | |
171 | stab: Option<&'tcx Stability>, | |
172 | const_stab: Option<&'tcx ConstStability>, | |
173 | f: impl FnOnce(&mut Self), | |
174 | ) { | |
175 | // These will be `Some` if this item changes the corresponding stability attribute. | |
176 | let mut replaced_parent_stab = None; | |
177 | let mut replaced_parent_const_stab = None; | |
178 | ||
179 | if let Some(stab) = stab { | |
180 | replaced_parent_stab = Some(replace(&mut self.parent_stab, Some(stab))); | |
181 | } | |
182 | if let Some(const_stab) = const_stab { | |
183 | replaced_parent_const_stab = | |
184 | Some(replace(&mut self.parent_const_stab, Some(const_stab))); | |
185 | } | |
186 | ||
187 | f(self); | |
188 | ||
189 | if let Some(orig_parent_stab) = replaced_parent_stab { | |
190 | self.parent_stab = orig_parent_stab; | |
191 | } | |
192 | if let Some(orig_parent_const_stab) = replaced_parent_const_stab { | |
193 | self.parent_const_stab = orig_parent_const_stab; | |
194 | } | |
195 | } | |
196 | ||
197 | fn forbid_staged_api_attrs( | |
198 | &mut self, | |
199 | hir_id: HirId, | |
200 | attrs: &[Attribute], | |
201 | item_sp: Span, | |
202 | kind: AnnotationKind, | |
203 | visit_children: impl FnOnce(&mut Self), | |
204 | ) { | |
205 | // Emit errors for non-staged-api crates. | |
206 | let unstable_attrs = [ | |
207 | sym::unstable, | |
208 | sym::stable, | |
209 | sym::rustc_deprecated, | |
210 | sym::rustc_const_unstable, | |
211 | sym::rustc_const_stable, | |
212 | ]; | |
213 | for attr in attrs { | |
214 | let name = attr.name_or_empty(); | |
215 | if unstable_attrs.contains(&name) { | |
216 | attr::mark_used(attr); | |
217 | struct_span_err!( | |
218 | self.tcx.sess, | |
219 | attr.span, | |
220 | E0734, | |
221 | "stability attributes may not be used outside of the standard library", | |
222 | ) | |
223 | .emit(); | |
224 | } | |
225 | } | |
226 | ||
227 | // Propagate unstability. This can happen even for non-staged-api crates in case | |
228 | // -Zforce-unstable-if-unmarked is set. | |
229 | if let Some(stab) = self.parent_stab { | |
230 | if stab.level.is_unstable() { | |
231 | self.index.stab_map.insert(hir_id, stab); | |
232 | } | |
233 | } | |
dfeec247 | 234 | |
74b04a01 XL |
235 | if let Some(depr) = attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp) { |
236 | if kind == AnnotationKind::Prohibited { | |
237 | self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless"); | |
dfeec247 | 238 | } |
74b04a01 XL |
239 | |
240 | // `Deprecation` is just two pointers, no need to intern it | |
241 | let depr_entry = DeprecationEntry::local(depr, hir_id); | |
242 | self.index.depr_map.insert(hir_id, depr_entry.clone()); | |
243 | ||
244 | let orig_parent_depr = replace(&mut self.parent_depr, Some(depr_entry)); | |
245 | visit_children(self); | |
246 | self.parent_depr = orig_parent_depr; | |
247 | } else if let Some(parent_depr) = self.parent_depr.clone() { | |
248 | self.index.depr_map.insert(hir_id, parent_depr); | |
249 | visit_children(self); | |
250 | } else { | |
251 | visit_children(self); | |
dfeec247 XL |
252 | } |
253 | } | |
254 | } | |
255 | ||
256 | impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { | |
257 | /// Because stability levels are scoped lexically, we want to walk | |
258 | /// nested items in the context of the outer item, so enable | |
259 | /// deep-walking. | |
260 | type Map = Map<'tcx>; | |
261 | ||
ba9703b0 XL |
262 | fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { |
263 | NestedVisitorMap::All(self.tcx.hir()) | |
dfeec247 XL |
264 | } |
265 | ||
266 | fn visit_item(&mut self, i: &'tcx Item<'tcx>) { | |
267 | let orig_in_trait_impl = self.in_trait_impl; | |
268 | let mut kind = AnnotationKind::Required; | |
269 | match i.kind { | |
270 | // Inherent impls and foreign modules serve only as containers for other items, | |
271 | // they don't have their own stability. They still can be annotated as unstable | |
272 | // and propagate this unstability to children, but this annotation is completely | |
273 | // optional. They inherit stability from their parents when unannotated. | |
274 | hir::ItemKind::Impl { of_trait: None, .. } | hir::ItemKind::ForeignMod(..) => { | |
275 | self.in_trait_impl = false; | |
276 | kind = AnnotationKind::Container; | |
277 | } | |
278 | hir::ItemKind::Impl { of_trait: Some(_), .. } => { | |
279 | self.in_trait_impl = true; | |
280 | } | |
281 | hir::ItemKind::Struct(ref sd, _) => { | |
282 | if let Some(ctor_hir_id) = sd.ctor_hir_id() { | |
283 | self.annotate(ctor_hir_id, &i.attrs, i.span, AnnotationKind::Required, |_| {}) | |
284 | } | |
285 | } | |
286 | _ => {} | |
287 | } | |
288 | ||
289 | self.annotate(i.hir_id, &i.attrs, i.span, kind, |v| intravisit::walk_item(v, i)); | |
290 | self.in_trait_impl = orig_in_trait_impl; | |
291 | } | |
292 | ||
293 | fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) { | |
294 | self.annotate(ti.hir_id, &ti.attrs, ti.span, AnnotationKind::Required, |v| { | |
295 | intravisit::walk_trait_item(v, ti); | |
296 | }); | |
297 | } | |
298 | ||
299 | fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { | |
300 | let kind = | |
301 | if self.in_trait_impl { AnnotationKind::Prohibited } else { AnnotationKind::Required }; | |
302 | self.annotate(ii.hir_id, &ii.attrs, ii.span, kind, |v| { | |
303 | intravisit::walk_impl_item(v, ii); | |
304 | }); | |
305 | } | |
306 | ||
307 | fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) { | |
308 | self.annotate(var.id, &var.attrs, var.span, AnnotationKind::Required, |v| { | |
309 | if let Some(ctor_hir_id) = var.data.ctor_hir_id() { | |
310 | v.annotate(ctor_hir_id, &var.attrs, var.span, AnnotationKind::Required, |_| {}); | |
311 | } | |
312 | ||
313 | intravisit::walk_variant(v, var, g, item_id) | |
314 | }) | |
315 | } | |
316 | ||
317 | fn visit_struct_field(&mut self, s: &'tcx StructField<'tcx>) { | |
318 | self.annotate(s.hir_id, &s.attrs, s.span, AnnotationKind::Required, |v| { | |
319 | intravisit::walk_struct_field(v, s); | |
320 | }); | |
321 | } | |
322 | ||
323 | fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) { | |
324 | self.annotate(i.hir_id, &i.attrs, i.span, AnnotationKind::Required, |v| { | |
325 | intravisit::walk_foreign_item(v, i); | |
326 | }); | |
327 | } | |
328 | ||
329 | fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) { | |
330 | self.annotate(md.hir_id, &md.attrs, md.span, AnnotationKind::Required, |_| {}); | |
331 | } | |
332 | } | |
333 | ||
f035d41b | 334 | struct MissingStabilityAnnotations<'tcx> { |
dfeec247 | 335 | tcx: TyCtxt<'tcx>, |
f035d41b | 336 | access_levels: &'tcx AccessLevels, |
dfeec247 XL |
337 | } |
338 | ||
f035d41b | 339 | impl<'tcx> MissingStabilityAnnotations<'tcx> { |
f9f354fc | 340 | fn check_missing_stability(&self, hir_id: HirId, span: Span) { |
dfeec247 XL |
341 | let stab = self.tcx.stability().local_stability(hir_id); |
342 | let is_error = | |
343 | !self.tcx.sess.opts.test && stab.is_none() && self.access_levels.is_reachable(hir_id); | |
344 | if is_error { | |
f9f354fc XL |
345 | let def_id = self.tcx.hir().local_def_id(hir_id); |
346 | let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id()); | |
347 | self.tcx.sess.span_err(span, &format!("{} has missing stability attribute", descr)); | |
dfeec247 XL |
348 | } |
349 | } | |
350 | } | |
351 | ||
f035d41b | 352 | impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { |
dfeec247 XL |
353 | type Map = Map<'tcx>; |
354 | ||
ba9703b0 XL |
355 | fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { |
356 | NestedVisitorMap::OnlyBodies(self.tcx.hir()) | |
dfeec247 XL |
357 | } |
358 | ||
359 | fn visit_item(&mut self, i: &'tcx Item<'tcx>) { | |
360 | match i.kind { | |
361 | // Inherent impls and foreign modules serve only as containers for other items, | |
362 | // they don't have their own stability. They still can be annotated as unstable | |
363 | // and propagate this unstability to children, but this annotation is completely | |
364 | // optional. They inherit stability from their parents when unannotated. | |
365 | hir::ItemKind::Impl { of_trait: None, .. } | hir::ItemKind::ForeignMod(..) => {} | |
366 | ||
f9f354fc | 367 | _ => self.check_missing_stability(i.hir_id, i.span), |
dfeec247 XL |
368 | } |
369 | ||
370 | intravisit::walk_item(self, i) | |
371 | } | |
372 | ||
373 | fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) { | |
f9f354fc | 374 | self.check_missing_stability(ti.hir_id, ti.span); |
dfeec247 XL |
375 | intravisit::walk_trait_item(self, ti); |
376 | } | |
377 | ||
378 | fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { | |
379 | let impl_def_id = self.tcx.hir().local_def_id(self.tcx.hir().get_parent_item(ii.hir_id)); | |
380 | if self.tcx.impl_trait_ref(impl_def_id).is_none() { | |
f9f354fc | 381 | self.check_missing_stability(ii.hir_id, ii.span); |
dfeec247 XL |
382 | } |
383 | intravisit::walk_impl_item(self, ii); | |
384 | } | |
385 | ||
386 | fn visit_variant(&mut self, var: &'tcx Variant<'tcx>, g: &'tcx Generics<'tcx>, item_id: HirId) { | |
f9f354fc | 387 | self.check_missing_stability(var.id, var.span); |
dfeec247 XL |
388 | intravisit::walk_variant(self, var, g, item_id); |
389 | } | |
390 | ||
391 | fn visit_struct_field(&mut self, s: &'tcx StructField<'tcx>) { | |
f9f354fc | 392 | self.check_missing_stability(s.hir_id, s.span); |
dfeec247 XL |
393 | intravisit::walk_struct_field(self, s); |
394 | } | |
395 | ||
396 | fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem<'tcx>) { | |
f9f354fc | 397 | self.check_missing_stability(i.hir_id, i.span); |
dfeec247 XL |
398 | intravisit::walk_foreign_item(self, i); |
399 | } | |
400 | ||
401 | fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) { | |
f9f354fc | 402 | self.check_missing_stability(md.hir_id, md.span); |
dfeec247 XL |
403 | } |
404 | } | |
405 | ||
406 | fn new_index(tcx: TyCtxt<'tcx>) -> Index<'tcx> { | |
407 | let is_staged_api = | |
408 | tcx.sess.opts.debugging_opts.force_unstable_if_unmarked || tcx.features().staged_api; | |
409 | let mut staged_api = FxHashMap::default(); | |
410 | staged_api.insert(LOCAL_CRATE, is_staged_api); | |
411 | let mut index = Index { | |
412 | staged_api, | |
413 | stab_map: Default::default(), | |
414 | const_stab_map: Default::default(), | |
415 | depr_map: Default::default(), | |
416 | active_features: Default::default(), | |
417 | }; | |
418 | ||
419 | let active_lib_features = &tcx.features().declared_lib_features; | |
420 | let active_lang_features = &tcx.features().declared_lang_features; | |
421 | ||
422 | // Put the active features into a map for quick lookup. | |
423 | index.active_features = active_lib_features | |
424 | .iter() | |
425 | .map(|&(s, ..)| s) | |
426 | .chain(active_lang_features.iter().map(|&(s, ..)| s)) | |
427 | .collect(); | |
428 | ||
429 | { | |
430 | let krate = tcx.hir().krate(); | |
431 | let mut annotator = Annotator { | |
432 | tcx, | |
433 | index: &mut index, | |
434 | parent_stab: None, | |
74b04a01 | 435 | parent_const_stab: None, |
dfeec247 XL |
436 | parent_depr: None, |
437 | in_trait_impl: false, | |
438 | }; | |
439 | ||
440 | // If the `-Z force-unstable-if-unmarked` flag is passed then we provide | |
441 | // a parent stability annotation which indicates that this is private | |
442 | // with the `rustc_private` feature. This is intended for use when | |
ba9703b0 | 443 | // compiling `librustc_*` crates themselves so we can leverage crates.io |
dfeec247 XL |
444 | // while maintaining the invariant that all sysroot crates are unstable |
445 | // by default and are unable to be used. | |
446 | if tcx.sess.opts.debugging_opts.force_unstable_if_unmarked { | |
447 | let reason = "this crate is being loaded from the sysroot, an \ | |
448 | unstable location; did you mean to load this crate \ | |
449 | from crates.io via `Cargo.toml` instead?"; | |
450 | let stability = tcx.intern_stability(Stability { | |
451 | level: attr::StabilityLevel::Unstable { | |
452 | reason: Some(Symbol::intern(reason)), | |
453 | issue: NonZeroU32::new(27812), | |
454 | is_soft: false, | |
455 | }, | |
456 | feature: sym::rustc_private, | |
457 | rustc_depr: None, | |
458 | }); | |
459 | annotator.parent_stab = Some(stability); | |
460 | } | |
461 | ||
462 | annotator.annotate( | |
463 | hir::CRATE_HIR_ID, | |
ba9703b0 XL |
464 | &krate.item.attrs, |
465 | krate.item.span, | |
dfeec247 XL |
466 | AnnotationKind::Required, |
467 | |v| intravisit::walk_crate(v, krate), | |
468 | ); | |
469 | } | |
ba9703b0 | 470 | index |
dfeec247 XL |
471 | } |
472 | ||
473 | /// Cross-references the feature names of unstable APIs with enabled | |
474 | /// features and possibly prints errors. | |
f035d41b | 475 | fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { |
dfeec247 XL |
476 | tcx.hir().visit_item_likes_in_module(module_def_id, &mut Checker { tcx }.as_deep_visitor()); |
477 | } | |
478 | ||
f035d41b | 479 | pub(crate) fn provide(providers: &mut Providers) { |
dfeec247 XL |
480 | *providers = Providers { check_mod_unstable_api_usage, ..*providers }; |
481 | providers.stability_index = |tcx, cnum| { | |
482 | assert_eq!(cnum, LOCAL_CRATE); | |
f9f354fc | 483 | new_index(tcx) |
dfeec247 XL |
484 | }; |
485 | } | |
486 | ||
487 | struct Checker<'tcx> { | |
488 | tcx: TyCtxt<'tcx>, | |
489 | } | |
490 | ||
491 | impl Visitor<'tcx> for Checker<'tcx> { | |
492 | type Map = Map<'tcx>; | |
493 | ||
494 | /// Because stability levels are scoped lexically, we want to walk | |
495 | /// nested items in the context of the outer item, so enable | |
496 | /// deep-walking. | |
ba9703b0 XL |
497 | fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { |
498 | NestedVisitorMap::OnlyBodies(self.tcx.hir()) | |
dfeec247 XL |
499 | } |
500 | ||
501 | fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { | |
502 | match item.kind { | |
503 | hir::ItemKind::ExternCrate(_) => { | |
504 | // compiler-generated `extern crate` items have a dummy span. | |
505 | if item.span.is_dummy() { | |
506 | return; | |
507 | } | |
508 | ||
509 | let def_id = self.tcx.hir().local_def_id(item.hir_id); | |
510 | let cnum = match self.tcx.extern_mod_stmt_cnum(def_id) { | |
511 | Some(cnum) => cnum, | |
512 | None => return, | |
513 | }; | |
514 | let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX }; | |
515 | self.tcx.check_stability(def_id, Some(item.hir_id), item.span); | |
516 | } | |
517 | ||
518 | // For implementations of traits, check the stability of each item | |
519 | // individually as it's possible to have a stable trait with unstable | |
520 | // items. | |
521 | hir::ItemKind::Impl { of_trait: Some(ref t), items, .. } => { | |
522 | if let Res::Def(DefKind::Trait, trait_did) = t.path.res { | |
523 | for impl_item_ref in items { | |
524 | let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); | |
525 | let trait_item_def_id = self | |
526 | .tcx | |
527 | .associated_items(trait_did) | |
74b04a01 XL |
528 | .filter_by_name_unhygienic(impl_item.ident.name) |
529 | .next() | |
dfeec247 XL |
530 | .map(|item| item.def_id); |
531 | if let Some(def_id) = trait_item_def_id { | |
532 | // Pass `None` to skip deprecation warnings. | |
533 | self.tcx.check_stability(def_id, None, impl_item.span); | |
534 | } | |
535 | } | |
536 | } | |
537 | } | |
538 | ||
539 | // There's no good place to insert stability check for non-Copy unions, | |
540 | // so semi-randomly perform it here in stability.rs | |
541 | hir::ItemKind::Union(..) if !self.tcx.features().untagged_unions => { | |
542 | let def_id = self.tcx.hir().local_def_id(item.hir_id); | |
543 | let adt_def = self.tcx.adt_def(def_id); | |
544 | let ty = self.tcx.type_of(def_id); | |
545 | ||
546 | if adt_def.has_dtor(self.tcx) { | |
547 | feature_err( | |
548 | &self.tcx.sess.parse_sess, | |
549 | sym::untagged_unions, | |
550 | item.span, | |
551 | "unions with `Drop` implementations are unstable", | |
552 | ) | |
553 | .emit(); | |
554 | } else { | |
555 | let param_env = self.tcx.param_env(def_id); | |
74b04a01 | 556 | if can_type_implement_copy(self.tcx, param_env, ty).is_err() { |
dfeec247 XL |
557 | feature_err( |
558 | &self.tcx.sess.parse_sess, | |
559 | sym::untagged_unions, | |
560 | item.span, | |
561 | "unions with non-`Copy` fields are unstable", | |
562 | ) | |
563 | .emit(); | |
564 | } | |
565 | } | |
566 | } | |
567 | ||
568 | _ => (/* pass */), | |
569 | } | |
570 | intravisit::walk_item(self, item); | |
571 | } | |
572 | ||
573 | fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, id: hir::HirId) { | |
574 | if let Some(def_id) = path.res.opt_def_id() { | |
575 | self.tcx.check_stability(def_id, Some(id), path.span) | |
576 | } | |
577 | intravisit::walk_path(self, path) | |
578 | } | |
579 | } | |
580 | ||
581 | /// Given the list of enabled features that were not language features (i.e., that | |
582 | /// were expected to be library features), and the list of features used from | |
583 | /// libraries, identify activated features that don't exist and error about them. | |
584 | pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { | |
585 | let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE); | |
586 | ||
587 | if tcx.stability().staged_api[&LOCAL_CRATE] { | |
588 | let krate = tcx.hir().krate(); | |
589 | let mut missing = MissingStabilityAnnotations { tcx, access_levels }; | |
f9f354fc | 590 | missing.check_missing_stability(hir::CRATE_HIR_ID, krate.item.span); |
dfeec247 XL |
591 | intravisit::walk_crate(&mut missing, krate); |
592 | krate.visit_all_item_likes(&mut missing.as_deep_visitor()); | |
593 | } | |
594 | ||
595 | let declared_lang_features = &tcx.features().declared_lang_features; | |
596 | let mut lang_features = FxHashSet::default(); | |
597 | for &(feature, span, since) in declared_lang_features { | |
598 | if let Some(since) = since { | |
599 | // Warn if the user has enabled an already-stable lang feature. | |
600 | unnecessary_stable_feature_lint(tcx, span, feature, since); | |
601 | } | |
602 | if !lang_features.insert(feature) { | |
603 | // Warn if the user enables a lang feature multiple times. | |
604 | duplicate_feature_err(tcx.sess, span, feature); | |
605 | } | |
606 | } | |
607 | ||
608 | let declared_lib_features = &tcx.features().declared_lib_features; | |
609 | let mut remaining_lib_features = FxHashMap::default(); | |
610 | for (feature, span) in declared_lib_features { | |
611 | if remaining_lib_features.contains_key(&feature) { | |
612 | // Warn if the user enables a lib feature multiple times. | |
613 | duplicate_feature_err(tcx.sess, *span, *feature); | |
614 | } | |
ba9703b0 | 615 | remaining_lib_features.insert(feature, *span); |
dfeec247 XL |
616 | } |
617 | // `stdbuild` has special handling for `libc`, so we need to | |
618 | // recognise the feature when building std. | |
619 | // Likewise, libtest is handled specially, so `test` isn't | |
620 | // available as we'd like it to be. | |
621 | // FIXME: only remove `libc` when `stdbuild` is active. | |
622 | // FIXME: remove special casing for `test`. | |
623 | remaining_lib_features.remove(&Symbol::intern("libc")); | |
624 | remaining_lib_features.remove(&sym::test); | |
625 | ||
626 | let check_features = |remaining_lib_features: &mut FxHashMap<_, _>, defined_features: &[_]| { | |
627 | for &(feature, since) in defined_features { | |
628 | if let Some(since) = since { | |
629 | if let Some(span) = remaining_lib_features.get(&feature) { | |
630 | // Warn if the user has enabled an already-stable lib feature. | |
631 | unnecessary_stable_feature_lint(tcx, *span, feature, since); | |
632 | } | |
633 | } | |
634 | remaining_lib_features.remove(&feature); | |
635 | if remaining_lib_features.is_empty() { | |
636 | break; | |
637 | } | |
638 | } | |
639 | }; | |
640 | ||
641 | // We always collect the lib features declared in the current crate, even if there are | |
642 | // no unknown features, because the collection also does feature attribute validation. | |
643 | let local_defined_features = tcx.lib_features().to_vec(); | |
644 | if !remaining_lib_features.is_empty() { | |
645 | check_features(&mut remaining_lib_features, &local_defined_features); | |
646 | ||
647 | for &cnum in &*tcx.crates() { | |
648 | if remaining_lib_features.is_empty() { | |
649 | break; | |
650 | } | |
651 | check_features(&mut remaining_lib_features, tcx.defined_lib_features(cnum)); | |
652 | } | |
653 | } | |
654 | ||
655 | for (feature, span) in remaining_lib_features { | |
656 | struct_span_err!(tcx.sess, span, E0635, "unknown feature `{}`", feature).emit(); | |
657 | } | |
658 | ||
659 | // FIXME(#44232): the `used_features` table no longer exists, so we | |
74b04a01 | 660 | // don't lint about unused features. We should re-enable this one day! |
dfeec247 XL |
661 | } |
662 | ||
663 | fn unnecessary_stable_feature_lint(tcx: TyCtxt<'_>, span: Span, feature: Symbol, since: Symbol) { | |
74b04a01 XL |
664 | tcx.struct_span_lint_hir(lint::builtin::STABLE_FEATURES, hir::CRATE_HIR_ID, span, |lint| { |
665 | lint.build(&format!( | |
dfeec247 | 666 | "the feature `{}` has been stable since {} and no longer requires \ |
74b04a01 | 667 | an attribute to enable", |
dfeec247 | 668 | feature, since |
74b04a01 XL |
669 | )) |
670 | .emit(); | |
671 | }); | |
dfeec247 XL |
672 | } |
673 | ||
674 | fn duplicate_feature_err(sess: &Session, span: Span, feature: Symbol) { | |
675 | struct_span_err!(sess, span, E0636, "the feature `{}` has already been declared", feature) | |
676 | .emit(); | |
677 | } |