1 // Copyright 2012-2013 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.
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.
11 //! A 'lint' check is a kind of miscellaneous constraint that a user _might_
12 //! want to enforce, but might reasonably want to permit as well, on a
13 //! module-by-module basis. They contrast with static constraints enforced by
14 //! other phases of the compiler, which are generally required to hold in order
15 //! to compile the program at all.
17 //! The lint checking is all consolidated into one pass which runs just before
18 //! translation to LLVM bytecode. Throughout compilation, lint warnings can be
19 //! added via the `add_lint` method on the Session structure. This requires a
20 //! span and an id of the node that the lint is being added to. The lint isn't
21 //! actually emitted at that time because it is unknown what the actual lint
22 //! level at that location is.
24 //! To actually emit lint warnings/errors, a separate pass is used just before
25 //! translation. A context keeps track of the current state of all lint levels.
26 //! Upon entering a node of the ast which can modify the lint settings, the
27 //! previous lint state is pushed onto a stack and the ast is then recursed
28 //! upon. As the ast is traversed, this keeps track of the current lint level
29 //! for all lint attributes.
31 //! To add a new lint warning, all you need to do is to either invoke `add_lint`
32 //! on the session at the appropriate time, or write a few linting functions and
33 //! modify the Context visitor appropriately. If you're adding lints from the
34 //! Context itself, span_lint should be used instead of add_lint.
37 use middle::trans::adt; // for `adt::is_ffi_safe`
40 use metadata::csearch;
41 use util::ppaux::{ty_to_str};
44 use std::hashmap::HashMap;
53 use extra::smallintmap::SmallIntMap;
56 use syntax::attr::{AttrMetaMethods, AttributeMethods};
57 use syntax::codemap::Span;
59 use syntax::parse::token;
60 use syntax::{ast, ast_util, visit};
61 use syntax::visit::Visitor;
63 #[deriving(Clone, Eq)]
68 unnecessary_qualification,
73 non_uppercase_statics,
74 non_uppercase_pattern_statics,
85 unnecessary_allocation,
97 pub fn level_to_str(lv: level) -> &'static str {
106 #[deriving(Clone, Eq, Ord)]
108 allow, warn, deny, forbid
111 #[deriving(Clone, Eq)]
112 pub struct LintSpec {
118 impl Ord for LintSpec {
119 fn lt(&self, other: &LintSpec) -> bool { self.default < other.default }
122 pub type LintDict = HashMap<&'static str, LintSpec>;
131 static lint_table: &'static [(&'static str, LintSpec)] = &[
135 desc: "proper use of std::libc types in foreign modules",
142 desc: "only invoke foreign functions from fixedstacksegment fns",
148 lint: unused_imports,
149 desc: "imports that are never used",
153 ("unnecessary_qualification",
155 lint: unnecessary_qualification,
156 desc: "detects unnecessarily qualified names",
163 desc: "suggest using loop { } instead of while(true) { }",
169 lint: path_statement,
170 desc: "path statements with no effect",
174 ("unrecognized_lint",
176 lint: unrecognized_lint,
177 desc: "unrecognized lint attribute",
181 ("non_camel_case_types",
183 lint: non_camel_case_types,
184 desc: "types, variants and traits should have camel case names",
188 ("non_uppercase_statics",
190 lint: non_uppercase_statics,
191 desc: "static constants should have uppercase identifiers",
195 ("non_uppercase_pattern_statics",
197 lint: non_uppercase_pattern_statics,
198 desc: "static constants in match patterns should be all caps",
202 ("managed_heap_memory",
204 lint: managed_heap_memory,
205 desc: "use of managed (@ type) heap memory",
209 ("owned_heap_memory",
211 lint: owned_heap_memory,
212 desc: "use of owned (~ type) heap memory",
219 desc: "use of any (~ type or @ type) heap memory",
226 desc: "comparisons made useless by limits of the types involved",
233 desc: "unnecessary use of an `unsafe` block",
239 lint: unused_variable,
240 desc: "detect variables which are not used in any way",
246 lint: dead_assignment,
247 desc: "detect assignments that will never be read",
254 desc: "detect mut variables which don't need to be mutable",
258 ("unnecessary_allocation",
260 lint: unnecessary_allocation,
261 desc: "detects unnecessary allocations that can be eliminated",
268 desc: "detects missing documentation for public members",
274 lint: unreachable_code,
275 desc: "detects unreachable code",
282 desc: "detects use of #[deprecated] items",
289 desc: "detects use of #[experimental] items",
296 desc: "detects use of #[unstable] items (incl. items with no stability attribute)",
303 desc: "mass-change the level for lints which produce warnings",
309 Pass names should not contain a '-', as the compiler normalizes
310 '-' to '_' in command-line flags
312 pub fn get_lint_dict() -> LintDict {
313 let mut map = HashMap::new();
314 for &(k, v) in lint_table.iter() {
321 // All known lint modes (string versions)
323 // Current levels of each lint warning
324 cur: SmallIntMap<(level, LintSource)>,
325 // context we're checking in (used to access fields like sess)
328 // When recursing into an attributed node of the ast which modifies lint
329 // levels, this stack keeps track of the previous lint levels of whatever
331 lint_stack: ~[(lint, level, LintSource)],
335 fn get_level(&self, lint: lint) -> level {
336 match self.cur.find(&(lint as uint)) {
337 Some(&(lvl, _)) => lvl,
342 fn get_source(&self, lint: lint) -> LintSource {
343 match self.cur.find(&(lint as uint)) {
344 Some(&(_, src)) => src,
349 fn set_level(&mut self, lint: lint, level: level, src: LintSource) {
351 self.cur.remove(&(lint as uint));
353 self.cur.insert(lint as uint, (level, src));
357 fn lint_to_str(&self, lint: lint) -> &'static str {
358 for (k, v) in self.dict.iter() {
363 fail!("unregistered lint {:?}", lint);
366 fn span_lint(&self, lint: lint, span: Span, msg: &str) {
367 let (level, src) = match self.cur.find(&(lint as uint)) {
369 Some(&(warn, src)) => (self.get_level(warnings), src),
372 if level == allow { return }
375 let msg = match src {
377 format!("{}, \\#[{}({})] on by default", msg,
378 level_to_str(level), self.lint_to_str(lint))
381 format!("{} [-{} {}]", msg,
383 warn => 'W', deny => 'D', forbid => 'F',
385 }, self.lint_to_str(lint).replace("_", "-"))
393 warn => { self.tcx.sess.span_warn(span, msg); }
394 deny | forbid => { self.tcx.sess.span_err(span, msg); }
398 for &span in note.iter() {
399 self.tcx.sess.span_note(span, "lint level defined here");
404 * Merge the lints specified by any lint attributes into the
405 * current lint context, call the provided function, then reset the
406 * lints in effect to their previous state.
408 fn with_lint_attrs(&mut self, attrs: &[ast::Attribute],
409 f: &fn(&mut Context)) {
410 // Parse all of the lint attributes, and then add them all to the
411 // current dictionary of lint information. Along the way, keep a history
412 // of what we changed so we can roll everything back after invoking the
415 do each_lint(self.tcx.sess, attrs) |meta, level, lintname| {
416 match self.dict.find_equiv(&lintname) {
421 format!("unknown `{}` attribute: `{}`",
422 level_to_str(level), lintname));
425 let lint = lint.lint;
426 let now = self.get_level(lint);
427 if now == forbid && level != forbid {
428 self.tcx.sess.span_err(meta.span,
429 format!("{}({}) overruled by outer forbid({})",
431 lintname, lintname));
432 } else if now != level {
433 let src = self.get_source(lint);
434 self.lint_stack.push((lint, now, src));
436 self.set_level(lint, level, Node(meta.span));
447 let (lint, lvl, src) = self.lint_stack.pop();
448 self.set_level(lint, lvl, src);
452 fn visit_ids(&self, f: &fn(&mut ast_util::IdVisitor<Context>)) {
453 let mut v = ast_util::IdVisitor {
455 pass_through_items: false,
456 visited_outermost: false,
462 pub fn each_lint(sess: session::Session,
463 attrs: &[ast::Attribute],
464 f: &fn(@ast::MetaItem, level, @str) -> bool) -> bool {
465 let xs = [allow, warn, deny, forbid];
466 for &level in xs.iter() {
467 let level_name = level_to_str(level);
468 for attr in attrs.iter().filter(|m| level_name == m.name()) {
469 let meta = attr.node.value;
470 let metas = match meta.node {
471 ast::MetaList(_, ref metas) => metas,
473 sess.span_err(meta.span, "malformed lint attribute");
477 for meta in metas.iter() {
479 ast::MetaWord(lintname) => {
480 if !f(*meta, level, lintname) {
485 sess.span_err(meta.span, "malformed lint attribute");
494 fn check_while_true_expr(cx: &Context, e: &ast::Expr) {
496 ast::ExprWhile(cond, _) => {
498 ast::ExprLit(@codemap::Spanned {
499 node: ast::lit_bool(true), _}) =>
501 cx.span_lint(while_true, e.span,
502 "denote infinite loops with loop { ... }");
511 fn check_type_limits(cx: &Context, e: &ast::Expr) {
512 return match e.node {
513 ast::ExprBinary(_, binop, l, r) => {
514 if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) {
515 cx.span_lint(type_limits, e.span,
516 "comparison is useless due to type limits");
522 fn is_valid<T:cmp::Ord>(binop: ast::BinOp, v: T,
523 min: T, max: T) -> bool {
525 ast::BiLt => v <= max,
526 ast::BiLe => v < max,
527 ast::BiGt => v >= min,
528 ast::BiGe => v > min,
529 ast::BiEq | ast::BiNe => v >= min && v <= max,
534 fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
536 ast::BiLt => ast::BiGt,
537 ast::BiLe => ast::BiGe,
538 ast::BiGt => ast::BiLt,
539 ast::BiGe => ast::BiLe,
544 // for int & uint, be conservative with the warnings, so that the
545 // warnings are consistent between 32- and 64-bit platforms
546 fn int_ty_range(int_ty: ast::int_ty) -> (i64, i64) {
548 ast::ty_i => (i64::min_value, i64::max_value),
549 ast::ty_i8 => (i8::min_value as i64, i8::max_value as i64),
550 ast::ty_i16 => (i16::min_value as i64, i16::max_value as i64),
551 ast::ty_i32 => (i32::min_value as i64, i32::max_value as i64),
552 ast::ty_i64 => (i64::min_value, i64::max_value)
556 fn uint_ty_range(uint_ty: ast::uint_ty) -> (u64, u64) {
558 ast::ty_u => (u64::min_value, u64::max_value),
559 ast::ty_u8 => (u8::min_value as u64, u8::max_value as u64),
560 ast::ty_u16 => (u16::min_value as u64, u16::max_value as u64),
561 ast::ty_u32 => (u32::min_value as u64, u32::max_value as u64),
562 ast::ty_u64 => (u64::min_value, u64::max_value)
566 fn check_limits(tcx: ty::ctxt, binop: ast::BinOp,
567 l: &ast::Expr, r: &ast::Expr) -> bool {
568 let (lit, expr, swap) = match (&l.node, &r.node) {
569 (&ast::ExprLit(_), _) => (l, r, true),
570 (_, &ast::ExprLit(_)) => (r, l, false),
573 // Normalize the binop so that the literal is always on the RHS in
575 let norm_binop = if swap { rev_binop(binop) } else { binop };
576 match ty::get(ty::expr_ty(tcx, expr)).sty {
577 ty::ty_int(int_ty) => {
578 let (min, max) = int_ty_range(int_ty);
579 let lit_val: i64 = match lit.node {
580 ast::ExprLit(li) => match li.node {
581 ast::lit_int(v, _) => v,
582 ast::lit_uint(v, _) => v as i64,
583 ast::lit_int_unsuffixed(v) => v,
588 is_valid(norm_binop, lit_val, min, max)
590 ty::ty_uint(uint_ty) => {
591 let (min, max): (u64, u64) = uint_ty_range(uint_ty);
592 let lit_val: u64 = match lit.node {
593 ast::ExprLit(li) => match li.node {
594 ast::lit_int(v, _) => v as u64,
595 ast::lit_uint(v, _) => v,
596 ast::lit_int_unsuffixed(v) => v as u64,
601 is_valid(norm_binop, lit_val, min, max)
607 fn is_comparison(binop: ast::BinOp) -> bool {
609 ast::BiEq | ast::BiLt | ast::BiLe |
610 ast::BiNe | ast::BiGe | ast::BiGt => true,
616 fn check_item_ctypes(cx: &Context, it: &ast::item) {
617 fn check_ty(cx: &Context, ty: &ast::Ty) {
619 ast::ty_path(_, _, id) => {
620 match cx.tcx.def_map.get_copy(&id) {
621 ast::DefPrimTy(ast::ty_int(ast::ty_i)) => {
622 cx.span_lint(ctypes, ty.span,
623 "found rust type `int` in foreign module, while \
624 libc::c_int or libc::c_long should be used");
626 ast::DefPrimTy(ast::ty_uint(ast::ty_u)) => {
627 cx.span_lint(ctypes, ty.span,
628 "found rust type `uint` in foreign module, while \
629 libc::c_uint or libc::c_ulong should be used");
631 ast::DefTy(def_id) => {
632 if !adt::is_ffi_safe(cx.tcx, def_id) {
633 cx.span_lint(ctypes, ty.span,
634 "found enum type without foreign-function-safe \
635 representation annotation in foreign module");
636 // NOTE this message could be more helpful
642 ast::ty_ptr(ref mt) => { check_ty(cx, mt.ty) }
647 fn check_foreign_fn(cx: &Context, decl: &ast::fn_decl) {
648 for input in decl.inputs.iter() {
649 check_ty(cx, &input.ty);
651 check_ty(cx, &decl.output)
655 ast::item_foreign_mod(ref nmod) if !nmod.abis.is_intrinsic() => {
656 for ni in nmod.items.iter() {
658 ast::foreign_item_fn(ref decl, _) => {
659 check_foreign_fn(cx, decl);
661 ast::foreign_item_static(ref t, _) => { check_ty(cx, t); }
665 _ => {/* nothing to do */ }
669 fn check_heap_type(cx: &Context, span: Span, ty: ty::t) {
670 let xs = [managed_heap_memory, owned_heap_memory, heap_memory];
671 for &lint in xs.iter() {
672 if cx.get_level(lint) == allow { continue }
676 ty::fold_ty(cx.tcx, ty, |t| {
677 match ty::get(t).sty {
678 ty::ty_box(_) => n_box += 1,
679 ty::ty_uniq(_) => n_uniq += 1,
685 if n_uniq > 0 && lint != managed_heap_memory {
686 let s = ty_to_str(cx.tcx, ty);
687 let m = format!("type uses owned (~ type) pointers: {}", s);
688 cx.span_lint(lint, span, m);
691 if n_box > 0 && lint != owned_heap_memory {
692 let s = ty_to_str(cx.tcx, ty);
693 let m = format!("type uses managed (@ type) pointers: {}", s);
694 cx.span_lint(lint, span, m);
699 fn check_heap_item(cx: &Context, it: &ast::item) {
704 ast::item_struct(*) => check_heap_type(cx, it.span,
705 ty::node_id_to_type(cx.tcx,
710 // If it's a struct, we also have to check the fields' types
712 ast::item_struct(struct_def, _) => {
713 for struct_field in struct_def.fields.iter() {
714 check_heap_type(cx, struct_field.span,
715 ty::node_id_to_type(cx.tcx,
716 struct_field.node.id));
723 fn check_heap_expr(cx: &Context, e: &ast::Expr) {
724 let ty = ty::expr_ty(cx.tcx, e);
725 check_heap_type(cx, e.span, ty);
728 fn check_path_statement(cx: &Context, s: &ast::Stmt) {
730 ast::StmtSemi(@ast::Expr { node: ast::ExprPath(_), _ }, _) => {
731 cx.span_lint(path_statement, s.span,
732 "path statement with no effect");
738 fn check_item_non_camel_case_types(cx: &Context, it: &ast::item) {
739 fn is_camel_case(cx: ty::ctxt, ident: ast::Ident) -> bool {
740 let ident = cx.sess.str_of(ident);
741 assert!(!ident.is_empty());
742 let ident = ident.trim_chars(&'_');
744 // start with a non-lowercase letter rather than non-uppercase
745 // ones (some scripts don't have a concept of upper/lowercase)
746 !ident.char_at(0).is_lowercase() &&
747 !ident.contains_char('_')
750 fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
751 if !is_camel_case(cx.tcx, ident) {
753 non_camel_case_types, span,
754 format!("{} `{}` should have a camel case identifier",
755 sort, cx.tcx.sess.str_of(ident)));
760 ast::item_ty(*) | ast::item_struct(*) => {
761 check_case(cx, "type", it.ident, it.span)
763 ast::item_trait(*) => {
764 check_case(cx, "trait", it.ident, it.span)
766 ast::item_enum(ref enum_definition, _) => {
767 check_case(cx, "type", it.ident, it.span);
768 for variant in enum_definition.variants.iter() {
769 check_case(cx, "variant", variant.node.name, variant.span);
776 fn check_item_non_uppercase_statics(cx: &Context, it: &ast::item) {
778 // only check static constants
779 ast::item_static(_, ast::MutImmutable, _) => {
780 let s = cx.tcx.sess.str_of(it.ident);
781 // check for lowercase letters rather than non-uppercase
782 // ones (some scripts don't have a concept of
784 if s.iter().any(|c| c.is_lowercase()) {
785 cx.span_lint(non_uppercase_statics, it.span,
786 "static constant should have an uppercase identifier");
793 fn check_pat_non_uppercase_statics(cx: &Context, p: &ast::Pat) {
794 // Lint for constants that look like binding identifiers (#7526)
795 match (&p.node, cx.tcx.def_map.find(&p.id)) {
796 (&ast::PatIdent(_, ref path, _), Some(&ast::DefStatic(_, false))) => {
797 // last identifier alone is right choice for this lint.
798 let ident = path.segments.last().identifier;
799 let s = cx.tcx.sess.str_of(ident);
800 if s.iter().any(|c| c.is_lowercase()) {
801 cx.span_lint(non_uppercase_pattern_statics, path.span,
802 "static constant in pattern should be all caps");
809 fn check_unused_unsafe(cx: &Context, e: &ast::Expr) {
811 // Don't warn about generated blocks, that'll just pollute the
813 ast::ExprBlock(ref blk) => {
814 if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
815 !cx.tcx.used_unsafe.contains(&blk.id) {
816 cx.span_lint(unused_unsafe, blk.span,
817 "unnecessary `unsafe` block");
824 fn check_unused_mut_pat(cx: &Context, p: @ast::Pat) {
826 ast::PatIdent(ast::BindByValue(ast::MutMutable), _, _) => {
827 let mut used = false;
828 let mut bindings = 0;
829 do pat_util::pat_bindings(cx.tcx.def_map, p) |_, id, _, _| {
830 used = used || cx.tcx.used_mut_nodes.contains(&id);
834 let msg = if bindings == 1 {
835 "variable does not need to be mutable"
837 "variables do not need to be mutable"
839 cx.span_lint(unused_mut, p.span, msg);
846 fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
847 // Warn if string and vector literals with sigils are immediately borrowed.
848 // Those can have the sigil removed.
850 ast::ExprVstore(e2, ast::ExprVstoreUniq) |
851 ast::ExprVstore(e2, ast::ExprVstoreBox) => {
853 ast::ExprLit(@codemap::Spanned{node: ast::lit_str(*), _}) |
854 ast::ExprVec(*) => {}
862 match cx.tcx.adjustments.find_copy(&e.id) {
863 Some(@ty::AutoDerefRef(ty::AutoDerefRef {
864 autoref: Some(ty::AutoBorrowVec(*)), _ })) => {
865 cx.span_lint(unnecessary_allocation, e.span,
866 "unnecessary allocation, the sigil can be removed");
873 struct MissingDocLintVisitor(ty::ctxt);
875 impl MissingDocLintVisitor {
876 fn check_attrs(&self, attrs: &[ast::Attribute], id: ast::NodeId,
877 sp: Span, msg: ~str) {
878 if !attrs.iter().any(|a| a.node.is_sugared_doc) {
879 self.sess.add_lint(missing_doc, id, sp, msg);
883 fn check_struct(&self, sdef: &ast::struct_def) {
884 for field in sdef.fields.iter() {
885 match field.node.kind {
886 ast::named_field(_, vis) if vis != ast::private => {
887 self.check_attrs(field.node.attrs, field.node.id, field.span,
888 ~"missing documentation for a field");
890 ast::unnamed_field | ast::named_field(*) => {}
895 fn doc_hidden(&self, attrs: &[ast::Attribute]) -> bool {
896 do attrs.iter().any |attr| {
897 "doc" == attr.name() &&
898 match attr.meta_item_list() {
899 Some(l) => attr::contains_name(l, "hidden"),
900 None => false // not of the form #[doc(...)]
906 impl Visitor<()> for MissingDocLintVisitor {
907 fn visit_ty_method(&mut self, m:&ast::TypeMethod, _: ()) {
908 if self.doc_hidden(m.attrs) { return }
910 // All ty_method objects are linted about because they're part of a
911 // trait (no visibility)
912 self.check_attrs(m.attrs, m.id, m.span,
913 ~"missing documentation for a method");
914 visit::walk_ty_method(self, m, ());
917 fn visit_fn(&mut self, fk: &visit::fn_kind, d: &ast::fn_decl,
918 b: &ast::Block, sp: Span, id: ast::NodeId, _: ()) {
919 // Only warn about explicitly public methods.
921 visit::fk_method(_, _, m) => {
922 if self.doc_hidden(m.attrs) {
925 // If we're in a trait implementation, no need to duplicate
927 if m.vis == ast::public {
928 self.check_attrs(m.attrs, id, sp,
929 ~"missing documentation for a method");
934 visit::walk_fn(self, fk, d, b, sp, id, ());
937 fn visit_item(&mut self, it: @ast::item, _: ()) {
938 // If we're building a test harness, then warning about documentation is
939 // probably not really relevant right now
940 if self.sess.opts.test { return }
941 if self.doc_hidden(it.attrs) { return }
944 ast::item_struct(sdef, _) if it.vis == ast::public => {
945 self.check_attrs(it.attrs, it.id, it.span,
946 ~"missing documentation for a struct");
947 self.check_struct(sdef);
950 // Skip implementations because they inherit documentation from the
951 // trait (which was already linted)
952 ast::item_impl(_, Some(*), _, _) => return,
954 ast::item_trait(*) if it.vis != ast::public => return,
955 ast::item_trait(*) => self.check_attrs(it.attrs, it.id, it.span,
956 ~"missing documentation for a trait"),
958 ast::item_fn(*) if it.vis == ast::public => {
959 self.check_attrs(it.attrs, it.id, it.span,
960 ~"missing documentation for a function");
963 ast::item_mod(*) if it.vis == ast::public => {
964 self.check_attrs(it.attrs, it.id, it.span,
965 ~"missing documentation for a module");
968 ast::item_enum(ref edef, _) if it.vis == ast::public => {
969 self.check_attrs(it.attrs, it.id, it.span,
970 ~"missing documentation for an enum");
971 for variant in edef.variants.iter() {
972 if variant.node.vis == ast::private { continue; }
974 self.check_attrs(variant.node.attrs, variant.node.id,
976 ~"missing documentation for a variant");
977 match variant.node.kind {
978 ast::struct_variant_kind(sdef) => {
979 self.check_struct(sdef);
988 visit::walk_item(self, it, ());
992 /// Checks for use of items with #[deprecated], #[experimental] and
993 /// #[unstable] (or none of them) attributes.
994 fn check_stability(cx: &Context, e: &ast::Expr) {
995 let def = match e.node {
996 ast::ExprMethodCall(*) |
998 ast::ExprStruct(*) => {
999 match cx.tcx.def_map.find(&e.id) {
1007 let id = ast_util::def_id_of_def(def);
1009 let stability = if ast_util::is_local(id) {
1011 match cx.tcx.items.find(&id.node) {
1013 let s = do ast_node.with_attrs |attrs| {
1015 attr::find_stability(a.iter().map(|a| a.meta()))
1021 // no possibility of having attributes
1022 // (e.g. it's a local variable), so just
1027 _ => cx.tcx.sess.bug(format!("handle_def: {:?} not found", id))
1033 // run through all the attributes and take the first
1035 do csearch::get_item_attrs(cx.tcx.cstore, id) |meta_items| {
1037 s = attr::find_stability(meta_items.move_iter())
1043 let (lint, label) = match stability {
1044 // no stability attributes == Unstable
1045 None => (unstable, "unmarked"),
1046 Some(attr::Stability { level: attr::Unstable, _ }) =>
1047 (unstable, "unstable"),
1048 Some(attr::Stability { level: attr::Experimental, _ }) =>
1049 (experimental, "experimental"),
1050 Some(attr::Stability { level: attr::Deprecated, _ }) =>
1051 (deprecated, "deprecated"),
1055 let msg = match stability {
1056 Some(attr::Stability { text: Some(ref s), _ }) => {
1057 format!("use of {} item: {}", label, *s)
1059 _ => format!("use of {} item", label)
1062 cx.span_lint(lint, e.span, msg);
1065 impl Visitor<()> for Context {
1066 fn visit_item(&mut self, it: @ast::item, _: ()) {
1067 do self.with_lint_attrs(it.attrs) |cx| {
1068 check_item_ctypes(cx, it);
1069 check_item_non_camel_case_types(cx, it);
1070 check_item_non_uppercase_statics(cx, it);
1071 check_heap_item(cx, it);
1073 do cx.visit_ids |v| {
1074 v.visit_item(it, ());
1077 visit::walk_item(cx, it, ());
1081 fn visit_pat(&mut self, p: @ast::Pat, _: ()) {
1082 check_pat_non_uppercase_statics(self, p);
1083 check_unused_mut_pat(self, p);
1085 visit::walk_pat(self, p, ());
1088 fn visit_expr(&mut self, e: @ast::Expr, _: ()) {
1089 check_while_true_expr(self, e);
1090 check_stability(self, e);
1091 check_unused_unsafe(self, e);
1092 check_unnecessary_allocation(self, e);
1093 check_heap_expr(self, e);
1094 check_type_limits(self, e);
1096 visit::walk_expr(self, e, ());
1099 fn visit_stmt(&mut self, s: @ast::Stmt, _: ()) {
1100 check_path_statement(self, s);
1102 visit::walk_stmt(self, s, ());
1105 fn visit_fn(&mut self, fk: &visit::fn_kind, decl: &ast::fn_decl,
1106 body: &ast::Block, span: Span, id: ast::NodeId, _: ()) {
1107 let recurse = |this: &mut Context| {
1108 visit::walk_fn(this, fk, decl, body, span, id, ());
1112 visit::fk_method(_, _, m) => {
1113 do self.with_lint_attrs(m.attrs) |cx| {
1114 do cx.visit_ids |v| {
1115 v.visit_fn(fk, decl, body, span, id, ());
1125 impl ast_util::IdVisitingOperation for Context {
1126 fn visit_id(&self, id: ast::NodeId) {
1127 match self.tcx.sess.lints.pop(&id) {
1130 for (lint, span, msg) in l.move_iter() {
1131 self.span_lint(lint, span, msg)
1138 pub fn check_crate(tcx: ty::ctxt, crate: &ast::Crate) {
1139 // This visitor contains more state than is currently maintained in Context,
1140 // and there's no reason for the Context to keep track of this information
1142 let mut dox = MissingDocLintVisitor(tcx);
1143 visit::walk_crate(&mut dox, crate, ());
1145 let mut cx = Context {
1146 dict: @get_lint_dict(),
1147 cur: SmallIntMap::new(),
1152 // Install default lint levels, followed by the command line levels, and
1153 // then actually visit the whole crate.
1154 for (_, spec) in cx.dict.iter() {
1155 cx.set_level(spec.lint, spec.default, Default);
1157 for &(lint, level) in tcx.sess.opts.lint_opts.iter() {
1158 cx.set_level(lint, level, CommandLine);
1160 do cx.with_lint_attrs(crate.attrs) |cx| {
1161 do cx.visit_ids |v| {
1162 v.visited_outermost = true;
1163 visit::walk_crate(v, crate, ());
1165 visit::walk_crate(cx, crate, ());
1168 // If we missed any lints added to the session, then there's a bug somewhere
1169 // in the iteration code.
1170 for (id, v) in tcx.sess.lints.iter() {
1171 for &(lint, span, ref msg) in v.iter() {
1172 tcx.sess.span_bug(span, format!("unprocessed lint {:?} at {}: {}",
1174 ast_map::node_id_to_str(tcx.items,
1176 token::get_ident_interner()),
1181 tcx.sess.abort_if_errors();