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.
39 use metadata::csearch;
40 use util::ppaux::{ty_to_str};
43 use std::hashmap::HashMap;
52 use extra::smallintmap::SmallIntMap;
55 use syntax::attr::{AttrMetaMethods, AttributeMethods};
56 use syntax::codemap::Span;
58 use syntax::parse::token;
59 use syntax::{ast, ast_util, visit};
60 use syntax::visit::Visitor;
62 #[deriving(Clone, Eq)]
67 unnecessary_qualification,
72 non_uppercase_statics,
73 non_uppercase_pattern_statics,
84 unnecessary_allocation,
96 pub fn level_to_str(lv: level) -> &'static str {
105 #[deriving(Clone, Eq, Ord)]
107 allow, warn, deny, forbid
110 #[deriving(Clone, Eq)]
111 pub struct LintSpec {
117 impl Ord for LintSpec {
118 fn lt(&self, other: &LintSpec) -> bool { self.default < other.default }
121 pub type LintDict = HashMap<&'static str, LintSpec>;
130 static lint_table: &'static [(&'static str, LintSpec)] = &[
134 desc: "proper use of std::libc types in foreign modules",
141 desc: "only invoke foreign functions from fixedstacksegment fns",
147 lint: unused_imports,
148 desc: "imports that are never used",
152 ("unnecessary_qualification",
154 lint: unnecessary_qualification,
155 desc: "detects unnecessarily qualified names",
162 desc: "suggest using loop { } instead of while(true) { }",
168 lint: path_statement,
169 desc: "path statements with no effect",
173 ("unrecognized_lint",
175 lint: unrecognized_lint,
176 desc: "unrecognized lint attribute",
180 ("non_camel_case_types",
182 lint: non_camel_case_types,
183 desc: "types, variants and traits should have camel case names",
187 ("non_uppercase_statics",
189 lint: non_uppercase_statics,
190 desc: "static constants should have uppercase identifiers",
194 ("non_uppercase_pattern_statics",
196 lint: non_uppercase_pattern_statics,
197 desc: "static constants in match patterns should be all caps",
201 ("managed_heap_memory",
203 lint: managed_heap_memory,
204 desc: "use of managed (@ type) heap memory",
208 ("owned_heap_memory",
210 lint: owned_heap_memory,
211 desc: "use of owned (~ type) heap memory",
218 desc: "use of any (~ type or @ type) heap memory",
225 desc: "comparisons made useless by limits of the types involved",
232 desc: "unnecessary use of an `unsafe` block",
238 lint: unused_variable,
239 desc: "detect variables which are not used in any way",
245 lint: dead_assignment,
246 desc: "detect assignments that will never be read",
253 desc: "detect mut variables which don't need to be mutable",
257 ("unnecessary_allocation",
259 lint: unnecessary_allocation,
260 desc: "detects unnecessary allocations that can be eliminated",
267 desc: "detects missing documentation for public members",
273 lint: unreachable_code,
274 desc: "detects unreachable code",
281 desc: "detects use of #[deprecated] items",
288 desc: "detects use of #[experimental] items",
295 desc: "detects use of #[unstable] items (incl. items with no stability attribute)",
302 desc: "mass-change the level for lints which produce warnings",
308 Pass names should not contain a '-', as the compiler normalizes
309 '-' to '_' in command-line flags
311 pub fn get_lint_dict() -> LintDict {
312 let mut map = HashMap::new();
313 for &(k, v) in lint_table.iter() {
320 // All known lint modes (string versions)
322 // Current levels of each lint warning
323 cur: SmallIntMap<(level, LintSource)>,
324 // context we're checking in (used to access fields like sess)
327 // When recursing into an attributed node of the ast which modifies lint
328 // levels, this stack keeps track of the previous lint levels of whatever
330 lint_stack: ~[(lint, level, LintSource)],
334 fn get_level(&self, lint: lint) -> level {
335 match self.cur.find(&(lint as uint)) {
336 Some(&(lvl, _)) => lvl,
341 fn get_source(&self, lint: lint) -> LintSource {
342 match self.cur.find(&(lint as uint)) {
343 Some(&(_, src)) => src,
348 fn set_level(&mut self, lint: lint, level: level, src: LintSource) {
350 self.cur.remove(&(lint as uint));
352 self.cur.insert(lint as uint, (level, src));
356 fn lint_to_str(&self, lint: lint) -> &'static str {
357 for (k, v) in self.dict.iter() {
362 fail!("unregistered lint {:?}", lint);
365 fn span_lint(&self, lint: lint, span: Span, msg: &str) {
366 let (level, src) = match self.cur.find(&(lint as uint)) {
368 Some(&(warn, src)) => (self.get_level(warnings), src),
371 if level == allow { return }
374 let msg = match src {
376 format!("{}, \\#[{}({})] on by default", msg,
377 level_to_str(level), self.lint_to_str(lint))
380 format!("{} [-{} {}]", msg,
382 warn => 'W', deny => 'D', forbid => 'F',
384 }, self.lint_to_str(lint).replace("_", "-"))
392 warn => { self.tcx.sess.span_warn(span, msg); }
393 deny | forbid => { self.tcx.sess.span_err(span, msg); }
397 for &span in note.iter() {
398 self.tcx.sess.span_note(span, "lint level defined here");
403 * Merge the lints specified by any lint attributes into the
404 * current lint context, call the provided function, then reset the
405 * lints in effect to their previous state.
407 fn with_lint_attrs(&mut self, attrs: &[ast::Attribute],
408 f: &fn(&mut Context)) {
409 // Parse all of the lint attributes, and then add them all to the
410 // current dictionary of lint information. Along the way, keep a history
411 // of what we changed so we can roll everything back after invoking the
414 do each_lint(self.tcx.sess, attrs) |meta, level, lintname| {
415 match self.dict.find_equiv(&lintname) {
420 format!("unknown `{}` attribute: `{}`",
421 level_to_str(level), lintname));
424 let lint = lint.lint;
425 let now = self.get_level(lint);
426 if now == forbid && level != forbid {
427 self.tcx.sess.span_err(meta.span,
428 format!("{}({}) overruled by outer forbid({})",
430 lintname, lintname));
431 } else if now != level {
432 let src = self.get_source(lint);
433 self.lint_stack.push((lint, now, src));
435 self.set_level(lint, level, Node(meta.span));
446 let (lint, lvl, src) = self.lint_stack.pop();
447 self.set_level(lint, lvl, src);
451 fn visit_ids(&self, f: &fn(&mut ast_util::IdVisitor<Context>)) {
452 let mut v = ast_util::IdVisitor {
454 pass_through_items: false,
455 visited_outermost: false,
461 pub fn each_lint(sess: session::Session,
462 attrs: &[ast::Attribute],
463 f: &fn(@ast::MetaItem, level, @str) -> bool) -> bool {
464 let xs = [allow, warn, deny, forbid];
465 for &level in xs.iter() {
466 let level_name = level_to_str(level);
467 for attr in attrs.iter().filter(|m| level_name == m.name()) {
468 let meta = attr.node.value;
469 let metas = match meta.node {
470 ast::MetaList(_, ref metas) => metas,
472 sess.span_err(meta.span, "malformed lint attribute");
476 for meta in metas.iter() {
478 ast::MetaWord(lintname) => {
479 if !f(*meta, level, lintname) {
484 sess.span_err(meta.span, "malformed lint attribute");
493 fn check_while_true_expr(cx: &Context, e: &ast::Expr) {
495 ast::ExprWhile(cond, _) => {
497 ast::ExprLit(@codemap::Spanned {
498 node: ast::lit_bool(true), _}) =>
500 cx.span_lint(while_true, e.span,
501 "denote infinite loops with loop { ... }");
510 fn check_type_limits(cx: &Context, e: &ast::Expr) {
511 return match e.node {
512 ast::ExprBinary(_, binop, l, r) => {
513 if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) {
514 cx.span_lint(type_limits, e.span,
515 "comparison is useless due to type limits");
521 fn is_valid<T:cmp::Ord>(binop: ast::BinOp, v: T,
522 min: T, max: T) -> bool {
524 ast::BiLt => v <= max,
525 ast::BiLe => v < max,
526 ast::BiGt => v >= min,
527 ast::BiGe => v > min,
528 ast::BiEq | ast::BiNe => v >= min && v <= max,
533 fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
535 ast::BiLt => ast::BiGt,
536 ast::BiLe => ast::BiGe,
537 ast::BiGt => ast::BiLt,
538 ast::BiGe => ast::BiLe,
543 // for int & uint, be conservative with the warnings, so that the
544 // warnings are consistent between 32- and 64-bit platforms
545 fn int_ty_range(int_ty: ast::int_ty) -> (i64, i64) {
547 ast::ty_i => (i64::min_value, i64::max_value),
548 ast::ty_i8 => (i8::min_value as i64, i8::max_value as i64),
549 ast::ty_i16 => (i16::min_value as i64, i16::max_value as i64),
550 ast::ty_i32 => (i32::min_value as i64, i32::max_value as i64),
551 ast::ty_i64 => (i64::min_value, i64::max_value)
555 fn uint_ty_range(uint_ty: ast::uint_ty) -> (u64, u64) {
557 ast::ty_u => (u64::min_value, u64::max_value),
558 ast::ty_u8 => (u8::min_value as u64, u8::max_value as u64),
559 ast::ty_u16 => (u16::min_value as u64, u16::max_value as u64),
560 ast::ty_u32 => (u32::min_value as u64, u32::max_value as u64),
561 ast::ty_u64 => (u64::min_value, u64::max_value)
565 fn check_limits(tcx: ty::ctxt, binop: ast::BinOp,
566 l: &ast::Expr, r: &ast::Expr) -> bool {
567 let (lit, expr, swap) = match (&l.node, &r.node) {
568 (&ast::ExprLit(_), _) => (l, r, true),
569 (_, &ast::ExprLit(_)) => (r, l, false),
572 // Normalize the binop so that the literal is always on the RHS in
574 let norm_binop = if swap { rev_binop(binop) } else { binop };
575 match ty::get(ty::expr_ty(tcx, expr)).sty {
576 ty::ty_int(int_ty) => {
577 let (min, max) = int_ty_range(int_ty);
578 let lit_val: i64 = match lit.node {
579 ast::ExprLit(li) => match li.node {
580 ast::lit_int(v, _) => v,
581 ast::lit_uint(v, _) => v as i64,
582 ast::lit_int_unsuffixed(v) => v,
587 is_valid(norm_binop, lit_val, min, max)
589 ty::ty_uint(uint_ty) => {
590 let (min, max): (u64, u64) = uint_ty_range(uint_ty);
591 let lit_val: u64 = match lit.node {
592 ast::ExprLit(li) => match li.node {
593 ast::lit_int(v, _) => v as u64,
594 ast::lit_uint(v, _) => v,
595 ast::lit_int_unsuffixed(v) => v as u64,
600 is_valid(norm_binop, lit_val, min, max)
606 fn is_comparison(binop: ast::BinOp) -> bool {
608 ast::BiEq | ast::BiLt | ast::BiLe |
609 ast::BiNe | ast::BiGe | ast::BiGt => true,
615 fn check_item_ctypes(cx: &Context, it: &ast::item) {
616 fn check_ty(cx: &Context, ty: &ast::Ty) {
618 ast::ty_path(_, _, id) => {
619 match cx.tcx.def_map.get_copy(&id) {
620 ast::DefPrimTy(ast::ty_int(ast::ty_i)) => {
621 cx.span_lint(ctypes, ty.span,
622 "found rust type `int` in foreign module, while \
623 libc::c_int or libc::c_long should be used");
625 ast::DefPrimTy(ast::ty_uint(ast::ty_u)) => {
626 cx.span_lint(ctypes, ty.span,
627 "found rust type `uint` in foreign module, while \
628 libc::c_uint or libc::c_ulong should be used");
633 ast::ty_ptr(ref mt) => { check_ty(cx, mt.ty) }
638 fn check_foreign_fn(cx: &Context, decl: &ast::fn_decl) {
639 for input in decl.inputs.iter() {
640 check_ty(cx, &input.ty);
642 check_ty(cx, &decl.output)
646 ast::item_foreign_mod(ref nmod) if !nmod.abis.is_intrinsic() => {
647 for ni in nmod.items.iter() {
649 ast::foreign_item_fn(ref decl, _) => {
650 check_foreign_fn(cx, decl);
652 ast::foreign_item_static(ref t, _) => { check_ty(cx, t); }
656 _ => {/* nothing to do */ }
660 fn check_heap_type(cx: &Context, span: Span, ty: ty::t) {
661 let xs = [managed_heap_memory, owned_heap_memory, heap_memory];
662 for &lint in xs.iter() {
663 if cx.get_level(lint) == allow { continue }
667 ty::fold_ty(cx.tcx, ty, |t| {
668 match ty::get(t).sty {
669 ty::ty_box(_) => n_box += 1,
670 ty::ty_uniq(_) => n_uniq += 1,
676 if n_uniq > 0 && lint != managed_heap_memory {
677 let s = ty_to_str(cx.tcx, ty);
678 let m = format!("type uses owned (~ type) pointers: {}", s);
679 cx.span_lint(lint, span, m);
682 if n_box > 0 && lint != owned_heap_memory {
683 let s = ty_to_str(cx.tcx, ty);
684 let m = format!("type uses managed (@ type) pointers: {}", s);
685 cx.span_lint(lint, span, m);
690 fn check_heap_item(cx: &Context, it: &ast::item) {
695 ast::item_struct(*) => check_heap_type(cx, it.span,
696 ty::node_id_to_type(cx.tcx,
701 // If it's a struct, we also have to check the fields' types
703 ast::item_struct(struct_def, _) => {
704 for struct_field in struct_def.fields.iter() {
705 check_heap_type(cx, struct_field.span,
706 ty::node_id_to_type(cx.tcx,
707 struct_field.node.id));
714 fn check_heap_expr(cx: &Context, e: &ast::Expr) {
715 let ty = ty::expr_ty(cx.tcx, e);
716 check_heap_type(cx, e.span, ty);
719 fn check_path_statement(cx: &Context, s: &ast::Stmt) {
721 ast::StmtSemi(@ast::Expr { node: ast::ExprPath(_), _ }, _) => {
722 cx.span_lint(path_statement, s.span,
723 "path statement with no effect");
729 fn check_item_non_camel_case_types(cx: &Context, it: &ast::item) {
730 fn is_camel_case(cx: ty::ctxt, ident: ast::Ident) -> bool {
731 let ident = cx.sess.str_of(ident);
732 assert!(!ident.is_empty());
733 let ident = ident.trim_chars(&'_');
735 // start with a non-lowercase letter rather than non-uppercase
736 // ones (some scripts don't have a concept of upper/lowercase)
737 !ident.char_at(0).is_lowercase() &&
738 !ident.contains_char('_')
741 fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
742 if !is_camel_case(cx.tcx, ident) {
744 non_camel_case_types, span,
745 format!("{} `{}` should have a camel case identifier",
746 sort, cx.tcx.sess.str_of(ident)));
751 ast::item_ty(*) | ast::item_struct(*) => {
752 check_case(cx, "type", it.ident, it.span)
754 ast::item_trait(*) => {
755 check_case(cx, "trait", it.ident, it.span)
757 ast::item_enum(ref enum_definition, _) => {
758 check_case(cx, "type", it.ident, it.span);
759 for variant in enum_definition.variants.iter() {
760 check_case(cx, "variant", variant.node.name, variant.span);
767 fn check_item_non_uppercase_statics(cx: &Context, it: &ast::item) {
769 // only check static constants
770 ast::item_static(_, ast::MutImmutable, _) => {
771 let s = cx.tcx.sess.str_of(it.ident);
772 // check for lowercase letters rather than non-uppercase
773 // ones (some scripts don't have a concept of
775 if s.iter().any(|c| c.is_lowercase()) {
776 cx.span_lint(non_uppercase_statics, it.span,
777 "static constant should have an uppercase identifier");
784 fn check_pat_non_uppercase_statics(cx: &Context, p: &ast::Pat) {
785 // Lint for constants that look like binding identifiers (#7526)
786 match (&p.node, cx.tcx.def_map.find(&p.id)) {
787 (&ast::PatIdent(_, ref path, _), Some(&ast::DefStatic(_, false))) => {
788 // last identifier alone is right choice for this lint.
789 let ident = path.segments.last().identifier;
790 let s = cx.tcx.sess.str_of(ident);
791 if s.iter().any(|c| c.is_lowercase()) {
792 cx.span_lint(non_uppercase_pattern_statics, path.span,
793 "static constant in pattern should be all caps");
800 fn check_unused_unsafe(cx: &Context, e: &ast::Expr) {
802 // Don't warn about generated blocks, that'll just pollute the
804 ast::ExprBlock(ref blk) => {
805 if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
806 !cx.tcx.used_unsafe.contains(&blk.id) {
807 cx.span_lint(unused_unsafe, blk.span,
808 "unnecessary `unsafe` block");
815 fn check_unused_mut_pat(cx: &Context, p: @ast::Pat) {
816 fn check_unused_mut_pat_inner(cx: &Context, p: @ast::Pat) {
817 let mut used = false;
818 let mut bindings = 0;
819 do pat_util::pat_bindings(cx.tcx.def_map, p) |_, id, _, _| {
820 used = used || cx.tcx.used_mut_nodes.contains(&id);
824 let msg = if bindings == 1 {
825 "variable does not need to be mutable"
827 "variables do not need to be mutable"
829 cx.span_lint(unused_mut, p.span, msg);
832 do ast_util::walk_pat(p) |pat| {
834 ast::PatIdent(ast::BindByValue(ast::MutMutable), _, _) => {
835 check_unused_mut_pat_inner(cx, pat);
844 fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
845 // Warn if string and vector literals with sigils are immediately borrowed.
846 // Those can have the sigil removed.
848 ast::ExprVstore(e2, ast::ExprVstoreUniq) |
849 ast::ExprVstore(e2, ast::ExprVstoreBox) => {
851 ast::ExprLit(@codemap::Spanned{node: ast::lit_str(*), _}) |
852 ast::ExprVec(*) => {}
860 match cx.tcx.adjustments.find_copy(&e.id) {
861 Some(@ty::AutoDerefRef(ty::AutoDerefRef {
862 autoref: Some(ty::AutoBorrowVec(*)), _ })) => {
863 cx.span_lint(unnecessary_allocation, e.span,
864 "unnecessary allocation, the sigil can be removed");
871 struct MissingDocLintVisitor(ty::ctxt);
873 impl MissingDocLintVisitor {
874 fn check_attrs(&self, attrs: &[ast::Attribute], id: ast::NodeId,
875 sp: Span, msg: ~str) {
876 if !attrs.iter().any(|a| a.node.is_sugared_doc) {
877 self.sess.add_lint(missing_doc, id, sp, msg);
881 fn check_struct(&self, sdef: &ast::struct_def) {
882 for field in sdef.fields.iter() {
883 match field.node.kind {
884 ast::named_field(_, vis) if vis != ast::private => {
885 self.check_attrs(field.node.attrs, field.node.id, field.span,
886 ~"missing documentation for a field");
888 ast::unnamed_field | ast::named_field(*) => {}
893 fn doc_hidden(&self, attrs: &[ast::Attribute]) -> bool {
894 do attrs.iter().any |attr| {
895 "doc" == attr.name() &&
896 match attr.meta_item_list() {
897 Some(l) => attr::contains_name(l, "hidden"),
898 None => false // not of the form #[doc(...)]
904 impl Visitor<()> for MissingDocLintVisitor {
905 fn visit_ty_method(&mut self, m:&ast::TypeMethod, _: ()) {
906 if self.doc_hidden(m.attrs) { return }
908 // All ty_method objects are linted about because they're part of a
909 // trait (no visibility)
910 self.check_attrs(m.attrs, m.id, m.span,
911 ~"missing documentation for a method");
912 visit::walk_ty_method(self, m, ());
915 fn visit_fn(&mut self, fk: &visit::fn_kind, d: &ast::fn_decl,
916 b: &ast::Block, sp: Span, id: ast::NodeId, _: ()) {
917 // Only warn about explicitly public methods.
919 visit::fk_method(_, _, m) => {
920 if self.doc_hidden(m.attrs) {
923 // If we're in a trait implementation, no need to duplicate
925 if m.vis == ast::public {
926 self.check_attrs(m.attrs, id, sp,
927 ~"missing documentation for a method");
932 visit::walk_fn(self, fk, d, b, sp, id, ());
935 fn visit_item(&mut self, it: @ast::item, _: ()) {
936 // If we're building a test harness, then warning about documentation is
937 // probably not really relevant right now
938 if self.sess.opts.test { return }
939 if self.doc_hidden(it.attrs) { return }
942 ast::item_struct(sdef, _) if it.vis == ast::public => {
943 self.check_attrs(it.attrs, it.id, it.span,
944 ~"missing documentation for a struct");
945 self.check_struct(sdef);
948 // Skip implementations because they inherit documentation from the
949 // trait (which was already linted)
950 ast::item_impl(_, Some(*), _, _) => return,
952 ast::item_trait(*) if it.vis == ast::public => {
953 self.check_attrs(it.attrs, it.id, it.span,
954 ~"missing documentation for a trait");
957 ast::item_fn(*) if it.vis == ast::public => {
958 self.check_attrs(it.attrs, it.id, it.span,
959 ~"missing documentation for a function");
962 ast::item_mod(*) if it.vis == ast::public => {
963 self.check_attrs(it.attrs, it.id, it.span,
964 ~"missing documentation for a module");
967 ast::item_enum(ref edef, _) if it.vis == ast::public => {
968 self.check_attrs(it.attrs, it.id, it.span,
969 ~"missing documentation for an enum");
970 for variant in edef.variants.iter() {
971 if variant.node.vis == ast::private { continue; }
973 self.check_attrs(variant.node.attrs, variant.node.id,
975 ~"missing documentation for a variant");
976 match variant.node.kind {
977 ast::struct_variant_kind(sdef) => {
978 self.check_struct(sdef);
987 visit::walk_item(self, it, ());
991 /// Checks for use of items with #[deprecated], #[experimental] and
992 /// #[unstable] (or none of them) attributes.
993 fn check_stability(cx: &Context, e: &ast::Expr) {
994 let def = match e.node {
995 ast::ExprMethodCall(*) |
997 ast::ExprStruct(*) => {
998 match cx.tcx.def_map.find(&e.id) {
1006 let id = ast_util::def_id_of_def(def);
1008 let stability = if ast_util::is_local(id) {
1010 match cx.tcx.items.find(&id.node) {
1012 let s = do ast_node.with_attrs |attrs| {
1014 attr::find_stability(a.iter().map(|a| a.meta()))
1020 // no possibility of having attributes
1021 // (e.g. it's a local variable), so just
1026 _ => cx.tcx.sess.bug(format!("handle_def: {:?} not found", id))
1032 // run through all the attributes and take the first
1034 do csearch::get_item_attrs(cx.tcx.cstore, id) |meta_items| {
1036 s = attr::find_stability(meta_items.move_iter())
1042 let (lint, label) = match stability {
1043 // no stability attributes == Unstable
1044 None => (unstable, "unmarked"),
1045 Some(attr::Stability { level: attr::Unstable, _ }) =>
1046 (unstable, "unstable"),
1047 Some(attr::Stability { level: attr::Experimental, _ }) =>
1048 (experimental, "experimental"),
1049 Some(attr::Stability { level: attr::Deprecated, _ }) =>
1050 (deprecated, "deprecated"),
1054 let msg = match stability {
1055 Some(attr::Stability { text: Some(ref s), _ }) => {
1056 format!("use of {} item: {}", label, *s)
1058 _ => format!("use of {} item", label)
1061 cx.span_lint(lint, e.span, msg);
1064 impl Visitor<()> for Context {
1065 fn visit_item(&mut self, it: @ast::item, _: ()) {
1066 do self.with_lint_attrs(it.attrs) |cx| {
1067 check_item_ctypes(cx, it);
1068 check_item_non_camel_case_types(cx, it);
1069 check_item_non_uppercase_statics(cx, it);
1070 check_heap_item(cx, it);
1072 do cx.visit_ids |v| {
1073 v.visit_item(it, ());
1076 visit::walk_item(cx, it, ());
1080 fn visit_pat(&mut self, p: @ast::Pat, _: ()) {
1081 check_pat_non_uppercase_statics(self, p);
1082 check_unused_mut_pat(self, p);
1084 visit::walk_pat(self, p, ());
1087 fn visit_expr(&mut self, e: @ast::Expr, _: ()) {
1088 check_while_true_expr(self, e);
1089 check_stability(self, e);
1090 check_unused_unsafe(self, e);
1091 check_unnecessary_allocation(self, e);
1092 check_heap_expr(self, e);
1093 check_type_limits(self, e);
1095 visit::walk_expr(self, e, ());
1098 fn visit_stmt(&mut self, s: @ast::Stmt, _: ()) {
1099 check_path_statement(self, s);
1101 visit::walk_stmt(self, s, ());
1104 fn visit_fn(&mut self, fk: &visit::fn_kind, decl: &ast::fn_decl,
1105 body: &ast::Block, span: Span, id: ast::NodeId, _: ()) {
1106 let recurse = |this: &mut Context| {
1107 visit::walk_fn(this, fk, decl, body, span, id, ());
1111 visit::fk_method(_, _, m) => {
1112 do self.with_lint_attrs(m.attrs) |cx| {
1113 do cx.visit_ids |v| {
1114 v.visit_fn(fk, decl, body, span, id, ());
1124 impl ast_util::IdVisitingOperation for Context {
1125 fn visit_id(&self, id: ast::NodeId) {
1126 match self.tcx.sess.lints.pop(&id) {
1129 for (lint, span, msg) in l.move_iter() {
1130 self.span_lint(lint, span, msg)
1137 pub fn check_crate(tcx: ty::ctxt, crate: &ast::Crate) {
1138 // This visitor contains more state than is currently maintained in Context,
1139 // and there's no reason for the Context to keep track of this information
1141 let mut dox = MissingDocLintVisitor(tcx);
1142 visit::walk_crate(&mut dox, crate, ());
1144 let mut cx = Context {
1145 dict: @get_lint_dict(),
1146 cur: SmallIntMap::new(),
1151 // Install default lint levels, followed by the command line levels, and
1152 // then actually visit the whole crate.
1153 for (_, spec) in cx.dict.iter() {
1154 cx.set_level(spec.lint, spec.default, Default);
1156 for &(lint, level) in tcx.sess.opts.lint_opts.iter() {
1157 cx.set_level(lint, level, CommandLine);
1159 do cx.with_lint_attrs(crate.attrs) |cx| {
1160 do cx.visit_ids |v| {
1161 v.visited_outermost = true;
1162 visit::walk_crate(v, crate, ());
1164 visit::walk_crate(cx, crate, ());
1167 // If we missed any lints added to the session, then there's a bug somewhere
1168 // in the iteration code.
1169 for (id, v) in tcx.sess.lints.iter() {
1170 for &(lint, span, ref msg) in v.iter() {
1171 tcx.sess.span_bug(span, format!("unprocessed lint {:?} at {}: {}",
1173 ast_map::node_id_to_str(tcx.items,
1175 token::get_ident_interner()),
1180 tcx.sess.abort_if_errors();