]>
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 | ||
9cc50fc6 | 16 | use dep_graph::DepNode; |
54a0048b | 17 | use hir::map as hir_map; |
85aaf69f SL |
18 | use session::Session; |
19 | use lint; | |
92a42be0 | 20 | use middle::cstore::{CrateStore, LOCAL_CRATE}; |
54a0048b SL |
21 | use hir::def::Def; |
22 | use hir::def_id::{CRATE_DEF_INDEX, DefId}; | |
23 | use ty::{self, TyCtxt}; | |
92a42be0 | 24 | use middle::privacy::AccessLevels; |
85aaf69f SL |
25 | use syntax::parse::token::InternedString; |
26 | use syntax::codemap::{Span, DUMMY_SP}; | |
1a4d82fc | 27 | use syntax::ast; |
b039eaaf | 28 | use syntax::ast::{NodeId, Attribute}; |
e9174d1e | 29 | use syntax::feature_gate::{GateIssue, emit_feature_err}; |
9cc50fc6 | 30 | use syntax::attr::{self, Stability, Deprecation, AttrMetaMethods}; |
62682a34 | 31 | use util::nodemap::{DefIdMap, FnvHashSet, FnvHashMap}; |
1a4d82fc | 32 | |
54a0048b SL |
33 | use hir; |
34 | use hir::{Item, Generics, StructField, Variant, PatKind}; | |
35 | use hir::intravisit::{self, Visitor}; | |
e9174d1e | 36 | |
1a4d82fc | 37 | use std::mem::replace; |
c1a9b12d | 38 | use std::cmp::Ordering; |
1a4d82fc | 39 | |
b039eaaf SL |
40 | #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Copy, Debug, Eq, Hash)] |
41 | pub enum StabilityLevel { | |
42 | Unstable, | |
43 | Stable, | |
44 | } | |
45 | ||
46 | impl StabilityLevel { | |
47 | pub fn from_attr_level(level: &attr::StabilityLevel) -> Self { | |
48 | if level.is_stable() { Stable } else { Unstable } | |
49 | } | |
50 | } | |
51 | ||
92a42be0 SL |
52 | #[derive(PartialEq)] |
53 | enum AnnotationKind { | |
54 | // Annotation is required if not inherited from unstable parents | |
55 | Required, | |
56 | // Annotation is useless, reject it | |
57 | Prohibited, | |
58 | // Annotation itself is useless, but it can be propagated to children | |
59 | Container, | |
60 | } | |
61 | ||
1a4d82fc | 62 | /// A stability index, giving the stability level for items and methods. |
62682a34 SL |
63 | pub struct Index<'tcx> { |
64 | /// This is mostly a cache, except the stabilities of local items | |
65 | /// are filled by the annotator. | |
9cc50fc6 SL |
66 | stab_map: DefIdMap<Option<&'tcx Stability>>, |
67 | depr_map: DefIdMap<Option<Deprecation>>, | |
62682a34 SL |
68 | |
69 | /// Maps for each crate whether it is part of the staged API. | |
70 | staged_api: FnvHashMap<ast::CrateNum, bool> | |
1a4d82fc JJ |
71 | } |
72 | ||
73 | // A private tree-walker for producing an Index. | |
62682a34 | 74 | struct Annotator<'a, 'tcx: 'a> { |
54a0048b | 75 | tcx: &'a TyCtxt<'tcx>, |
62682a34 | 76 | index: &'a mut Index<'tcx>, |
9cc50fc6 SL |
77 | parent_stab: Option<&'tcx Stability>, |
78 | parent_depr: Option<Deprecation>, | |
92a42be0 SL |
79 | access_levels: &'a AccessLevels, |
80 | in_trait_impl: bool, | |
1a4d82fc JJ |
81 | } |
82 | ||
62682a34 | 83 | impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> { |
1a4d82fc JJ |
84 | // Determine the stability for a node based on its attributes and inherited |
85 | // stability. The stability is recorded in the index and used as the parent. | |
9cc50fc6 | 86 | fn annotate<F>(&mut self, id: NodeId, attrs: &[Attribute], |
92a42be0 SL |
87 | item_sp: Span, kind: AnnotationKind, visit_children: F) |
88 | where F: FnOnce(&mut Annotator) | |
1a4d82fc | 89 | { |
92a42be0 | 90 | if self.index.staged_api[&LOCAL_CRATE] && self.tcx.sess.features.borrow().staged_api { |
9346a6ac | 91 | debug!("annotate(id = {:?}, attrs = {:?})", id, attrs); |
9cc50fc6 SL |
92 | if let Some(..) = attr::find_deprecation(self.tcx.sess.diagnostic(), attrs, item_sp) { |
93 | self.tcx.sess.span_err(item_sp, "`#[deprecated]` cannot be used in staged api, \ | |
94 | use `#[rustc_deprecated]` instead"); | |
95 | } | |
92a42be0 SL |
96 | if let Some(mut stab) = attr::find_stability(self.tcx.sess.diagnostic(), |
97 | attrs, item_sp) { | |
98 | // Error if prohibited, or can't inherit anything from a container | |
99 | if kind == AnnotationKind::Prohibited || | |
100 | (kind == AnnotationKind::Container && | |
101 | stab.level.is_stable() && | |
9cc50fc6 | 102 | stab.rustc_depr.is_none()) { |
92a42be0 SL |
103 | self.tcx.sess.span_err(item_sp, "This stability annotation is useless"); |
104 | } | |
105 | ||
106 | debug!("annotate: found {:?}", stab); | |
107 | // If parent is deprecated and we're not, inherit this by merging | |
108 | // deprecated_since and its reason. | |
9cc50fc6 SL |
109 | if let Some(parent_stab) = self.parent_stab { |
110 | if parent_stab.rustc_depr.is_some() && stab.rustc_depr.is_none() { | |
111 | stab.rustc_depr = parent_stab.rustc_depr.clone() | |
c1a9b12d | 112 | } |
92a42be0 | 113 | } |
c1a9b12d | 114 | |
92a42be0 SL |
115 | let stab = self.tcx.intern_stability(stab); |
116 | ||
117 | // Check if deprecated_since < stable_since. If it is, | |
118 | // this is *almost surely* an accident. | |
9cc50fc6 SL |
119 | if let (&Some(attr::RustcDeprecation {since: ref dep_since, ..}), |
120 | &attr::Stable {since: ref stab_since}) = (&stab.rustc_depr, &stab.level) { | |
92a42be0 SL |
121 | // Explicit version of iter::order::lt to handle parse errors properly |
122 | for (dep_v, stab_v) in dep_since.split(".").zip(stab_since.split(".")) { | |
123 | if let (Ok(dep_v), Ok(stab_v)) = (dep_v.parse::<u64>(), stab_v.parse()) { | |
124 | match dep_v.cmp(&stab_v) { | |
125 | Ordering::Less => { | |
126 | self.tcx.sess.span_err(item_sp, "An API can't be stabilized \ | |
127 | after it is deprecated"); | |
128 | break | |
c1a9b12d | 129 | } |
92a42be0 SL |
130 | Ordering::Equal => continue, |
131 | Ordering::Greater => break, | |
c1a9b12d | 132 | } |
92a42be0 SL |
133 | } else { |
134 | // Act like it isn't less because the question is now nonsensical, | |
135 | // and this makes us not do anything else interesting. | |
136 | self.tcx.sess.span_err(item_sp, "Invalid stability or deprecation \ | |
137 | version found"); | |
138 | break | |
139 | } | |
c1a9b12d | 140 | } |
92a42be0 | 141 | } |
c1a9b12d | 142 | |
92a42be0 | 143 | let def_id = self.tcx.map.local_def_id(id); |
9cc50fc6 | 144 | self.index.stab_map.insert(def_id, Some(stab)); |
92a42be0 | 145 | |
9cc50fc6 | 146 | let orig_parent_stab = replace(&mut self.parent_stab, Some(stab)); |
92a42be0 | 147 | visit_children(self); |
9cc50fc6 | 148 | self.parent_stab = orig_parent_stab; |
92a42be0 | 149 | } else { |
9cc50fc6 | 150 | debug!("annotate: not found, parent = {:?}", self.parent_stab); |
92a42be0 SL |
151 | let mut is_error = kind == AnnotationKind::Required && |
152 | self.access_levels.is_reachable(id) && | |
153 | !self.tcx.sess.opts.test; | |
9cc50fc6 | 154 | if let Some(stab) = self.parent_stab { |
92a42be0 SL |
155 | if stab.level.is_unstable() { |
156 | let def_id = self.tcx.map.local_def_id(id); | |
9cc50fc6 | 157 | self.index.stab_map.insert(def_id, Some(stab)); |
92a42be0 | 158 | is_error = false; |
9346a6ac AL |
159 | } |
160 | } | |
92a42be0 SL |
161 | if is_error { |
162 | self.tcx.sess.span_err(item_sp, "This node does not have \ | |
163 | a stability attribute"); | |
1a4d82fc | 164 | } |
92a42be0 | 165 | visit_children(self); |
1a4d82fc | 166 | } |
9346a6ac | 167 | } else { |
92a42be0 | 168 | // Emit errors for non-staged-api crates. |
9346a6ac AL |
169 | for attr in attrs { |
170 | let tag = attr.name(); | |
92a42be0 | 171 | if tag == "unstable" || tag == "stable" || tag == "rustc_deprecated" { |
9346a6ac | 172 | attr::mark_used(attr); |
92a42be0 SL |
173 | self.tcx.sess.span_err(attr.span(), "stability attributes may not be used \ |
174 | outside of the standard library"); | |
1a4d82fc | 175 | } |
1a4d82fc | 176 | } |
9cc50fc6 SL |
177 | |
178 | if let Some(depr) = attr::find_deprecation(self.tcx.sess.diagnostic(), attrs, item_sp) { | |
179 | if kind == AnnotationKind::Prohibited { | |
180 | self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless"); | |
181 | } | |
182 | ||
183 | // `Deprecation` is just two pointers, no need to intern it | |
184 | let def_id = self.tcx.map.local_def_id(id); | |
185 | self.index.depr_map.insert(def_id, Some(depr.clone())); | |
186 | ||
187 | let orig_parent_depr = replace(&mut self.parent_depr, Some(depr)); | |
188 | visit_children(self); | |
189 | self.parent_depr = orig_parent_depr; | |
190 | } else if let Some(depr) = self.parent_depr.clone() { | |
191 | let def_id = self.tcx.map.local_def_id(id); | |
192 | self.index.depr_map.insert(def_id, Some(depr)); | |
193 | visit_children(self); | |
194 | } else { | |
195 | visit_children(self); | |
196 | } | |
1a4d82fc JJ |
197 | } |
198 | } | |
199 | } | |
200 | ||
62682a34 | 201 | impl<'a, 'tcx, 'v> Visitor<'v> for Annotator<'a, 'tcx> { |
92a42be0 SL |
202 | /// Because stability levels are scoped lexically, we want to walk |
203 | /// nested items in the context of the outer item, so enable | |
204 | /// deep-walking. | |
205 | fn visit_nested_item(&mut self, item: hir::ItemId) { | |
206 | self.visit_item(self.tcx.map.expect_item(item.id)) | |
207 | } | |
1a4d82fc | 208 | |
92a42be0 SL |
209 | fn visit_item(&mut self, i: &Item) { |
210 | let orig_in_trait_impl = self.in_trait_impl; | |
92a42be0 SL |
211 | let mut kind = AnnotationKind::Required; |
212 | match i.node { | |
213 | // Inherent impls and foreign modules serve only as containers for other items, | |
214 | // they don't have their own stability. They still can be annotated as unstable | |
215 | // and propagate this unstability to children, but this annotation is completely | |
216 | // optional. They inherit stability from their parents when unannotated. | |
217 | hir::ItemImpl(_, _, _, None, _, _) | hir::ItemForeignMod(..) => { | |
218 | self.in_trait_impl = false; | |
219 | kind = AnnotationKind::Container; | |
b039eaaf | 220 | } |
92a42be0 SL |
221 | hir::ItemImpl(_, _, _, Some(_), _, _) => { |
222 | self.in_trait_impl = true; | |
223 | } | |
224 | hir::ItemStruct(ref sd, _) => { | |
92a42be0 SL |
225 | if !sd.is_struct() { |
226 | self.annotate(sd.id(), &i.attrs, i.span, AnnotationKind::Required, |_| {}) | |
227 | } | |
228 | } | |
92a42be0 | 229 | _ => {} |
1a4d82fc | 230 | } |
1a4d82fc | 231 | |
92a42be0 SL |
232 | self.annotate(i.id, &i.attrs, i.span, kind, |v| { |
233 | intravisit::walk_item(v, i) | |
234 | }); | |
235 | self.in_trait_impl = orig_in_trait_impl; | |
1a4d82fc JJ |
236 | } |
237 | ||
e9174d1e | 238 | fn visit_trait_item(&mut self, ti: &hir::TraitItem) { |
92a42be0 SL |
239 | self.annotate(ti.id, &ti.attrs, ti.span, AnnotationKind::Required, |v| { |
240 | intravisit::walk_trait_item(v, ti); | |
241 | }); | |
c34b1796 | 242 | } |
1a4d82fc | 243 | |
e9174d1e | 244 | fn visit_impl_item(&mut self, ii: &hir::ImplItem) { |
92a42be0 SL |
245 | let kind = if self.in_trait_impl { |
246 | AnnotationKind::Prohibited | |
247 | } else { | |
248 | AnnotationKind::Required | |
249 | }; | |
250 | self.annotate(ii.id, &ii.attrs, ii.span, kind, |v| { | |
251 | intravisit::walk_impl_item(v, ii); | |
252 | }); | |
1a4d82fc JJ |
253 | } |
254 | ||
b039eaaf | 255 | fn visit_variant(&mut self, var: &Variant, g: &'v Generics, item_id: NodeId) { |
92a42be0 SL |
256 | self.annotate(var.node.data.id(), &var.node.attrs, var.span, AnnotationKind::Required, |v| { |
257 | intravisit::walk_variant(v, var, g, item_id); | |
258 | }) | |
1a4d82fc JJ |
259 | } |
260 | ||
261 | fn visit_struct_field(&mut self, s: &StructField) { | |
54a0048b | 262 | self.annotate(s.id, &s.attrs, s.span, AnnotationKind::Required, |v| { |
92a42be0 SL |
263 | intravisit::walk_struct_field(v, s); |
264 | }); | |
1a4d82fc JJ |
265 | } |
266 | ||
e9174d1e | 267 | fn visit_foreign_item(&mut self, i: &hir::ForeignItem) { |
92a42be0 SL |
268 | self.annotate(i.id, &i.attrs, i.span, AnnotationKind::Required, |v| { |
269 | intravisit::walk_foreign_item(v, i); | |
270 | }); | |
271 | } | |
272 | ||
273 | fn visit_macro_def(&mut self, md: &'v hir::MacroDef) { | |
274 | if md.imported_from.is_none() { | |
275 | self.annotate(md.id, &md.attrs, md.span, AnnotationKind::Required, |_| {}); | |
276 | } | |
1a4d82fc JJ |
277 | } |
278 | } | |
279 | ||
62682a34 | 280 | impl<'tcx> Index<'tcx> { |
1a4d82fc | 281 | /// Construct the stability index for a crate being compiled. |
54a0048b | 282 | pub fn build(&mut self, tcx: &TyCtxt<'tcx>, access_levels: &AccessLevels) { |
7453a54e SL |
283 | let _task = tcx.dep_graph.in_task(DepNode::StabilityIndex); |
284 | let krate = tcx.map.krate(); | |
1a4d82fc | 285 | let mut annotator = Annotator { |
62682a34 | 286 | tcx: tcx, |
85aaf69f | 287 | index: self, |
9cc50fc6 SL |
288 | parent_stab: None, |
289 | parent_depr: None, | |
92a42be0 SL |
290 | access_levels: access_levels, |
291 | in_trait_impl: false, | |
1a4d82fc | 292 | }; |
92a42be0 SL |
293 | annotator.annotate(ast::CRATE_NODE_ID, &krate.attrs, krate.span, AnnotationKind::Required, |
294 | |v| intravisit::walk_crate(v, krate)); | |
85aaf69f SL |
295 | } |
296 | ||
7453a54e SL |
297 | pub fn new(hir_map: &hir_map::Map) -> Index<'tcx> { |
298 | let _task = hir_map.dep_graph.in_task(DepNode::StabilityIndex); | |
299 | let krate = hir_map.krate(); | |
300 | ||
62682a34 | 301 | let mut is_staged_api = false; |
85aaf69f | 302 | for attr in &krate.attrs { |
92a42be0 SL |
303 | if attr.name() == "stable" || attr.name() == "unstable" { |
304 | is_staged_api = true; | |
305 | break | |
85aaf69f SL |
306 | } |
307 | } | |
92a42be0 | 308 | |
62682a34 | 309 | let mut staged_api = FnvHashMap(); |
e9174d1e | 310 | staged_api.insert(LOCAL_CRATE, is_staged_api); |
85aaf69f SL |
311 | Index { |
312 | staged_api: staged_api, | |
9cc50fc6 SL |
313 | stab_map: DefIdMap(), |
314 | depr_map: DefIdMap(), | |
85aaf69f SL |
315 | } |
316 | } | |
317 | } | |
318 | ||
319 | /// Cross-references the feature names of unstable APIs with enabled | |
320 | /// features and possibly prints errors. Returns a list of all | |
321 | /// features used. | |
54a0048b | 322 | pub fn check_unstable_api_usage(tcx: &TyCtxt) |
b039eaaf | 323 | -> FnvHashMap<InternedString, StabilityLevel> { |
9cc50fc6 | 324 | let _task = tcx.dep_graph.in_task(DepNode::StabilityCheck); |
85aaf69f SL |
325 | let ref active_lib_features = tcx.sess.features.borrow().declared_lib_features; |
326 | ||
327 | // Put the active features into a map for quick lookup | |
328 | let active_features = active_lib_features.iter().map(|&(ref s, _)| s.clone()).collect(); | |
329 | ||
330 | let mut checker = Checker { | |
331 | tcx: tcx, | |
332 | active_features: active_features, | |
b039eaaf SL |
333 | used_features: FnvHashMap(), |
334 | in_skip_block: 0, | |
85aaf69f | 335 | }; |
92a42be0 | 336 | intravisit::walk_crate(&mut checker, tcx.map.krate()); |
85aaf69f | 337 | |
9cc50fc6 | 338 | checker.used_features |
85aaf69f SL |
339 | } |
340 | ||
341 | struct Checker<'a, 'tcx: 'a> { | |
54a0048b | 342 | tcx: &'a TyCtxt<'tcx>, |
85aaf69f | 343 | active_features: FnvHashSet<InternedString>, |
b039eaaf SL |
344 | used_features: FnvHashMap<InternedString, StabilityLevel>, |
345 | // Within a block where feature gate checking can be skipped. | |
346 | in_skip_block: u32, | |
85aaf69f SL |
347 | } |
348 | ||
349 | impl<'a, 'tcx> Checker<'a, 'tcx> { | |
9cc50fc6 SL |
350 | fn check(&mut self, id: DefId, span: Span, |
351 | stab: &Option<&Stability>, _depr: &Option<Deprecation>) { | |
352 | if !is_staged_api(self.tcx, id) { | |
353 | return; | |
354 | } | |
85aaf69f | 355 | // Only the cross-crate scenario matters when checking unstable APIs |
e9174d1e | 356 | let cross_crate = !id.is_local(); |
b039eaaf SL |
357 | if !cross_crate { |
358 | return | |
359 | } | |
360 | ||
361 | // We don't need to check for stability - presumably compiler generated code. | |
362 | if self.in_skip_block > 0 { | |
363 | return; | |
364 | } | |
85aaf69f SL |
365 | |
366 | match *stab { | |
b039eaaf SL |
367 | Some(&Stability { level: attr::Unstable {ref reason, issue}, ref feature, .. }) => { |
368 | self.used_features.insert(feature.clone(), Unstable); | |
85aaf69f SL |
369 | |
370 | if !self.active_features.contains(feature) { | |
e9174d1e | 371 | let msg = match *reason { |
85aaf69f SL |
372 | Some(ref r) => format!("use of unstable library feature '{}': {}", |
373 | &feature, &r), | |
374 | None => format!("use of unstable library feature '{}'", &feature) | |
375 | }; | |
c34b1796 | 376 | emit_feature_err(&self.tcx.sess.parse_sess.span_diagnostic, |
b039eaaf | 377 | &feature, span, GateIssue::Library(Some(issue)), &msg); |
85aaf69f SL |
378 | } |
379 | } | |
b039eaaf SL |
380 | Some(&Stability { ref level, ref feature, .. }) => { |
381 | self.used_features.insert(feature.clone(), StabilityLevel::from_attr_level(level)); | |
85aaf69f SL |
382 | |
383 | // Stable APIs are always ok to call and deprecated APIs are | |
384 | // handled by a lint. | |
385 | } | |
386 | None => { | |
387 | // This is an 'unmarked' API, which should not exist | |
388 | // in the standard library. | |
389 | if self.tcx.sess.features.borrow().unmarked_api { | |
9cc50fc6 SL |
390 | self.tcx.sess.struct_span_warn(span, "use of unmarked library feature") |
391 | .span_note(span, "this is either a bug in the library you are \ | |
85aaf69f | 392 | using or a bug in the compiler - please \ |
9cc50fc6 SL |
393 | report it in both places") |
394 | .emit() | |
85aaf69f | 395 | } else { |
9cc50fc6 SL |
396 | self.tcx.sess.struct_span_err(span, "use of unmarked library feature") |
397 | .span_note(span, "this is either a bug in the library you are \ | |
85aaf69f | 398 | using or a bug in the compiler - please \ |
9cc50fc6 SL |
399 | report it in both places") |
400 | .span_note(span, "use #![feature(unmarked_api)] in the \ | |
401 | crate attributes to override this") | |
402 | .emit() | |
85aaf69f SL |
403 | } |
404 | } | |
405 | } | |
406 | } | |
407 | } | |
408 | ||
409 | impl<'a, 'v, 'tcx> Visitor<'v> for Checker<'a, 'tcx> { | |
92a42be0 SL |
410 | /// Because stability levels are scoped lexically, we want to walk |
411 | /// nested items in the context of the outer item, so enable | |
412 | /// deep-walking. | |
413 | fn visit_nested_item(&mut self, item: hir::ItemId) { | |
414 | self.visit_item(self.tcx.map.expect_item(item.id)) | |
415 | } | |
416 | ||
e9174d1e | 417 | fn visit_item(&mut self, item: &hir::Item) { |
85aaf69f SL |
418 | // When compiling with --test we don't enforce stability on the |
419 | // compiler-generated test module, demarcated with `DUMMY_SP` plus the | |
420 | // name `__test` | |
b039eaaf | 421 | if item.span == DUMMY_SP && item.name.as_str() == "__test" { return } |
85aaf69f SL |
422 | |
423 | check_item(self.tcx, item, true, | |
9cc50fc6 | 424 | &mut |id, sp, stab, depr| self.check(id, sp, stab, depr)); |
92a42be0 | 425 | intravisit::walk_item(self, item); |
85aaf69f SL |
426 | } |
427 | ||
e9174d1e | 428 | fn visit_expr(&mut self, ex: &hir::Expr) { |
85aaf69f | 429 | check_expr(self.tcx, ex, |
9cc50fc6 | 430 | &mut |id, sp, stab, depr| self.check(id, sp, stab, depr)); |
92a42be0 | 431 | intravisit::walk_expr(self, ex); |
85aaf69f SL |
432 | } |
433 | ||
e9174d1e | 434 | fn visit_path(&mut self, path: &hir::Path, id: ast::NodeId) { |
85aaf69f | 435 | check_path(self.tcx, path, id, |
9cc50fc6 | 436 | &mut |id, sp, stab, depr| self.check(id, sp, stab, depr)); |
92a42be0 | 437 | intravisit::walk_path(self, path) |
85aaf69f | 438 | } |
c34b1796 | 439 | |
b039eaaf SL |
440 | fn visit_path_list_item(&mut self, prefix: &hir::Path, item: &hir::PathListItem) { |
441 | check_path_list_item(self.tcx, item, | |
9cc50fc6 | 442 | &mut |id, sp, stab, depr| self.check(id, sp, stab, depr)); |
92a42be0 | 443 | intravisit::walk_path_list_item(self, prefix, item) |
b039eaaf SL |
444 | } |
445 | ||
e9174d1e | 446 | fn visit_pat(&mut self, pat: &hir::Pat) { |
c34b1796 | 447 | check_pat(self.tcx, pat, |
9cc50fc6 | 448 | &mut |id, sp, stab, depr| self.check(id, sp, stab, depr)); |
92a42be0 | 449 | intravisit::walk_pat(self, pat) |
c34b1796 | 450 | } |
b039eaaf SL |
451 | |
452 | fn visit_block(&mut self, b: &hir::Block) { | |
453 | let old_skip_count = self.in_skip_block; | |
454 | match b.rules { | |
455 | hir::BlockCheckMode::PushUnstableBlock => { | |
456 | self.in_skip_block += 1; | |
457 | } | |
458 | hir::BlockCheckMode::PopUnstableBlock => { | |
459 | self.in_skip_block = self.in_skip_block.checked_sub(1).unwrap(); | |
460 | } | |
461 | _ => {} | |
462 | } | |
92a42be0 | 463 | intravisit::walk_block(self, b); |
b039eaaf SL |
464 | self.in_skip_block = old_skip_count; |
465 | } | |
85aaf69f SL |
466 | } |
467 | ||
468 | /// Helper for discovering nodes to check for stability | |
54a0048b | 469 | pub fn check_item(tcx: &TyCtxt, item: &hir::Item, warn_about_defns: bool, |
9cc50fc6 | 470 | cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) { |
85aaf69f | 471 | match item.node { |
e9174d1e | 472 | hir::ItemExternCrate(_) => { |
85aaf69f SL |
473 | // compiler-generated `extern crate` items have a dummy span. |
474 | if item.span == DUMMY_SP { return } | |
475 | ||
92a42be0 | 476 | let cnum = match tcx.sess.cstore.extern_mod_stmt_cnum(item.id) { |
85aaf69f SL |
477 | Some(cnum) => cnum, |
478 | None => return, | |
479 | }; | |
b039eaaf | 480 | let id = DefId { krate: cnum, index: CRATE_DEF_INDEX }; |
85aaf69f SL |
481 | maybe_do_stability_check(tcx, id, item.span, cb); |
482 | } | |
483 | ||
484 | // For implementations of traits, check the stability of each item | |
485 | // individually as it's possible to have a stable trait with unstable | |
486 | // items. | |
e9174d1e | 487 | hir::ItemImpl(_, _, _, Some(ref t), _, ref impl_items) => { |
c34b1796 | 488 | let trait_did = tcx.def_map.borrow().get(&t.ref_id).unwrap().def_id(); |
c1a9b12d | 489 | let trait_items = tcx.trait_items(trait_did); |
85aaf69f SL |
490 | |
491 | for impl_item in impl_items { | |
85aaf69f | 492 | let item = trait_items.iter().find(|item| { |
b039eaaf | 493 | item.name() == impl_item.name |
85aaf69f SL |
494 | }).unwrap(); |
495 | if warn_about_defns { | |
c34b1796 | 496 | maybe_do_stability_check(tcx, item.def_id(), impl_item.span, cb); |
85aaf69f SL |
497 | } |
498 | } | |
499 | } | |
500 | ||
501 | _ => (/* pass */) | |
502 | } | |
503 | } | |
504 | ||
505 | /// Helper for discovering nodes to check for stability | |
54a0048b | 506 | pub fn check_expr(tcx: &TyCtxt, e: &hir::Expr, |
9cc50fc6 | 507 | cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) { |
85aaf69f SL |
508 | let span; |
509 | let id = match e.node { | |
e9174d1e | 510 | hir::ExprMethodCall(i, _, _) => { |
85aaf69f SL |
511 | span = i.span; |
512 | let method_call = ty::MethodCall::expr(e.id); | |
c1a9b12d | 513 | tcx.tables.borrow().method_map[&method_call].def_id |
85aaf69f | 514 | } |
e9174d1e | 515 | hir::ExprField(ref base_e, ref field) => { |
c34b1796 | 516 | span = field.span; |
c1a9b12d | 517 | match tcx.expr_ty_adjusted(base_e).sty { |
b039eaaf | 518 | ty::TyStruct(def, _) => def.struct_variant().field_named(field.node).did, |
54a0048b SL |
519 | _ => span_bug!(e.span, |
520 | "stability::check_expr: named field access on non-struct") | |
c34b1796 AL |
521 | } |
522 | } | |
e9174d1e | 523 | hir::ExprTupField(ref base_e, ref field) => { |
c34b1796 | 524 | span = field.span; |
c1a9b12d | 525 | match tcx.expr_ty_adjusted(base_e).sty { |
e9174d1e | 526 | ty::TyStruct(def, _) => def.struct_variant().fields[field.node].did, |
62682a34 | 527 | ty::TyTuple(..) => return, |
54a0048b SL |
528 | _ => span_bug!(e.span, |
529 | "stability::check_expr: unnamed field access on \ | |
530 | something other than a tuple or struct") | |
c34b1796 AL |
531 | } |
532 | } | |
e9174d1e | 533 | hir::ExprStruct(_, ref expr_fields, _) => { |
c1a9b12d | 534 | let type_ = tcx.expr_ty(e); |
c34b1796 | 535 | match type_.sty { |
e9174d1e | 536 | ty::TyStruct(def, _) => { |
c34b1796 AL |
537 | // check the stability of each field that appears |
538 | // in the construction expression. | |
539 | for field in expr_fields { | |
e9174d1e | 540 | let did = def.struct_variant() |
b039eaaf | 541 | .field_named(field.name.node) |
e9174d1e | 542 | .did; |
c34b1796 AL |
543 | maybe_do_stability_check(tcx, did, field.span, cb); |
544 | } | |
545 | ||
546 | // we're done. | |
547 | return | |
548 | } | |
549 | // we don't look at stability attributes on | |
550 | // struct-like enums (yet...), but it's definitely not | |
551 | // a bug to have construct one. | |
62682a34 | 552 | ty::TyEnum(..) => return, |
c34b1796 | 553 | _ => { |
54a0048b SL |
554 | span_bug!(e.span, |
555 | "stability::check_expr: struct construction \ | |
556 | of non-struct, type {:?}", | |
557 | type_); | |
c34b1796 AL |
558 | } |
559 | } | |
560 | } | |
85aaf69f SL |
561 | _ => return |
562 | }; | |
563 | ||
564 | maybe_do_stability_check(tcx, id, span, cb); | |
565 | } | |
566 | ||
54a0048b | 567 | pub fn check_path(tcx: &TyCtxt, path: &hir::Path, id: ast::NodeId, |
9cc50fc6 | 568 | cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) { |
c34b1796 | 569 | match tcx.def_map.borrow().get(&id).map(|d| d.full_def()) { |
7453a54e SL |
570 | Some(Def::PrimTy(..)) => {} |
571 | Some(Def::SelfTy(..)) => {} | |
c34b1796 AL |
572 | Some(def) => { |
573 | maybe_do_stability_check(tcx, def.def_id(), path.span, cb); | |
574 | } | |
575 | None => {} | |
576 | } | |
b039eaaf | 577 | } |
c34b1796 | 578 | |
54a0048b | 579 | pub fn check_path_list_item(tcx: &TyCtxt, item: &hir::PathListItem, |
9cc50fc6 | 580 | cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) { |
b039eaaf | 581 | match tcx.def_map.borrow().get(&item.node.id()).map(|d| d.full_def()) { |
7453a54e | 582 | Some(Def::PrimTy(..)) => {} |
b039eaaf SL |
583 | Some(def) => { |
584 | maybe_do_stability_check(tcx, def.def_id(), item.span, cb); | |
585 | } | |
586 | None => {} | |
587 | } | |
c34b1796 AL |
588 | } |
589 | ||
54a0048b | 590 | pub fn check_pat(tcx: &TyCtxt, pat: &hir::Pat, |
9cc50fc6 | 591 | cb: &mut FnMut(DefId, Span, &Option<&Stability>, &Option<Deprecation>)) { |
c34b1796 AL |
592 | debug!("check_pat(pat = {:?})", pat); |
593 | if is_internal(tcx, pat.span) { return; } | |
594 | ||
e9174d1e SL |
595 | let v = match tcx.pat_ty_opt(pat) { |
596 | Some(&ty::TyS { sty: ty::TyStruct(def, _), .. }) => def.struct_variant(), | |
c34b1796 | 597 | Some(_) | None => return, |
85aaf69f | 598 | }; |
c34b1796 AL |
599 | match pat.node { |
600 | // Foo(a, b, c) | |
7453a54e SL |
601 | // A Variant(..) pattern `PatKind::TupleStruct(_, None)` doesn't have to be recursed into. |
602 | PatKind::TupleStruct(_, Some(ref pat_fields)) => { | |
e9174d1e | 603 | for (field, struct_field) in pat_fields.iter().zip(&v.fields) { |
e9174d1e | 604 | maybe_do_stability_check(tcx, struct_field.did, field.span, cb) |
c34b1796 AL |
605 | } |
606 | } | |
607 | // Foo { a, b, c } | |
7453a54e | 608 | PatKind::Struct(_, ref pat_fields, _) => { |
c34b1796 | 609 | for field in pat_fields { |
b039eaaf | 610 | let did = v.field_named(field.node.name).did; |
c34b1796 AL |
611 | maybe_do_stability_check(tcx, did, field.span, cb); |
612 | } | |
613 | } | |
614 | // everything else is fine. | |
615 | _ => {} | |
616 | } | |
85aaf69f SL |
617 | } |
618 | ||
54a0048b | 619 | fn maybe_do_stability_check(tcx: &TyCtxt, id: DefId, span: Span, |
9cc50fc6 SL |
620 | cb: &mut FnMut(DefId, Span, |
621 | &Option<&Stability>, &Option<Deprecation>)) { | |
c1a9b12d SL |
622 | if is_internal(tcx, span) { |
623 | debug!("maybe_do_stability_check: \ | |
624 | skipping span={:?} since it is internal", span); | |
625 | return; | |
626 | } | |
9cc50fc6 SL |
627 | let (stability, deprecation) = if is_staged_api(tcx, id) { |
628 | (lookup_stability(tcx, id), None) | |
629 | } else { | |
630 | (None, lookup_deprecation(tcx, id)) | |
631 | }; | |
c1a9b12d SL |
632 | debug!("maybe_do_stability_check: \ |
633 | inspecting id={:?} span={:?} of stability={:?}", id, span, stability); | |
9cc50fc6 | 634 | cb(id, span, &stability, &deprecation); |
85aaf69f SL |
635 | } |
636 | ||
54a0048b | 637 | fn is_internal(tcx: &TyCtxt, span: Span) -> bool { |
c34b1796 | 638 | tcx.sess.codemap().span_allows_unstable(span) |
85aaf69f SL |
639 | } |
640 | ||
54a0048b | 641 | fn is_staged_api(tcx: &TyCtxt, id: DefId) -> bool { |
c1a9b12d | 642 | match tcx.trait_item_of_item(id) { |
85aaf69f SL |
643 | Some(ty::MethodTraitItemId(trait_method_id)) |
644 | if trait_method_id != id => { | |
645 | is_staged_api(tcx, trait_method_id) | |
646 | } | |
85aaf69f | 647 | _ => { |
62682a34 | 648 | *tcx.stability.borrow_mut().staged_api.entry(id.krate).or_insert_with( |
92a42be0 | 649 | || tcx.sess.cstore.is_staged_api(id.krate)) |
85aaf69f | 650 | } |
1a4d82fc JJ |
651 | } |
652 | } | |
653 | ||
654 | /// Lookup the stability for a node, loading external crate | |
655 | /// metadata as necessary. | |
54a0048b | 656 | pub fn lookup_stability<'tcx>(tcx: &TyCtxt<'tcx>, id: DefId) -> Option<&'tcx Stability> { |
9cc50fc6 | 657 | if let Some(st) = tcx.stability.borrow().stab_map.get(&id) { |
62682a34 SL |
658 | return *st; |
659 | } | |
660 | ||
9cc50fc6 SL |
661 | let st = lookup_stability_uncached(tcx, id); |
662 | tcx.stability.borrow_mut().stab_map.insert(id, st); | |
62682a34 SL |
663 | st |
664 | } | |
665 | ||
54a0048b | 666 | pub fn lookup_deprecation<'tcx>(tcx: &TyCtxt<'tcx>, id: DefId) -> Option<Deprecation> { |
9cc50fc6 SL |
667 | if let Some(depr) = tcx.stability.borrow().depr_map.get(&id) { |
668 | return depr.clone(); | |
1a4d82fc JJ |
669 | } |
670 | ||
9cc50fc6 SL |
671 | let depr = lookup_deprecation_uncached(tcx, id); |
672 | tcx.stability.borrow_mut().depr_map.insert(id, depr.clone()); | |
673 | depr | |
674 | } | |
675 | ||
54a0048b | 676 | fn lookup_stability_uncached<'tcx>(tcx: &TyCtxt<'tcx>, id: DefId) -> Option<&'tcx Stability> { |
9cc50fc6 SL |
677 | debug!("lookup(id={:?})", id); |
678 | if id.is_local() { | |
62682a34 | 679 | None // The stability cache is filled partially lazily |
1a4d82fc | 680 | } else { |
92a42be0 | 681 | tcx.sess.cstore.stability(id).map(|st| tcx.intern_stability(st)) |
9cc50fc6 SL |
682 | } |
683 | } | |
62682a34 | 684 | |
54a0048b | 685 | fn lookup_deprecation_uncached<'tcx>(tcx: &TyCtxt<'tcx>, id: DefId) -> Option<Deprecation> { |
9cc50fc6 SL |
686 | debug!("lookup(id={:?})", id); |
687 | if id.is_local() { | |
688 | None // The stability cache is filled partially lazily | |
689 | } else { | |
690 | tcx.sess.cstore.deprecation(id) | |
691 | } | |
1a4d82fc JJ |
692 | } |
693 | ||
85aaf69f SL |
694 | /// Given the list of enabled features that were not language features (i.e. that |
695 | /// were expected to be library features), and the list of features used from | |
696 | /// libraries, identify activated features that don't exist and error about them. | |
697 | pub fn check_unused_or_stable_features(sess: &Session, | |
698 | lib_features_used: &FnvHashMap<InternedString, | |
b039eaaf | 699 | StabilityLevel>) { |
85aaf69f SL |
700 | let ref declared_lib_features = sess.features.borrow().declared_lib_features; |
701 | let mut remaining_lib_features: FnvHashMap<InternedString, Span> | |
702 | = declared_lib_features.clone().into_iter().collect(); | |
703 | ||
704 | let stable_msg = "this feature is stable. attribute no longer needed"; | |
705 | ||
62682a34 | 706 | for &span in &sess.features.borrow().declared_stable_lang_features { |
85aaf69f SL |
707 | sess.add_lint(lint::builtin::STABLE_FEATURES, |
708 | ast::CRATE_NODE_ID, | |
709 | span, | |
710 | stable_msg.to_string()); | |
711 | } | |
712 | ||
62682a34 | 713 | for (used_lib_feature, level) in lib_features_used { |
85aaf69f SL |
714 | match remaining_lib_features.remove(used_lib_feature) { |
715 | Some(span) => { | |
b039eaaf | 716 | if *level == Stable { |
85aaf69f SL |
717 | sess.add_lint(lint::builtin::STABLE_FEATURES, |
718 | ast::CRATE_NODE_ID, | |
719 | span, | |
720 | stable_msg.to_string()); | |
721 | } | |
1a4d82fc | 722 | } |
85aaf69f | 723 | None => ( /* used but undeclared, handled during the previous ast visit */ ) |
1a4d82fc JJ |
724 | } |
725 | } | |
85aaf69f | 726 | |
62682a34 | 727 | for &span in remaining_lib_features.values() { |
85aaf69f SL |
728 | sess.add_lint(lint::builtin::UNUSED_FEATURES, |
729 | ast::CRATE_NODE_ID, | |
730 | span, | |
731 | "unused or unknown feature".to_string()); | |
732 | } | |
1a4d82fc | 733 | } |