]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2014 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! A pass that annotates every item and method with its stability level, | |
12 | //! propagating default levels lexically from parent to children ast nodes. | |
13 | ||
b039eaaf SL |
14 | pub use self::StabilityLevel::*; |
15 | ||
85aaf69f | 16 | use lint; |
54a0048b | 17 | use hir::def::Def; |
ea8adc8c | 18 | use hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId, LOCAL_CRATE}; |
476ff2be | 19 | use ty::{self, TyCtxt}; |
92a42be0 | 20 | use middle::privacy::AccessLevels; |
abe05a73 | 21 | use session::DiagnosticMessageId; |
476ff2be | 22 | use syntax::symbol::Symbol; |
abe05a73 | 23 | use syntax_pos::{Span, MultiSpan, DUMMY_SP}; |
1a4d82fc | 24 | use syntax::ast; |
b039eaaf | 25 | use syntax::ast::{NodeId, Attribute}; |
3157f602 | 26 | use syntax::feature_gate::{GateIssue, emit_feature_err, find_lang_feature_accepted_version}; |
9e0c209e | 27 | use syntax::attr::{self, Stability, Deprecation}; |
ea8adc8c | 28 | use util::nodemap::{FxHashSet, FxHashMap}; |
1a4d82fc | 29 | |
54a0048b | 30 | use hir; |
ea8adc8c | 31 | use hir::{Item, Generics, StructField, Variant, HirId}; |
476ff2be | 32 | use hir::intravisit::{self, Visitor, NestedVisitorMap}; |
e9174d1e | 33 | |
1a4d82fc | 34 | use std::mem::replace; |
c1a9b12d | 35 | use std::cmp::Ordering; |
1a4d82fc | 36 | |
b039eaaf SL |
37 | #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Copy, Debug, Eq, Hash)] |
38 | pub enum StabilityLevel { | |
39 | Unstable, | |
40 | Stable, | |
41 | } | |
42 | ||
43 | impl StabilityLevel { | |
44 | pub fn from_attr_level(level: &attr::StabilityLevel) -> Self { | |
45 | if level.is_stable() { Stable } else { Unstable } | |
46 | } | |
47 | } | |
48 | ||
92a42be0 SL |
49 | #[derive(PartialEq)] |
50 | enum AnnotationKind { | |
51 | // Annotation is required if not inherited from unstable parents | |
52 | Required, | |
53 | // Annotation is useless, reject it | |
54 | Prohibited, | |
55 | // Annotation itself is useless, but it can be propagated to children | |
56 | Container, | |
57 | } | |
58 | ||
5bcae85e SL |
59 | /// An entry in the `depr_map`. |
60 | #[derive(Clone)] | |
61 | pub struct DeprecationEntry { | |
62 | /// The metadata of the attribute associated with this entry. | |
63 | pub attr: Deprecation, | |
64 | /// The def id where the attr was originally attached. `None` for non-local | |
65 | /// `DefId`'s. | |
ea8adc8c | 66 | origin: Option<HirId>, |
5bcae85e SL |
67 | } |
68 | ||
ea8adc8c XL |
69 | impl_stable_hash_for!(struct self::DeprecationEntry { |
70 | attr, | |
71 | origin | |
72 | }); | |
73 | ||
5bcae85e | 74 | impl DeprecationEntry { |
ea8adc8c | 75 | fn local(attr: Deprecation, id: HirId) -> DeprecationEntry { |
5bcae85e | 76 | DeprecationEntry { |
041b39d2 | 77 | attr, |
ea8adc8c | 78 | origin: Some(id), |
5bcae85e SL |
79 | } |
80 | } | |
81 | ||
ea8adc8c | 82 | pub fn external(attr: Deprecation) -> DeprecationEntry { |
5bcae85e | 83 | DeprecationEntry { |
041b39d2 | 84 | attr, |
5bcae85e SL |
85 | origin: None, |
86 | } | |
87 | } | |
88 | ||
89 | pub fn same_origin(&self, other: &DeprecationEntry) -> bool { | |
90 | match (self.origin, other.origin) { | |
91 | (Some(o1), Some(o2)) => o1 == o2, | |
92 | _ => false | |
93 | } | |
94 | } | |
95 | } | |
96 | ||
1a4d82fc | 97 | /// A stability index, giving the stability level for items and methods. |
62682a34 SL |
98 | pub struct Index<'tcx> { |
99 | /// This is mostly a cache, except the stabilities of local items | |
100 | /// are filled by the annotator. | |
ea8adc8c XL |
101 | stab_map: FxHashMap<HirId, &'tcx Stability>, |
102 | depr_map: FxHashMap<HirId, DeprecationEntry>, | |
62682a34 SL |
103 | |
104 | /// Maps for each crate whether it is part of the staged API. | |
476ff2be SL |
105 | staged_api: FxHashMap<CrateNum, bool>, |
106 | ||
107 | /// Features enabled for this crate. | |
108 | active_features: FxHashSet<Symbol>, | |
1a4d82fc JJ |
109 | } |
110 | ||
ea8adc8c XL |
111 | impl_stable_hash_for!(struct self::Index<'tcx> { |
112 | stab_map, | |
113 | depr_map, | |
114 | staged_api, | |
115 | active_features | |
116 | }); | |
117 | ||
1a4d82fc | 118 | // A private tree-walker for producing an Index. |
62682a34 | 119 | struct Annotator<'a, 'tcx: 'a> { |
a7813a04 | 120 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
62682a34 | 121 | index: &'a mut Index<'tcx>, |
9cc50fc6 | 122 | parent_stab: Option<&'tcx Stability>, |
5bcae85e | 123 | parent_depr: Option<DeprecationEntry>, |
92a42be0 | 124 | in_trait_impl: bool, |
1a4d82fc JJ |
125 | } |
126 | ||
62682a34 | 127 | impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> { |
1a4d82fc JJ |
128 | // Determine the stability for a node based on its attributes and inherited |
129 | // stability. The stability is recorded in the index and used as the parent. | |
9cc50fc6 | 130 | fn annotate<F>(&mut self, id: NodeId, attrs: &[Attribute], |
92a42be0 | 131 | item_sp: Span, kind: AnnotationKind, visit_children: F) |
476ff2be | 132 | where F: FnOnce(&mut Self) |
1a4d82fc | 133 | { |
0531ce1d | 134 | if self.tcx.features().staged_api { |
3b2f2976 | 135 | // This crate explicitly wants staged API. |
9346a6ac | 136 | debug!("annotate(id = {:?}, attrs = {:?})", id, attrs); |
9cc50fc6 SL |
137 | if let Some(..) = attr::find_deprecation(self.tcx.sess.diagnostic(), attrs, item_sp) { |
138 | self.tcx.sess.span_err(item_sp, "`#[deprecated]` cannot be used in staged api, \ | |
139 | use `#[rustc_deprecated]` instead"); | |
140 | } | |
92a42be0 SL |
141 | if let Some(mut stab) = attr::find_stability(self.tcx.sess.diagnostic(), |
142 | attrs, item_sp) { | |
143 | // Error if prohibited, or can't inherit anything from a container | |
144 | if kind == AnnotationKind::Prohibited || | |
145 | (kind == AnnotationKind::Container && | |
146 | stab.level.is_stable() && | |
9cc50fc6 | 147 | stab.rustc_depr.is_none()) { |
92a42be0 SL |
148 | self.tcx.sess.span_err(item_sp, "This stability annotation is useless"); |
149 | } | |
150 | ||
151 | debug!("annotate: found {:?}", stab); | |
152 | // If parent is deprecated and we're not, inherit this by merging | |
153 | // deprecated_since and its reason. | |
9cc50fc6 SL |
154 | if let Some(parent_stab) = self.parent_stab { |
155 | if parent_stab.rustc_depr.is_some() && stab.rustc_depr.is_none() { | |
156 | stab.rustc_depr = parent_stab.rustc_depr.clone() | |
c1a9b12d | 157 | } |
92a42be0 | 158 | } |
c1a9b12d | 159 | |
92a42be0 SL |
160 | let stab = self.tcx.intern_stability(stab); |
161 | ||
162 | // Check if deprecated_since < stable_since. If it is, | |
163 | // this is *almost surely* an accident. | |
476ff2be SL |
164 | if let (&Some(attr::RustcDeprecation {since: dep_since, ..}), |
165 | &attr::Stable {since: stab_since}) = (&stab.rustc_depr, &stab.level) { | |
92a42be0 | 166 | // Explicit version of iter::order::lt to handle parse errors properly |
476ff2be SL |
167 | for (dep_v, stab_v) in |
168 | dep_since.as_str().split(".").zip(stab_since.as_str().split(".")) { | |
92a42be0 SL |
169 | if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::<u64>(), stab_v.parse()) { |
170 | match dep_v.cmp(&stab_v) { | |
171 | Ordering::Less => { | |
172 | self.tcx.sess.span_err(item_sp, "An API can't be stabilized \ | |
173 | after it is deprecated"); | |
174 | break | |
c1a9b12d | 175 | } |
92a42be0 SL |
176 | Ordering::Equal => continue, |
177 | Ordering::Greater => break, | |
c1a9b12d | 178 | } |
92a42be0 SL |
179 | } else { |
180 | // Act like it isn't less because the question is now nonsensical, | |
181 | // and this makes us not do anything else interesting. | |
182 | self.tcx.sess.span_err(item_sp, "Invalid stability or deprecation \ | |
183 | version found"); | |
184 | break | |
185 | } | |
c1a9b12d | 186 | } |
92a42be0 | 187 | } |
c1a9b12d | 188 | |
ea8adc8c XL |
189 | let hir_id = self.tcx.hir.node_to_hir_id(id); |
190 | self.index.stab_map.insert(hir_id, stab); | |
92a42be0 | 191 | |
9cc50fc6 | 192 | let orig_parent_stab = replace(&mut self.parent_stab, Some(stab)); |
92a42be0 | 193 | visit_children(self); |
9cc50fc6 | 194 | self.parent_stab = orig_parent_stab; |
92a42be0 | 195 | } else { |
9cc50fc6 | 196 | debug!("annotate: not found, parent = {:?}", self.parent_stab); |
9cc50fc6 | 197 | if let Some(stab) = self.parent_stab { |
92a42be0 | 198 | if stab.level.is_unstable() { |
ea8adc8c XL |
199 | let hir_id = self.tcx.hir.node_to_hir_id(id); |
200 | self.index.stab_map.insert(hir_id, stab); | |
9346a6ac AL |
201 | } |
202 | } | |
92a42be0 | 203 | visit_children(self); |
1a4d82fc | 204 | } |
9346a6ac | 205 | } else { |
92a42be0 | 206 | // Emit errors for non-staged-api crates. |
9346a6ac | 207 | for attr in attrs { |
cc61c64b | 208 | let tag = unwrap_or!(attr.name(), continue); |
92a42be0 | 209 | if tag == "unstable" || tag == "stable" || tag == "rustc_deprecated" { |
9346a6ac | 210 | attr::mark_used(attr); |
92a42be0 SL |
211 | self.tcx.sess.span_err(attr.span(), "stability attributes may not be used \ |
212 | outside of the standard library"); | |
1a4d82fc | 213 | } |
1a4d82fc | 214 | } |
9cc50fc6 | 215 | |
3b2f2976 XL |
216 | // Propagate unstability. This can happen even for non-staged-api crates in case |
217 | // -Zforce-unstable-if-unmarked is set. | |
218 | if let Some(stab) = self.parent_stab { | |
219 | if stab.level.is_unstable() { | |
ea8adc8c XL |
220 | let hir_id = self.tcx.hir.node_to_hir_id(id); |
221 | self.index.stab_map.insert(hir_id, stab); | |
3b2f2976 XL |
222 | } |
223 | } | |
224 | ||
9cc50fc6 SL |
225 | if let Some(depr) = attr::find_deprecation(self.tcx.sess.diagnostic(), attrs, item_sp) { |
226 | if kind == AnnotationKind::Prohibited { | |
227 | self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless"); | |
228 | } | |
229 | ||
230 | // `Deprecation` is just two pointers, no need to intern it | |
ea8adc8c XL |
231 | let hir_id = self.tcx.hir.node_to_hir_id(id); |
232 | let depr_entry = DeprecationEntry::local(depr, hir_id); | |
233 | self.index.depr_map.insert(hir_id, depr_entry.clone()); | |
9cc50fc6 | 234 | |
ea8adc8c XL |
235 | let orig_parent_depr = replace(&mut self.parent_depr, |
236 | Some(depr_entry)); | |
9cc50fc6 SL |
237 | visit_children(self); |
238 | self.parent_depr = orig_parent_depr; | |
ea8adc8c XL |
239 | } else if let Some(parent_depr) = self.parent_depr.clone() { |
240 | let hir_id = self.tcx.hir.node_to_hir_id(id); | |
241 | self.index.depr_map.insert(hir_id, parent_depr); | |
9cc50fc6 SL |
242 | visit_children(self); |
243 | } else { | |
244 | visit_children(self); | |
245 | } | |
1a4d82fc JJ |
246 | } |
247 | } | |
248 | } | |
249 | ||
476ff2be | 250 | impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { |
92a42be0 SL |
251 | /// Because stability levels are scoped lexically, we want to walk |
252 | /// nested items in the context of the outer item, so enable | |
253 | /// deep-walking. | |
476ff2be | 254 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { |
32a655c1 | 255 | NestedVisitorMap::All(&self.tcx.hir) |
92a42be0 | 256 | } |
1a4d82fc | 257 | |
476ff2be | 258 | fn visit_item(&mut self, i: &'tcx Item) { |
92a42be0 | 259 | let orig_in_trait_impl = self.in_trait_impl; |
92a42be0 SL |
260 | let mut kind = AnnotationKind::Required; |
261 | match i.node { | |
262 | // Inherent impls and foreign modules serve only as containers for other items, | |
263 | // they don't have their own stability. They still can be annotated as unstable | |
264 | // and propagate this unstability to children, but this annotation is completely | |
265 | // optional. They inherit stability from their parents when unannotated. | |
9e0c209e | 266 | hir::ItemImpl(.., None, _, _) | hir::ItemForeignMod(..) => { |
92a42be0 SL |
267 | self.in_trait_impl = false; |
268 | kind = AnnotationKind::Container; | |
b039eaaf | 269 | } |
9e0c209e | 270 | hir::ItemImpl(.., Some(_), _, _) => { |
92a42be0 SL |
271 | self.in_trait_impl = true; |
272 | } | |
273 | hir::ItemStruct(ref sd, _) => { | |
92a42be0 SL |
274 | if !sd.is_struct() { |
275 | self.annotate(sd.id(), &i.attrs, i.span, AnnotationKind::Required, |_| {}) | |
276 | } | |
277 | } | |
92a42be0 | 278 | _ => {} |
1a4d82fc | 279 | } |
1a4d82fc | 280 | |
92a42be0 SL |
281 | self.annotate(i.id, &i.attrs, i.span, kind, |v| { |
282 | intravisit::walk_item(v, i) | |
283 | }); | |
284 | self.in_trait_impl = orig_in_trait_impl; | |
1a4d82fc JJ |
285 | } |
286 | ||
476ff2be | 287 | fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem) { |
92a42be0 SL |
288 | self.annotate(ti.id, &ti.attrs, ti.span, AnnotationKind::Required, |v| { |
289 | intravisit::walk_trait_item(v, ti); | |
290 | }); | |
c34b1796 | 291 | } |
1a4d82fc | 292 | |
476ff2be | 293 | fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem) { |
92a42be0 SL |
294 | let kind = if self.in_trait_impl { |
295 | AnnotationKind::Prohibited | |
296 | } else { | |
297 | AnnotationKind::Required | |
298 | }; | |
299 | self.annotate(ii.id, &ii.attrs, ii.span, kind, |v| { | |
300 | intravisit::walk_impl_item(v, ii); | |
301 | }); | |
1a4d82fc JJ |
302 | } |
303 | ||
476ff2be | 304 | fn visit_variant(&mut self, var: &'tcx Variant, g: &'tcx Generics, item_id: NodeId) { |
92a42be0 SL |
305 | self.annotate(var.node.data.id(), &var.node.attrs, var.span, AnnotationKind::Required, |v| { |
306 | intravisit::walk_variant(v, var, g, item_id); | |
307 | }) | |
1a4d82fc JJ |
308 | } |
309 | ||
476ff2be | 310 | fn visit_struct_field(&mut self, s: &'tcx StructField) { |
54a0048b | 311 | self.annotate(s.id, &s.attrs, s.span, AnnotationKind::Required, |v| { |
92a42be0 SL |
312 | intravisit::walk_struct_field(v, s); |
313 | }); | |
1a4d82fc JJ |
314 | } |
315 | ||
476ff2be | 316 | fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem) { |
92a42be0 SL |
317 | self.annotate(i.id, &i.attrs, i.span, AnnotationKind::Required, |v| { |
318 | intravisit::walk_foreign_item(v, i); | |
319 | }); | |
320 | } | |
321 | ||
476ff2be | 322 | fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef) { |
32a655c1 | 323 | self.annotate(md.id, &md.attrs, md.span, AnnotationKind::Required, |_| {}); |
1a4d82fc JJ |
324 | } |
325 | } | |
326 | ||
476ff2be SL |
327 | struct MissingStabilityAnnotations<'a, 'tcx: 'a> { |
328 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
329 | access_levels: &'a AccessLevels, | |
330 | } | |
331 | ||
332 | impl<'a, 'tcx: 'a> MissingStabilityAnnotations<'a, 'tcx> { | |
333 | fn check_missing_stability(&self, id: NodeId, span: Span) { | |
ea8adc8c XL |
334 | let hir_id = self.tcx.hir.node_to_hir_id(id); |
335 | let stab = self.tcx.stability().local_stability(hir_id); | |
476ff2be | 336 | let is_error = !self.tcx.sess.opts.test && |
ea8adc8c | 337 | stab.is_none() && |
476ff2be SL |
338 | self.access_levels.is_reachable(id); |
339 | if is_error { | |
340 | self.tcx.sess.span_err(span, "This node does not have a stability attribute"); | |
341 | } | |
342 | } | |
343 | } | |
344 | ||
345 | impl<'a, 'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'a, 'tcx> { | |
346 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { | |
32a655c1 | 347 | NestedVisitorMap::OnlyBodies(&self.tcx.hir) |
476ff2be SL |
348 | } |
349 | ||
350 | fn visit_item(&mut self, i: &'tcx Item) { | |
351 | match i.node { | |
352 | // Inherent impls and foreign modules serve only as containers for other items, | |
353 | // they don't have their own stability. They still can be annotated as unstable | |
354 | // and propagate this unstability to children, but this annotation is completely | |
355 | // optional. They inherit stability from their parents when unannotated. | |
356 | hir::ItemImpl(.., None, _, _) | hir::ItemForeignMod(..) => {} | |
357 | ||
358 | _ => self.check_missing_stability(i.id, i.span) | |
359 | } | |
360 | ||
361 | intravisit::walk_item(self, i) | |
362 | } | |
363 | ||
364 | fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem) { | |
365 | self.check_missing_stability(ti.id, ti.span); | |
366 | intravisit::walk_trait_item(self, ti); | |
367 | } | |
368 | ||
369 | fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem) { | |
32a655c1 | 370 | let impl_def_id = self.tcx.hir.local_def_id(self.tcx.hir.get_parent(ii.id)); |
476ff2be SL |
371 | if self.tcx.impl_trait_ref(impl_def_id).is_none() { |
372 | self.check_missing_stability(ii.id, ii.span); | |
373 | } | |
374 | intravisit::walk_impl_item(self, ii); | |
375 | } | |
376 | ||
377 | fn visit_variant(&mut self, var: &'tcx Variant, g: &'tcx Generics, item_id: NodeId) { | |
378 | self.check_missing_stability(var.node.data.id(), var.span); | |
379 | intravisit::walk_variant(self, var, g, item_id); | |
380 | } | |
381 | ||
382 | fn visit_struct_field(&mut self, s: &'tcx StructField) { | |
383 | self.check_missing_stability(s.id, s.span); | |
384 | intravisit::walk_struct_field(self, s); | |
385 | } | |
386 | ||
387 | fn visit_foreign_item(&mut self, i: &'tcx hir::ForeignItem) { | |
388 | self.check_missing_stability(i.id, i.span); | |
389 | intravisit::walk_foreign_item(self, i); | |
390 | } | |
391 | ||
392 | fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef) { | |
32a655c1 | 393 | self.check_missing_stability(md.id, md.span); |
476ff2be SL |
394 | } |
395 | } | |
396 | ||
a7813a04 | 397 | impl<'a, 'tcx> Index<'tcx> { |
ea8adc8c XL |
398 | pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Index<'tcx> { |
399 | let is_staged_api = | |
400 | tcx.sess.opts.debugging_opts.force_unstable_if_unmarked || | |
0531ce1d | 401 | tcx.features().staged_api; |
ea8adc8c XL |
402 | let mut staged_api = FxHashMap(); |
403 | staged_api.insert(LOCAL_CRATE, is_staged_api); | |
404 | let mut index = Index { | |
405 | staged_api, | |
406 | stab_map: FxHashMap(), | |
407 | depr_map: FxHashMap(), | |
408 | active_features: FxHashSet(), | |
409 | }; | |
410 | ||
0531ce1d | 411 | let ref active_lib_features = tcx.features().declared_lib_features; |
476ff2be SL |
412 | |
413 | // Put the active features into a map for quick lookup | |
ea8adc8c XL |
414 | index.active_features = active_lib_features.iter().map(|&(ref s, _)| s.clone()).collect(); |
415 | ||
416 | { | |
417 | let krate = tcx.hir.krate(); | |
418 | let mut annotator = Annotator { | |
419 | tcx, | |
420 | index: &mut index, | |
421 | parent_stab: None, | |
422 | parent_depr: None, | |
423 | in_trait_impl: false, | |
424 | }; | |
476ff2be | 425 | |
ea8adc8c XL |
426 | // If the `-Z force-unstable-if-unmarked` flag is passed then we provide |
427 | // a parent stability annotation which indicates that this is private | |
428 | // with the `rustc_private` feature. This is intended for use when | |
429 | // compiling librustc crates themselves so we can leverage crates.io | |
430 | // while maintaining the invariant that all sysroot crates are unstable | |
431 | // by default and are unable to be used. | |
432 | if tcx.sess.opts.debugging_opts.force_unstable_if_unmarked { | |
433 | let reason = "this crate is being loaded from the sysroot, an \ | |
434 | unstable location; did you mean to load this crate \ | |
435 | from crates.io via `Cargo.toml` instead?"; | |
436 | let stability = tcx.intern_stability(Stability { | |
437 | level: attr::StabilityLevel::Unstable { | |
438 | reason: Some(Symbol::intern(reason)), | |
439 | issue: 27812, | |
440 | }, | |
441 | feature: Symbol::intern("rustc_private"), | |
442 | rustc_depr: None, | |
443 | rustc_const_unstable: None, | |
444 | }); | |
445 | annotator.parent_stab = Some(stability); | |
446 | } | |
7cac9316 | 447 | |
ea8adc8c XL |
448 | annotator.annotate(ast::CRATE_NODE_ID, |
449 | &krate.attrs, | |
450 | krate.span, | |
451 | AnnotationKind::Required, | |
452 | |v| intravisit::walk_crate(v, krate)); | |
7cac9316 | 453 | } |
ea8adc8c XL |
454 | return index |
455 | } | |
7cac9316 | 456 | |
ea8adc8c XL |
457 | pub fn local_stability(&self, id: HirId) -> Option<&'tcx Stability> { |
458 | self.stab_map.get(&id).cloned() | |
85aaf69f SL |
459 | } |
460 | ||
ea8adc8c XL |
461 | pub fn local_deprecation_entry(&self, id: HirId) -> Option<DeprecationEntry> { |
462 | self.depr_map.get(&id).cloned() | |
85aaf69f SL |
463 | } |
464 | } | |
465 | ||
466 | /// Cross-references the feature names of unstable APIs with enabled | |
476ff2be SL |
467 | /// features and possibly prints errors. |
468 | pub fn check_unstable_api_usage<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { | |
469 | let mut checker = Checker { tcx: tcx }; | |
cc61c64b | 470 | tcx.hir.krate().visit_all_item_likes(&mut checker.as_deep_visitor()); |
85aaf69f SL |
471 | } |
472 | ||
473 | struct Checker<'a, 'tcx: 'a> { | |
a7813a04 | 474 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
85aaf69f SL |
475 | } |
476 | ||
0531ce1d XL |
477 | /// Result of `TyCtxt::eval_stability`. |
478 | pub enum EvalResult { | |
479 | /// We can use the item because it is stable or we provided the | |
480 | /// corresponding feature gate. | |
481 | Allow, | |
482 | /// We cannot use the item because it is unstable and we did not provide the | |
483 | /// corresponding feature gate. | |
484 | Deny { | |
485 | feature: Symbol, | |
486 | reason: Option<Symbol>, | |
487 | issue: u32, | |
488 | }, | |
489 | /// The item does not have the `#[stable]` or `#[unstable]` marker assigned. | |
490 | Unmarked, | |
491 | } | |
492 | ||
476ff2be SL |
493 | impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { |
494 | // (See issue #38412) | |
8bb4bdeb XL |
495 | fn skip_stability_check_due_to_privacy(self, mut def_id: DefId) -> bool { |
496 | // Check if `def_id` is a trait method. | |
7cac9316 | 497 | match self.describe_def(def_id) { |
8bb4bdeb XL |
498 | Some(Def::Method(_)) | |
499 | Some(Def::AssociatedTy(_)) | | |
500 | Some(Def::AssociatedConst(_)) => { | |
501 | match self.associated_item(def_id).container { | |
502 | ty::TraitContainer(trait_def_id) => { | |
503 | // Trait methods do not declare visibility (even | |
504 | // for visibility info in cstore). Use containing | |
505 | // trait instead, so methods of pub traits are | |
506 | // themselves considered pub. | |
507 | def_id = trait_def_id; | |
508 | } | |
509 | _ => {} | |
476ff2be SL |
510 | } |
511 | } | |
8bb4bdeb XL |
512 | _ => {} |
513 | } | |
514 | ||
ea8adc8c | 515 | let visibility = self.visibility(def_id); |
476ff2be SL |
516 | |
517 | match visibility { | |
518 | // must check stability for pub items. | |
519 | ty::Visibility::Public => false, | |
520 | ||
521 | // these are not visible outside crate; therefore | |
522 | // stability markers are irrelevant, if even present. | |
523 | ty::Visibility::Restricted(..) | | |
32a655c1 | 524 | ty::Visibility::Invisible => true, |
476ff2be SL |
525 | } |
526 | } | |
527 | ||
0531ce1d XL |
528 | /// Evaluates the stability of an item. |
529 | /// | |
530 | /// Returns `EvalResult::Allow` if the item is stable, or unstable but the corresponding | |
531 | /// `#![feature]` has been provided. Returns `EvalResult::Deny` which describes the offending | |
532 | /// unstable feature otherwise. | |
533 | /// | |
534 | /// If `id` is `Some(_)`, this function will also check if the item at `def_id` has been | |
535 | /// deprecated. If the item is indeed deprecated, we will emit a deprecation lint attached to | |
536 | /// `id`. | |
537 | pub fn eval_stability(self, def_id: DefId, id: Option<NodeId>, span: Span) -> EvalResult { | |
cc61c64b | 538 | if span.allows_unstable() { |
476ff2be SL |
539 | debug!("stability: \ |
540 | skipping span={:?} since it is internal", span); | |
0531ce1d | 541 | return EvalResult::Allow; |
476ff2be SL |
542 | } |
543 | ||
0531ce1d | 544 | let lint_deprecated = |def_id: DefId, id: NodeId, note: Option<Symbol>| { |
abe05a73 XL |
545 | let path = self.item_path_str(def_id); |
546 | ||
476ff2be | 547 | let msg = if let Some(note) = note { |
abe05a73 | 548 | format!("use of deprecated item '{}': {}", path, note) |
476ff2be | 549 | } else { |
abe05a73 | 550 | format!("use of deprecated item '{}'", path) |
476ff2be SL |
551 | }; |
552 | ||
3b2f2976 | 553 | self.lint_node(lint::builtin::DEPRECATED, id, span, &msg); |
0531ce1d XL |
554 | if id == ast::DUMMY_NODE_ID { |
555 | span_bug!(span, "emitted a deprecated lint with dummy node id: {:?}", def_id); | |
556 | } | |
476ff2be SL |
557 | }; |
558 | ||
559 | // Deprecated attributes apply in-crate and cross-crate. | |
0531ce1d XL |
560 | if let Some(id) = id { |
561 | if let Some(depr_entry) = self.lookup_deprecation_entry(def_id) { | |
32a655c1 | 562 | let parent_def_id = self.hir.local_def_id(self.hir.get_parent(id)); |
0531ce1d XL |
563 | let skip = self.lookup_deprecation_entry(parent_def_id) |
564 | .map_or(false, |parent_depr| parent_depr.same_origin(&depr_entry)); | |
565 | if !skip { | |
566 | lint_deprecated(def_id, id, depr_entry.attr.note); | |
567 | } | |
476ff2be | 568 | }; |
476ff2be SL |
569 | } |
570 | ||
7cac9316 XL |
571 | let is_staged_api = self.lookup_stability(DefId { |
572 | index: CRATE_DEF_INDEX, | |
573 | ..def_id | |
574 | }).is_some(); | |
476ff2be | 575 | if !is_staged_api { |
0531ce1d | 576 | return EvalResult::Allow; |
9cc50fc6 | 577 | } |
476ff2be SL |
578 | |
579 | let stability = self.lookup_stability(def_id); | |
580 | debug!("stability: \ | |
581 | inspecting def_id={:?} span={:?} of stability={:?}", def_id, span, stability); | |
582 | ||
583 | if let Some(&Stability{rustc_depr: Some(attr::RustcDeprecation { reason, .. }), ..}) | |
584 | = stability { | |
0531ce1d XL |
585 | if let Some(id) = id { |
586 | lint_deprecated(def_id, id, Some(reason)); | |
476ff2be SL |
587 | } |
588 | } | |
589 | ||
85aaf69f | 590 | // Only the cross-crate scenario matters when checking unstable APIs |
476ff2be | 591 | let cross_crate = !def_id.is_local(); |
b039eaaf | 592 | if !cross_crate { |
0531ce1d | 593 | return EvalResult::Allow; |
b039eaaf SL |
594 | } |
595 | ||
476ff2be SL |
596 | // Issue 38412: private items lack stability markers. |
597 | if self.skip_stability_check_due_to_privacy(def_id) { | |
0531ce1d | 598 | return EvalResult::Allow; |
476ff2be | 599 | } |
85aaf69f | 600 | |
476ff2be | 601 | match stability { |
0531ce1d XL |
602 | Some(&Stability { level: attr::Unstable { reason, issue }, feature, .. }) => { |
603 | if self.stability().active_features.contains(&feature) { | |
604 | return EvalResult::Allow; | |
85aaf69f | 605 | } |
7cac9316 XL |
606 | |
607 | // When we're compiling the compiler itself we may pull in | |
608 | // crates from crates.io, but those crates may depend on other | |
609 | // crates also pulled in from crates.io. We want to ideally be | |
610 | // able to compile everything without requiring upstream | |
611 | // modifications, so in the case that this looks like a | |
612 | // rustc_private crate (e.g. a compiler crate) and we also have | |
613 | // the `-Z force-unstable-if-unmarked` flag present (we're | |
614 | // compiling a compiler crate), then let this missing feature | |
615 | // annotation slide. | |
0531ce1d | 616 | if feature == "rustc_private" && issue == 27812 { |
7cac9316 | 617 | if self.sess.opts.debugging_opts.force_unstable_if_unmarked { |
0531ce1d | 618 | return EvalResult::Allow; |
7cac9316 XL |
619 | } |
620 | } | |
621 | ||
0531ce1d XL |
622 | EvalResult::Deny { feature, reason, issue } |
623 | } | |
624 | Some(_) => { | |
625 | // Stable APIs are always ok to call and deprecated APIs are | |
626 | // handled by the lint emitting logic above. | |
627 | EvalResult::Allow | |
628 | } | |
629 | None => { | |
630 | EvalResult::Unmarked | |
631 | } | |
632 | } | |
633 | } | |
634 | ||
635 | /// Checks if an item is stable or error out. | |
636 | /// | |
637 | /// If the item defined by `def_id` is unstable and the corresponding `#![feature]` does not | |
638 | /// exist, emits an error. | |
639 | /// | |
640 | /// Additionally, this function will also check if the item is deprecated. If so, and `id` is | |
641 | /// not `None`, a deprecated lint attached to `id` will be emitted. | |
642 | pub fn check_stability(self, def_id: DefId, id: Option<NodeId>, span: Span) { | |
643 | match self.eval_stability(def_id, id, span) { | |
644 | EvalResult::Allow => {} | |
645 | EvalResult::Deny { feature, reason, issue } => { | |
646 | let msg = match reason { | |
647 | Some(r) => format!("use of unstable library feature '{}': {}", feature, r), | |
7cac9316 XL |
648 | None => format!("use of unstable library feature '{}'", &feature) |
649 | }; | |
abe05a73 | 650 | |
abe05a73 XL |
651 | let msp: MultiSpan = span.into(); |
652 | let cm = &self.sess.parse_sess.codemap(); | |
653 | let span_key = msp.primary_span().and_then(|sp: Span| | |
654 | if sp != DUMMY_SP { | |
655 | let file = cm.lookup_char_pos(sp.lo()).file; | |
ff7c6d11 | 656 | if file.name.is_macros() { |
abe05a73 XL |
657 | None |
658 | } else { | |
659 | Some(span) | |
660 | } | |
661 | } else { | |
662 | None | |
663 | } | |
664 | ); | |
665 | ||
666 | let error_id = (DiagnosticMessageId::StabilityId(issue), span_key, msg.clone()); | |
667 | let fresh = self.sess.one_time_diagnostics.borrow_mut().insert(error_id); | |
668 | if fresh { | |
669 | emit_feature_err(&self.sess.parse_sess, &feature.as_str(), span, | |
670 | GateIssue::Library(Some(issue)), &msg); | |
671 | } | |
85aaf69f | 672 | } |
0531ce1d XL |
673 | EvalResult::Unmarked => { |
674 | span_bug!(span, "encountered unmarked API: {:?}", def_id); | |
85aaf69f SL |
675 | } |
676 | } | |
677 | } | |
678 | } | |
679 | ||
476ff2be | 680 | impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> { |
92a42be0 SL |
681 | /// Because stability levels are scoped lexically, we want to walk |
682 | /// nested items in the context of the outer item, so enable | |
683 | /// deep-walking. | |
476ff2be | 684 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { |
32a655c1 | 685 | NestedVisitorMap::OnlyBodies(&self.tcx.hir) |
476ff2be SL |
686 | } |
687 | ||
688 | fn visit_item(&mut self, item: &'tcx hir::Item) { | |
689 | match item.node { | |
690 | hir::ItemExternCrate(_) => { | |
691 | // compiler-generated `extern crate` items have a dummy span. | |
692 | if item.span == DUMMY_SP { return } | |
693 | ||
ea8adc8c XL |
694 | let def_id = self.tcx.hir.local_def_id(item.id); |
695 | let cnum = match self.tcx.extern_mod_stmt_cnum(def_id) { | |
476ff2be SL |
696 | Some(cnum) => cnum, |
697 | None => return, | |
698 | }; | |
699 | let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX }; | |
0531ce1d | 700 | self.tcx.check_stability(def_id, Some(item.id), item.span); |
85aaf69f | 701 | } |
85aaf69f | 702 | |
476ff2be SL |
703 | // For implementations of traits, check the stability of each item |
704 | // individually as it's possible to have a stable trait with unstable | |
705 | // items. | |
706 | hir::ItemImpl(.., Some(ref t), _, ref impl_item_refs) => { | |
707 | if let Def::Trait(trait_did) = t.path.def { | |
708 | for impl_item_ref in impl_item_refs { | |
32a655c1 | 709 | let impl_item = self.tcx.hir.impl_item(impl_item_ref.id); |
476ff2be SL |
710 | let trait_item_def_id = self.tcx.associated_items(trait_did) |
711 | .find(|item| item.name == impl_item.name).map(|item| item.def_id); | |
712 | if let Some(def_id) = trait_item_def_id { | |
0531ce1d XL |
713 | // Pass `None` to skip deprecation warnings. |
714 | self.tcx.check_stability(def_id, None, impl_item.span); | |
9e0c209e | 715 | } |
9e0c209e | 716 | } |
476ff2be | 717 | } |
c34b1796 | 718 | } |
85aaf69f | 719 | |
7cac9316 XL |
720 | // There's no good place to insert stability check for non-Copy unions, |
721 | // so semi-randomly perform it here in stability.rs | |
0531ce1d | 722 | hir::ItemUnion(..) if !self.tcx.features().untagged_unions => { |
7cac9316 XL |
723 | let def_id = self.tcx.hir.local_def_id(item.id); |
724 | let adt_def = self.tcx.adt_def(def_id); | |
725 | let ty = self.tcx.type_of(def_id); | |
726 | ||
727 | if adt_def.has_dtor(self.tcx) { | |
728 | emit_feature_err(&self.tcx.sess.parse_sess, | |
729 | "untagged_unions", item.span, GateIssue::Language, | |
730 | "unions with `Drop` implementations are unstable"); | |
731 | } else { | |
732 | let param_env = self.tcx.param_env(def_id); | |
733 | if !param_env.can_type_implement_copy(self.tcx, ty, item.span).is_ok() { | |
734 | emit_feature_err(&self.tcx.sess.parse_sess, | |
735 | "untagged_unions", item.span, GateIssue::Language, | |
736 | "unions with non-`Copy` fields are unstable"); | |
737 | } | |
738 | } | |
739 | } | |
740 | ||
476ff2be | 741 | _ => (/* pass */) |
c34b1796 | 742 | } |
476ff2be | 743 | intravisit::walk_item(self, item); |
c34b1796 | 744 | } |
85aaf69f | 745 | |
476ff2be SL |
746 | fn visit_path(&mut self, path: &'tcx hir::Path, id: ast::NodeId) { |
747 | match path.def { | |
ea8adc8c | 748 | Def::Local(..) | Def::Upvar(..) | |
476ff2be | 749 | Def::PrimTy(..) | Def::SelfTy(..) | Def::Err => {} |
0531ce1d | 750 | _ => self.tcx.check_stability(path.def.def_id(), Some(id), path.span) |
85aaf69f | 751 | } |
476ff2be | 752 | intravisit::walk_path(self, path) |
1a4d82fc JJ |
753 | } |
754 | } | |
755 | ||
476ff2be | 756 | impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { |
a7813a04 | 757 | pub fn lookup_deprecation(self, id: DefId) -> Option<Deprecation> { |
5bcae85e SL |
758 | self.lookup_deprecation_entry(id).map(|depr| depr.attr) |
759 | } | |
1a4d82fc JJ |
760 | } |
761 | ||
85aaf69f SL |
762 | /// Given the list of enabled features that were not language features (i.e. that |
763 | /// were expected to be library features), and the list of features used from | |
764 | /// libraries, identify activated features that don't exist and error about them. | |
cc61c64b | 765 | pub fn check_unused_or_stable_features<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { |
7cac9316 | 766 | let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE); |
cc61c64b | 767 | |
ea8adc8c | 768 | if tcx.stability().staged_api[&LOCAL_CRATE] { |
32a655c1 | 769 | let krate = tcx.hir.krate(); |
476ff2be | 770 | let mut missing = MissingStabilityAnnotations { |
041b39d2 XL |
771 | tcx, |
772 | access_levels, | |
476ff2be SL |
773 | }; |
774 | missing.check_missing_stability(ast::CRATE_NODE_ID, krate.span); | |
775 | intravisit::walk_crate(&mut missing, krate); | |
776 | krate.visit_all_item_likes(&mut missing.as_deep_visitor()); | |
777 | } | |
778 | ||
0531ce1d | 779 | let ref declared_lib_features = tcx.features().declared_lib_features; |
476ff2be | 780 | let mut remaining_lib_features: FxHashMap<Symbol, Span> |
85aaf69f | 781 | = declared_lib_features.clone().into_iter().collect(); |
041b39d2 | 782 | remaining_lib_features.remove(&Symbol::intern("proc_macro")); |
85aaf69f | 783 | |
0531ce1d | 784 | for &(ref stable_lang_feature, span) in &tcx.features().declared_stable_lang_features { |
476ff2be | 785 | let version = find_lang_feature_accepted_version(&stable_lang_feature.as_str()) |
3157f602 | 786 | .expect("unexpectedly couldn't find version feature was stabilized"); |
3b2f2976 | 787 | tcx.lint_node(lint::builtin::STABLE_FEATURES, |
85aaf69f SL |
788 | ast::CRATE_NODE_ID, |
789 | span, | |
3b2f2976 | 790 | &format_stable_since_msg(version)); |
85aaf69f SL |
791 | } |
792 | ||
ea8adc8c XL |
793 | // FIXME(#44232) the `used_features` table no longer exists, so we don't |
794 | // lint about unknown or unused features. We should reenable | |
795 | // this one day! | |
796 | // | |
797 | // let index = tcx.stability(); | |
798 | // for (used_lib_feature, level) in &index.used_features { | |
799 | // remaining_lib_features.remove(used_lib_feature); | |
800 | // } | |
801 | // | |
802 | // for &span in remaining_lib_features.values() { | |
803 | // tcx.lint_node(lint::builtin::UNUSED_FEATURES, | |
804 | // ast::CRATE_NODE_ID, | |
805 | // span, | |
806 | // "unused or unknown feature"); | |
807 | // } | |
808 | } | |
85aaf69f | 809 | |
ea8adc8c XL |
810 | fn format_stable_since_msg(version: &str) -> String { |
811 | format!("this feature has been stable since {}. Attribute no longer needed", version) | |
1a4d82fc | 812 | } |