]>
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 | ||
85aaf69f SL |
14 | use session::Session; |
15 | use lint; | |
16 | use middle::def; | |
1a4d82fc | 17 | use middle::ty; |
85aaf69f | 18 | use middle::privacy::PublicItems; |
1a4d82fc | 19 | use metadata::csearch; |
85aaf69f SL |
20 | use syntax::parse::token::InternedString; |
21 | use syntax::codemap::{Span, DUMMY_SP}; | |
1a4d82fc JJ |
22 | use syntax::{attr, visit}; |
23 | use syntax::ast; | |
24 | use syntax::ast::{Attribute, Block, Crate, DefId, FnDecl, NodeId, Variant}; | |
c34b1796 | 25 | use syntax::ast::{Item, Generics, StructField}; |
62682a34 | 26 | use syntax::ast_util::{is_local, local_def}; |
85aaf69f | 27 | use syntax::attr::{Stability, AttrMetaMethods}; |
c34b1796 AL |
28 | use syntax::visit::{FnKind, Visitor}; |
29 | use syntax::feature_gate::emit_feature_err; | |
62682a34 | 30 | use util::nodemap::{DefIdMap, FnvHashSet, FnvHashMap}; |
1a4d82fc JJ |
31 | |
32 | use std::mem::replace; | |
c1a9b12d | 33 | use std::cmp::Ordering; |
1a4d82fc JJ |
34 | |
35 | /// A stability index, giving the stability level for items and methods. | |
62682a34 SL |
36 | pub struct Index<'tcx> { |
37 | /// This is mostly a cache, except the stabilities of local items | |
38 | /// are filled by the annotator. | |
39 | map: DefIdMap<Option<&'tcx Stability>>, | |
40 | ||
41 | /// Maps for each crate whether it is part of the staged API. | |
42 | staged_api: FnvHashMap<ast::CrateNum, bool> | |
1a4d82fc JJ |
43 | } |
44 | ||
45 | // A private tree-walker for producing an Index. | |
62682a34 SL |
46 | struct Annotator<'a, 'tcx: 'a> { |
47 | tcx: &'a ty::ctxt<'tcx>, | |
48 | index: &'a mut Index<'tcx>, | |
49 | parent: Option<&'tcx Stability>, | |
85aaf69f | 50 | export_map: &'a PublicItems, |
1a4d82fc JJ |
51 | } |
52 | ||
62682a34 | 53 | impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> { |
1a4d82fc JJ |
54 | // Determine the stability for a node based on its attributes and inherited |
55 | // stability. The stability is recorded in the index and used as the parent. | |
56 | fn annotate<F>(&mut self, id: NodeId, use_parent: bool, | |
85aaf69f | 57 | attrs: &Vec<Attribute>, item_sp: Span, f: F, required: bool) where |
1a4d82fc JJ |
58 | F: FnOnce(&mut Annotator), |
59 | { | |
62682a34 | 60 | if self.index.staged_api[&ast::LOCAL_CRATE] { |
9346a6ac | 61 | debug!("annotate(id = {:?}, attrs = {:?})", id, attrs); |
62682a34 | 62 | match attr::find_stability(self.tcx.sess.diagnostic(), attrs, item_sp) { |
c1a9b12d | 63 | Some(mut stab) => { |
9346a6ac | 64 | debug!("annotate: found {:?}", stab); |
c1a9b12d SL |
65 | // if parent is deprecated and we're not, inherit this by merging |
66 | // deprecated_since and its reason. | |
67 | if let Some(parent_stab) = self.parent { | |
68 | if parent_stab.deprecated_since.is_some() | |
69 | && stab.deprecated_since.is_none() { | |
70 | stab.deprecated_since = parent_stab.deprecated_since.clone(); | |
71 | stab.reason = parent_stab.reason.clone(); | |
72 | } | |
73 | } | |
74 | ||
62682a34 | 75 | let stab = self.tcx.intern_stability(stab); |
c1a9b12d SL |
76 | |
77 | // Check if deprecated_since < stable_since. If it is, | |
78 | // this is *almost surely* an accident. | |
79 | let deprecated_predates_stable = match (stab.deprecated_since.as_ref(), | |
80 | stab.since.as_ref()) { | |
81 | (Some(dep_since), Some(stab_since)) => { | |
82 | // explicit version of iter::order::lt to handle parse errors properly | |
83 | let mut is_less = false; | |
84 | for (dep_v, stab_v) in dep_since.split(".").zip(stab_since.split(".")) { | |
85 | match (dep_v.parse::<u64>(), stab_v.parse::<u64>()) { | |
86 | (Ok(dep_v), Ok(stab_v)) => match dep_v.cmp(&stab_v) { | |
87 | Ordering::Less => { | |
88 | is_less = true; | |
89 | break; | |
90 | } | |
91 | Ordering::Equal => { continue; } | |
92 | Ordering::Greater => { break; } | |
93 | }, | |
94 | _ => { | |
95 | self.tcx.sess.span_err(item_sp, | |
96 | "Invalid stability or deprecation version found"); | |
97 | // act like it isn't less because the question is now | |
98 | // nonsensical, and this makes us not do anything else | |
99 | // interesting. | |
100 | break; | |
101 | } | |
102 | } | |
103 | } | |
104 | is_less | |
105 | }, | |
106 | _ => false, | |
107 | }; | |
108 | ||
109 | if deprecated_predates_stable { | |
110 | self.tcx.sess.span_err(item_sp, | |
111 | "An API can't be stabilized after it is deprecated"); | |
112 | } | |
113 | ||
62682a34 | 114 | self.index.map.insert(local_def(id), Some(stab)); |
9346a6ac AL |
115 | |
116 | // Don't inherit #[stable(feature = "rust1", since = "1.0.0")] | |
117 | if stab.level != attr::Stable { | |
118 | let parent = replace(&mut self.parent, Some(stab)); | |
119 | f(self); | |
120 | self.parent = parent; | |
121 | } else { | |
122 | f(self); | |
123 | } | |
124 | } | |
125 | None => { | |
126 | debug!("annotate: not found, use_parent = {:?}, parent = {:?}", | |
127 | use_parent, self.parent); | |
128 | if use_parent { | |
62682a34 SL |
129 | if let Some(stab) = self.parent { |
130 | self.index.map.insert(local_def(id), Some(stab)); | |
131 | } else if self.index.staged_api[&ast::LOCAL_CRATE] && required | |
9346a6ac | 132 | && self.export_map.contains(&id) |
62682a34 SL |
133 | && !self.tcx.sess.opts.test { |
134 | self.tcx.sess.span_err(item_sp, | |
135 | "This node does not \ | |
136 | have a stability attribute"); | |
9346a6ac AL |
137 | } |
138 | } | |
1a4d82fc JJ |
139 | f(self); |
140 | } | |
141 | } | |
9346a6ac AL |
142 | } else { |
143 | // Emit warnings for non-staged-api crates. These should be errors. | |
144 | for attr in attrs { | |
145 | let tag = attr.name(); | |
146 | if tag == "unstable" || tag == "stable" || tag == "deprecated" { | |
147 | attr::mark_used(attr); | |
62682a34 | 148 | self.tcx.sess.span_err(attr.span(), |
9346a6ac AL |
149 | "stability attributes may not be used outside \ |
150 | of the standard library"); | |
1a4d82fc | 151 | } |
1a4d82fc | 152 | } |
9346a6ac | 153 | f(self); |
1a4d82fc JJ |
154 | } |
155 | } | |
156 | } | |
157 | ||
62682a34 | 158 | impl<'a, 'tcx, 'v> Visitor<'v> for Annotator<'a, 'tcx> { |
1a4d82fc JJ |
159 | fn visit_item(&mut self, i: &Item) { |
160 | // FIXME (#18969): the following is a hack around the fact | |
161 | // that we cannot currently annotate the stability of | |
162 | // `deriving`. Basically, we do *not* allow stability | |
163 | // inheritance on trait implementations, so that derived | |
164 | // implementations appear to be unannotated. This then allows | |
165 | // derived implementations to be automatically tagged with the | |
166 | // stability of the trait. This is WRONG, but expedient to get | |
167 | // libstd stabilized for the 1.0 release. | |
168 | let use_parent = match i.node { | |
169 | ast::ItemImpl(_, _, _, Some(_), _, _) => false, | |
170 | _ => true, | |
171 | }; | |
172 | ||
85aaf69f SL |
173 | // In case of a `pub use <mod>;`, we should not error since the stability |
174 | // is inherited from the module itself | |
175 | let required = match i.node { | |
176 | ast::ItemUse(_) => i.vis != ast::Public, | |
177 | _ => true | |
178 | }; | |
179 | ||
180 | self.annotate(i.id, use_parent, &i.attrs, i.span, | |
181 | |v| visit::walk_item(v, i), required); | |
1a4d82fc JJ |
182 | |
183 | if let ast::ItemStruct(ref sd, _) = i.node { | |
184 | sd.ctor_id.map(|id| { | |
85aaf69f | 185 | self.annotate(id, true, &i.attrs, i.span, |_| {}, true) |
1a4d82fc JJ |
186 | }); |
187 | } | |
188 | } | |
189 | ||
c34b1796 AL |
190 | fn visit_fn(&mut self, _: FnKind<'v>, _: &'v FnDecl, |
191 | _: &'v Block, _: Span, _: NodeId) { | |
1a4d82fc JJ |
192 | // Items defined in a function body have no reason to have |
193 | // a stability attribute, so we don't recurse. | |
194 | } | |
195 | ||
c34b1796 AL |
196 | fn visit_trait_item(&mut self, ti: &ast::TraitItem) { |
197 | self.annotate(ti.id, true, &ti.attrs, ti.span, | |
198 | |v| visit::walk_trait_item(v, ti), true); | |
199 | } | |
1a4d82fc | 200 | |
c34b1796 AL |
201 | fn visit_impl_item(&mut self, ii: &ast::ImplItem) { |
202 | self.annotate(ii.id, true, &ii.attrs, ii.span, | |
203 | |v| visit::walk_impl_item(v, ii), true); | |
1a4d82fc JJ |
204 | } |
205 | ||
206 | fn visit_variant(&mut self, var: &Variant, g: &'v Generics) { | |
85aaf69f SL |
207 | self.annotate(var.node.id, true, &var.node.attrs, var.span, |
208 | |v| visit::walk_variant(v, var, g), true) | |
1a4d82fc JJ |
209 | } |
210 | ||
211 | fn visit_struct_field(&mut self, s: &StructField) { | |
85aaf69f SL |
212 | self.annotate(s.node.id, true, &s.node.attrs, s.span, |
213 | |v| visit::walk_struct_field(v, s), true); | |
1a4d82fc JJ |
214 | } |
215 | ||
216 | fn visit_foreign_item(&mut self, i: &ast::ForeignItem) { | |
85aaf69f | 217 | self.annotate(i.id, true, &i.attrs, i.span, |_| {}, true); |
1a4d82fc JJ |
218 | } |
219 | } | |
220 | ||
62682a34 | 221 | impl<'tcx> Index<'tcx> { |
1a4d82fc | 222 | /// Construct the stability index for a crate being compiled. |
62682a34 | 223 | pub fn build(&mut self, tcx: &ty::ctxt<'tcx>, krate: &Crate, export_map: &PublicItems) { |
1a4d82fc | 224 | let mut annotator = Annotator { |
62682a34 | 225 | tcx: tcx, |
85aaf69f SL |
226 | index: self, |
227 | parent: None, | |
228 | export_map: export_map, | |
1a4d82fc | 229 | }; |
85aaf69f SL |
230 | annotator.annotate(ast::CRATE_NODE_ID, true, &krate.attrs, krate.span, |
231 | |v| visit::walk_crate(v, krate), true); | |
232 | } | |
233 | ||
234 | pub fn new(krate: &Crate) -> Index { | |
62682a34 | 235 | let mut is_staged_api = false; |
85aaf69f | 236 | for attr in &krate.attrs { |
c34b1796 | 237 | if &attr.name()[..] == "staged_api" { |
85aaf69f SL |
238 | match attr.node.value.node { |
239 | ast::MetaWord(_) => { | |
240 | attr::mark_used(attr); | |
62682a34 | 241 | is_staged_api = true; |
85aaf69f SL |
242 | } |
243 | _ => (/*pass*/) | |
244 | } | |
245 | } | |
246 | } | |
62682a34 SL |
247 | let mut staged_api = FnvHashMap(); |
248 | staged_api.insert(ast::LOCAL_CRATE, is_staged_api); | |
85aaf69f SL |
249 | Index { |
250 | staged_api: staged_api, | |
62682a34 | 251 | map: DefIdMap(), |
85aaf69f SL |
252 | } |
253 | } | |
254 | } | |
255 | ||
256 | /// Cross-references the feature names of unstable APIs with enabled | |
257 | /// features and possibly prints errors. Returns a list of all | |
258 | /// features used. | |
259 | pub fn check_unstable_api_usage(tcx: &ty::ctxt) | |
260 | -> FnvHashMap<InternedString, attr::StabilityLevel> { | |
261 | let ref active_lib_features = tcx.sess.features.borrow().declared_lib_features; | |
262 | ||
263 | // Put the active features into a map for quick lookup | |
264 | let active_features = active_lib_features.iter().map(|&(ref s, _)| s.clone()).collect(); | |
265 | ||
266 | let mut checker = Checker { | |
267 | tcx: tcx, | |
268 | active_features: active_features, | |
269 | used_features: FnvHashMap() | |
270 | }; | |
271 | ||
272 | let krate = tcx.map.krate(); | |
273 | visit::walk_crate(&mut checker, krate); | |
274 | ||
275 | let used_features = checker.used_features; | |
276 | return used_features; | |
277 | } | |
278 | ||
279 | struct Checker<'a, 'tcx: 'a> { | |
280 | tcx: &'a ty::ctxt<'tcx>, | |
281 | active_features: FnvHashSet<InternedString>, | |
282 | used_features: FnvHashMap<InternedString, attr::StabilityLevel> | |
283 | } | |
284 | ||
285 | impl<'a, 'tcx> Checker<'a, 'tcx> { | |
62682a34 | 286 | fn check(&mut self, id: ast::DefId, span: Span, stab: &Option<&Stability>) { |
85aaf69f SL |
287 | // Only the cross-crate scenario matters when checking unstable APIs |
288 | let cross_crate = !is_local(id); | |
289 | if !cross_crate { return } | |
290 | ||
291 | match *stab { | |
c1a9b12d | 292 | Some(&Stability { level: attr::Unstable, ref feature, ref reason, issue, .. }) => { |
85aaf69f SL |
293 | self.used_features.insert(feature.clone(), attr::Unstable); |
294 | ||
295 | if !self.active_features.contains(feature) { | |
c1a9b12d | 296 | let mut msg = match *reason { |
85aaf69f SL |
297 | Some(ref r) => format!("use of unstable library feature '{}': {}", |
298 | &feature, &r), | |
299 | None => format!("use of unstable library feature '{}'", &feature) | |
300 | }; | |
c1a9b12d SL |
301 | if let Some(n) = issue { |
302 | use std::fmt::Write; | |
303 | write!(&mut msg, " (see issue #{})", n).unwrap(); | |
304 | } | |
85aaf69f | 305 | |
c34b1796 | 306 | emit_feature_err(&self.tcx.sess.parse_sess.span_diagnostic, |
85aaf69f SL |
307 | &feature, span, &msg); |
308 | } | |
309 | } | |
62682a34 | 310 | Some(&Stability { level, ref feature, .. }) => { |
85aaf69f SL |
311 | self.used_features.insert(feature.clone(), level); |
312 | ||
313 | // Stable APIs are always ok to call and deprecated APIs are | |
314 | // handled by a lint. | |
315 | } | |
316 | None => { | |
317 | // This is an 'unmarked' API, which should not exist | |
318 | // in the standard library. | |
319 | if self.tcx.sess.features.borrow().unmarked_api { | |
320 | self.tcx.sess.span_warn(span, "use of unmarked library feature"); | |
321 | self.tcx.sess.span_note(span, "this is either a bug in the library you are \ | |
322 | using or a bug in the compiler - please \ | |
323 | report it in both places"); | |
324 | } else { | |
325 | self.tcx.sess.span_err(span, "use of unmarked library feature"); | |
326 | self.tcx.sess.span_note(span, "this is either a bug in the library you are \ | |
327 | using or a bug in the compiler - please \ | |
328 | report it in both places"); | |
329 | self.tcx.sess.span_note(span, "use #![feature(unmarked_api)] in the \ | |
330 | crate attributes to override this"); | |
331 | } | |
332 | } | |
333 | } | |
334 | } | |
335 | } | |
336 | ||
337 | impl<'a, 'v, 'tcx> Visitor<'v> for Checker<'a, 'tcx> { | |
338 | fn visit_item(&mut self, item: &ast::Item) { | |
339 | // When compiling with --test we don't enforce stability on the | |
340 | // compiler-generated test module, demarcated with `DUMMY_SP` plus the | |
341 | // name `__test` | |
c1a9b12d | 342 | if item.span == DUMMY_SP && item.ident.name == "__test" { return } |
85aaf69f SL |
343 | |
344 | check_item(self.tcx, item, true, | |
345 | &mut |id, sp, stab| self.check(id, sp, stab)); | |
346 | visit::walk_item(self, item); | |
347 | } | |
348 | ||
349 | fn visit_expr(&mut self, ex: &ast::Expr) { | |
350 | check_expr(self.tcx, ex, | |
351 | &mut |id, sp, stab| self.check(id, sp, stab)); | |
352 | visit::walk_expr(self, ex); | |
353 | } | |
354 | ||
355 | fn visit_path(&mut self, path: &ast::Path, id: ast::NodeId) { | |
356 | check_path(self.tcx, path, id, | |
357 | &mut |id, sp, stab| self.check(id, sp, stab)); | |
358 | visit::walk_path(self, path) | |
359 | } | |
c34b1796 AL |
360 | |
361 | fn visit_pat(&mut self, pat: &ast::Pat) { | |
362 | check_pat(self.tcx, pat, | |
363 | &mut |id, sp, stab| self.check(id, sp, stab)); | |
364 | visit::walk_pat(self, pat) | |
365 | } | |
85aaf69f SL |
366 | } |
367 | ||
368 | /// Helper for discovering nodes to check for stability | |
369 | pub fn check_item(tcx: &ty::ctxt, item: &ast::Item, warn_about_defns: bool, | |
62682a34 | 370 | cb: &mut FnMut(ast::DefId, Span, &Option<&Stability>)) { |
85aaf69f SL |
371 | match item.node { |
372 | ast::ItemExternCrate(_) => { | |
373 | // compiler-generated `extern crate` items have a dummy span. | |
374 | if item.span == DUMMY_SP { return } | |
375 | ||
376 | let cnum = match tcx.sess.cstore.find_extern_mod_stmt_cnum(item.id) { | |
377 | Some(cnum) => cnum, | |
378 | None => return, | |
379 | }; | |
380 | let id = ast::DefId { krate: cnum, node: ast::CRATE_NODE_ID }; | |
381 | maybe_do_stability_check(tcx, id, item.span, cb); | |
382 | } | |
383 | ||
384 | // For implementations of traits, check the stability of each item | |
385 | // individually as it's possible to have a stable trait with unstable | |
386 | // items. | |
387 | ast::ItemImpl(_, _, _, Some(ref t), _, ref impl_items) => { | |
c34b1796 | 388 | let trait_did = tcx.def_map.borrow().get(&t.ref_id).unwrap().def_id(); |
c1a9b12d | 389 | let trait_items = tcx.trait_items(trait_did); |
85aaf69f SL |
390 | |
391 | for impl_item in impl_items { | |
85aaf69f | 392 | let item = trait_items.iter().find(|item| { |
c34b1796 | 393 | item.name() == impl_item.ident.name |
85aaf69f SL |
394 | }).unwrap(); |
395 | if warn_about_defns { | |
c34b1796 | 396 | maybe_do_stability_check(tcx, item.def_id(), impl_item.span, cb); |
85aaf69f SL |
397 | } |
398 | } | |
399 | } | |
400 | ||
401 | _ => (/* pass */) | |
402 | } | |
403 | } | |
404 | ||
405 | /// Helper for discovering nodes to check for stability | |
406 | pub fn check_expr(tcx: &ty::ctxt, e: &ast::Expr, | |
62682a34 | 407 | cb: &mut FnMut(ast::DefId, Span, &Option<&Stability>)) { |
85aaf69f SL |
408 | let span; |
409 | let id = match e.node { | |
410 | ast::ExprMethodCall(i, _, _) => { | |
411 | span = i.span; | |
412 | let method_call = ty::MethodCall::expr(e.id); | |
c1a9b12d | 413 | tcx.tables.borrow().method_map[&method_call].def_id |
85aaf69f | 414 | } |
c34b1796 AL |
415 | ast::ExprField(ref base_e, ref field) => { |
416 | span = field.span; | |
c1a9b12d | 417 | match tcx.expr_ty_adjusted(base_e).sty { |
62682a34 | 418 | ty::TyStruct(did, _) => { |
c1a9b12d | 419 | tcx.lookup_struct_fields(did) |
c34b1796 AL |
420 | .iter() |
421 | .find(|f| f.name == field.node.name) | |
422 | .unwrap_or_else(|| { | |
423 | tcx.sess.span_bug(field.span, | |
424 | "stability::check_expr: unknown named field access") | |
425 | }) | |
426 | .id | |
427 | } | |
428 | _ => tcx.sess.span_bug(e.span, | |
429 | "stability::check_expr: named field access on non-struct") | |
430 | } | |
431 | } | |
432 | ast::ExprTupField(ref base_e, ref field) => { | |
433 | span = field.span; | |
c1a9b12d | 434 | match tcx.expr_ty_adjusted(base_e).sty { |
62682a34 | 435 | ty::TyStruct(did, _) => { |
c1a9b12d | 436 | tcx.lookup_struct_fields(did) |
c34b1796 AL |
437 | .get(field.node) |
438 | .unwrap_or_else(|| { | |
439 | tcx.sess.span_bug(field.span, | |
440 | "stability::check_expr: unknown unnamed field access") | |
441 | }) | |
442 | .id | |
443 | } | |
62682a34 | 444 | ty::TyTuple(..) => return, |
c34b1796 AL |
445 | _ => tcx.sess.span_bug(e.span, |
446 | "stability::check_expr: unnamed field access on \ | |
447 | something other than a tuple or struct") | |
448 | } | |
449 | } | |
450 | ast::ExprStruct(_, ref expr_fields, _) => { | |
c1a9b12d | 451 | let type_ = tcx.expr_ty(e); |
c34b1796 | 452 | match type_.sty { |
62682a34 | 453 | ty::TyStruct(did, _) => { |
c1a9b12d | 454 | let struct_fields = tcx.lookup_struct_fields(did); |
c34b1796 AL |
455 | // check the stability of each field that appears |
456 | // in the construction expression. | |
457 | for field in expr_fields { | |
458 | let did = struct_fields | |
459 | .iter() | |
460 | .find(|f| f.name == field.ident.node.name) | |
461 | .unwrap_or_else(|| { | |
462 | tcx.sess.span_bug(field.span, | |
463 | "stability::check_expr: unknown named \ | |
464 | field access") | |
465 | }) | |
466 | .id; | |
467 | maybe_do_stability_check(tcx, did, field.span, cb); | |
468 | } | |
469 | ||
470 | // we're done. | |
471 | return | |
472 | } | |
473 | // we don't look at stability attributes on | |
474 | // struct-like enums (yet...), but it's definitely not | |
475 | // a bug to have construct one. | |
62682a34 | 476 | ty::TyEnum(..) => return, |
c34b1796 AL |
477 | _ => { |
478 | tcx.sess.span_bug(e.span, | |
479 | &format!("stability::check_expr: struct construction \ | |
480 | of non-struct, type {:?}", | |
62682a34 | 481 | type_)); |
c34b1796 AL |
482 | } |
483 | } | |
484 | } | |
85aaf69f SL |
485 | _ => return |
486 | }; | |
487 | ||
488 | maybe_do_stability_check(tcx, id, span, cb); | |
489 | } | |
490 | ||
491 | pub fn check_path(tcx: &ty::ctxt, path: &ast::Path, id: ast::NodeId, | |
62682a34 | 492 | cb: &mut FnMut(ast::DefId, Span, &Option<&Stability>)) { |
c34b1796 AL |
493 | match tcx.def_map.borrow().get(&id).map(|d| d.full_def()) { |
494 | Some(def::DefPrimTy(..)) => {} | |
495 | Some(def) => { | |
496 | maybe_do_stability_check(tcx, def.def_id(), path.span, cb); | |
497 | } | |
498 | None => {} | |
499 | } | |
500 | ||
501 | } | |
502 | ||
503 | pub fn check_pat(tcx: &ty::ctxt, pat: &ast::Pat, | |
62682a34 | 504 | cb: &mut FnMut(ast::DefId, Span, &Option<&Stability>)) { |
c34b1796 AL |
505 | debug!("check_pat(pat = {:?})", pat); |
506 | if is_internal(tcx, pat.span) { return; } | |
507 | ||
c1a9b12d | 508 | let did = match tcx.pat_ty_opt(pat) { |
62682a34 | 509 | Some(&ty::TyS { sty: ty::TyStruct(did, _), .. }) => did, |
c34b1796 | 510 | Some(_) | None => return, |
85aaf69f | 511 | }; |
c1a9b12d | 512 | let struct_fields = tcx.lookup_struct_fields(did); |
c34b1796 AL |
513 | match pat.node { |
514 | // Foo(a, b, c) | |
515 | ast::PatEnum(_, Some(ref pat_fields)) => { | |
62682a34 | 516 | for (field, struct_field) in pat_fields.iter().zip(&struct_fields) { |
c34b1796 AL |
517 | // a .. pattern is fine, but anything positional is |
518 | // not. | |
519 | if let ast::PatWild(ast::PatWildMulti) = field.node { | |
520 | continue | |
521 | } | |
522 | maybe_do_stability_check(tcx, struct_field.id, field.span, cb) | |
523 | } | |
524 | } | |
525 | // Foo { a, b, c } | |
526 | ast::PatStruct(_, ref pat_fields, _) => { | |
527 | for field in pat_fields { | |
528 | let did = struct_fields | |
529 | .iter() | |
530 | .find(|f| f.name == field.node.ident.name) | |
531 | .unwrap_or_else(|| { | |
532 | tcx.sess.span_bug(field.span, | |
533 | "stability::check_pat: unknown named field access") | |
534 | }) | |
535 | .id; | |
536 | maybe_do_stability_check(tcx, did, field.span, cb); | |
537 | } | |
538 | } | |
539 | // everything else is fine. | |
540 | _ => {} | |
541 | } | |
85aaf69f SL |
542 | } |
543 | ||
544 | fn maybe_do_stability_check(tcx: &ty::ctxt, id: ast::DefId, span: Span, | |
62682a34 | 545 | cb: &mut FnMut(ast::DefId, Span, &Option<&Stability>)) { |
c1a9b12d SL |
546 | if !is_staged_api(tcx, id) { |
547 | debug!("maybe_do_stability_check: \ | |
548 | skipping id={:?} since it is not staged_api", id); | |
549 | return; | |
550 | } | |
551 | if is_internal(tcx, span) { | |
552 | debug!("maybe_do_stability_check: \ | |
553 | skipping span={:?} since it is internal", span); | |
554 | return; | |
555 | } | |
85aaf69f | 556 | let ref stability = lookup(tcx, id); |
c1a9b12d SL |
557 | debug!("maybe_do_stability_check: \ |
558 | inspecting id={:?} span={:?} of stability={:?}", id, span, stability); | |
85aaf69f SL |
559 | cb(id, span, stability); |
560 | } | |
561 | ||
562 | fn is_internal(tcx: &ty::ctxt, span: Span) -> bool { | |
c34b1796 | 563 | tcx.sess.codemap().span_allows_unstable(span) |
85aaf69f SL |
564 | } |
565 | ||
566 | fn is_staged_api(tcx: &ty::ctxt, id: DefId) -> bool { | |
c1a9b12d | 567 | match tcx.trait_item_of_item(id) { |
85aaf69f SL |
568 | Some(ty::MethodTraitItemId(trait_method_id)) |
569 | if trait_method_id != id => { | |
570 | is_staged_api(tcx, trait_method_id) | |
571 | } | |
85aaf69f | 572 | _ => { |
62682a34 SL |
573 | *tcx.stability.borrow_mut().staged_api.entry(id.krate).or_insert_with( |
574 | || csearch::is_staged_api(&tcx.sess.cstore, id.krate)) | |
85aaf69f | 575 | } |
1a4d82fc JJ |
576 | } |
577 | } | |
578 | ||
579 | /// Lookup the stability for a node, loading external crate | |
580 | /// metadata as necessary. | |
62682a34 SL |
581 | pub fn lookup<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> { |
582 | if let Some(st) = tcx.stability.borrow().map.get(&id) { | |
583 | return *st; | |
584 | } | |
585 | ||
586 | let st = lookup_uncached(tcx, id); | |
587 | tcx.stability.borrow_mut().map.insert(id, st); | |
588 | st | |
589 | } | |
590 | ||
591 | fn lookup_uncached<'tcx>(tcx: &ty::ctxt<'tcx>, id: DefId) -> Option<&'tcx Stability> { | |
592 | debug!("lookup(id={:?})", id); | |
1a4d82fc JJ |
593 | |
594 | // is this definition the implementation of a trait method? | |
c1a9b12d | 595 | match tcx.trait_item_of_item(id) { |
1a4d82fc JJ |
596 | Some(ty::MethodTraitItemId(trait_method_id)) if trait_method_id != id => { |
597 | debug!("lookup: trait_method_id={:?}", trait_method_id); | |
598 | return lookup(tcx, trait_method_id) | |
599 | } | |
600 | _ => {} | |
601 | } | |
602 | ||
603 | let item_stab = if is_local(id) { | |
62682a34 | 604 | None // The stability cache is filled partially lazily |
1a4d82fc | 605 | } else { |
62682a34 | 606 | csearch::get_stability(&tcx.sess.cstore, id).map(|st| tcx.intern_stability(st)) |
1a4d82fc JJ |
607 | }; |
608 | ||
609 | item_stab.or_else(|| { | |
c1a9b12d SL |
610 | if tcx.is_impl(id) { |
611 | if let Some(trait_id) = tcx.trait_id_of_impl(id) { | |
62682a34 SL |
612 | // FIXME (#18969): for the time being, simply use the |
613 | // stability of the trait to determine the stability of any | |
614 | // unmarked impls for it. See FIXME above for more details. | |
615 | ||
616 | debug!("lookup: trait_id={:?}", trait_id); | |
617 | return lookup(tcx, trait_id); | |
618 | } | |
1a4d82fc | 619 | } |
62682a34 | 620 | None |
1a4d82fc JJ |
621 | }) |
622 | } | |
623 | ||
85aaf69f SL |
624 | /// Given the list of enabled features that were not language features (i.e. that |
625 | /// were expected to be library features), and the list of features used from | |
626 | /// libraries, identify activated features that don't exist and error about them. | |
627 | pub fn check_unused_or_stable_features(sess: &Session, | |
628 | lib_features_used: &FnvHashMap<InternedString, | |
629 | attr::StabilityLevel>) { | |
630 | let ref declared_lib_features = sess.features.borrow().declared_lib_features; | |
631 | let mut remaining_lib_features: FnvHashMap<InternedString, Span> | |
632 | = declared_lib_features.clone().into_iter().collect(); | |
633 | ||
634 | let stable_msg = "this feature is stable. attribute no longer needed"; | |
635 | ||
62682a34 | 636 | for &span in &sess.features.borrow().declared_stable_lang_features { |
85aaf69f SL |
637 | sess.add_lint(lint::builtin::STABLE_FEATURES, |
638 | ast::CRATE_NODE_ID, | |
639 | span, | |
640 | stable_msg.to_string()); | |
641 | } | |
642 | ||
62682a34 | 643 | for (used_lib_feature, level) in lib_features_used { |
85aaf69f SL |
644 | match remaining_lib_features.remove(used_lib_feature) { |
645 | Some(span) => { | |
646 | if *level == attr::Stable { | |
647 | sess.add_lint(lint::builtin::STABLE_FEATURES, | |
648 | ast::CRATE_NODE_ID, | |
649 | span, | |
650 | stable_msg.to_string()); | |
651 | } | |
1a4d82fc | 652 | } |
85aaf69f | 653 | None => ( /* used but undeclared, handled during the previous ast visit */ ) |
1a4d82fc JJ |
654 | } |
655 | } | |
85aaf69f | 656 | |
62682a34 | 657 | for &span in remaining_lib_features.values() { |
85aaf69f SL |
658 | sess.add_lint(lint::builtin::UNUSED_FEATURES, |
659 | ast::CRATE_NODE_ID, | |
660 | span, | |
661 | "unused or unknown feature".to_string()); | |
662 | } | |
1a4d82fc | 663 | } |