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