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.
38 use middle::trans::adt; // for `adt::is_ffi_safe`
41 use metadata::csearch;
42 use util::ppaux::{ty_to_str};
45 use std::hashmap::HashMap;
54 use extra::smallintmap::SmallIntMap;
57 use syntax::attr::{AttrMetaMethods, AttributeMethods};
58 use syntax::codemap::Span;
60 use syntax::parse::token;
61 use syntax::{ast, ast_util, visit};
62 use syntax::visit::Visitor;
64 #[deriving(Clone, Eq)]
68 unnecessary_qualification,
73 non_uppercase_statics,
74 non_uppercase_pattern_statics,
88 unnecessary_allocation,
100 pub fn level_to_str(lv: level) -> &'static str {
109 #[deriving(Clone, Eq, Ord)]
111 allow, warn, deny, forbid
114 #[deriving(Clone, Eq)]
115 pub struct LintSpec {
121 impl Ord for LintSpec {
122 fn lt(&self, other: &LintSpec) -> bool { self.default < other.default }
125 pub type LintDict = HashMap<&'static str, LintSpec>;
134 static lint_table: &'static [(&'static str, LintSpec)] = &[
138 desc: "proper use of std::libc types in foreign modules",
144 lint: unused_imports,
145 desc: "imports that are never used",
149 ("unnecessary_qualification",
151 lint: unnecessary_qualification,
152 desc: "detects unnecessarily qualified names",
159 desc: "suggest using loop { } instead of while(true) { }",
165 lint: path_statement,
166 desc: "path statements with no effect",
170 ("unrecognized_lint",
172 lint: unrecognized_lint,
173 desc: "unrecognized lint attribute",
177 ("non_camel_case_types",
179 lint: non_camel_case_types,
180 desc: "types, variants and traits should have camel case names",
184 ("non_uppercase_statics",
186 lint: non_uppercase_statics,
187 desc: "static constants should have uppercase identifiers",
191 ("non_uppercase_pattern_statics",
193 lint: non_uppercase_pattern_statics,
194 desc: "static constants in match patterns should be all caps",
198 ("managed_heap_memory",
200 lint: managed_heap_memory,
201 desc: "use of managed (@ type) heap memory",
205 ("owned_heap_memory",
207 lint: owned_heap_memory,
208 desc: "use of owned (~ type) heap memory",
215 desc: "use of any (~ type or @ type) heap memory",
222 desc: "comparisons made useless by limits of the types involved",
229 desc: "literal out of range for its type",
237 desc: "unnecessary use of an `unsafe` block",
244 desc: "usage of an `unsafe` block",
250 lint: attribute_usage,
251 desc: "detects bad use of attributes",
257 lint: unused_variable,
258 desc: "detect variables which are not used in any way",
264 lint: dead_assignment,
265 desc: "detect assignments that will never be read",
272 desc: "detect mut variables which don't need to be mutable",
276 ("unnecessary_allocation",
278 lint: unnecessary_allocation,
279 desc: "detects unnecessary allocations that can be eliminated",
286 desc: "detects missing documentation for public members",
292 lint: unreachable_code,
293 desc: "detects unreachable code",
300 desc: "detects use of #[deprecated] items",
307 desc: "detects use of #[experimental] items",
314 desc: "detects use of #[unstable] items (incl. items with no stability attribute)",
321 desc: "mass-change the level for lints which produce warnings",
327 Pass names should not contain a '-', as the compiler normalizes
328 '-' to '_' in command-line flags
330 pub fn get_lint_dict() -> LintDict {
331 let mut map = HashMap::new();
332 for &(k, v) in lint_table.iter() {
338 struct Context<'self> {
339 // All known lint modes (string versions)
341 // Current levels of each lint warning
342 cur: SmallIntMap<(level, LintSource)>,
343 // context we're checking in (used to access fields like sess)
345 // Items exported by the crate; used by the missing_doc lint.
346 exported_items: &'self privacy::ExportedItems,
347 // The id of the current `ast::struct_def` being walked.
348 cur_struct_def_id: ast::NodeId,
349 // Whether some ancestor of the current node was marked
353 // When recursing into an attributed node of the ast which modifies lint
354 // levels, this stack keeps track of the previous lint levels of whatever
356 lint_stack: ~[(lint, level, LintSource)],
358 // id of the last visited negated expression
359 negated_expr_id: ast::NodeId
362 impl<'self> Context<'self> {
363 fn get_level(&self, lint: lint) -> level {
364 match self.cur.find(&(lint as uint)) {
365 Some(&(lvl, _)) => lvl,
370 fn get_source(&self, lint: lint) -> LintSource {
371 match self.cur.find(&(lint as uint)) {
372 Some(&(_, src)) => src,
377 fn set_level(&mut self, lint: lint, level: level, src: LintSource) {
379 self.cur.remove(&(lint as uint));
381 self.cur.insert(lint as uint, (level, src));
385 fn lint_to_str(&self, lint: lint) -> &'static str {
386 for (k, v) in self.dict.iter() {
391 fail!("unregistered lint {:?}", lint);
394 fn span_lint(&self, lint: lint, span: Span, msg: &str) {
395 let (level, src) = match self.cur.find(&(lint as uint)) {
397 Some(&(warn, src)) => (self.get_level(warnings), src),
400 if level == allow { return }
403 let msg = match src {
405 format!("{}, \\#[{}({})] on by default", msg,
406 level_to_str(level), self.lint_to_str(lint))
409 format!("{} [-{} {}]", msg,
411 warn => 'W', deny => 'D', forbid => 'F',
413 }, self.lint_to_str(lint).replace("_", "-"))
421 warn => { self.tcx.sess.span_warn(span, msg); }
422 deny | forbid => { self.tcx.sess.span_err(span, msg); }
426 for &span in note.iter() {
427 self.tcx.sess.span_note(span, "lint level defined here");
432 * Merge the lints specified by any lint attributes into the
433 * current lint context, call the provided function, then reset the
434 * lints in effect to their previous state.
436 fn with_lint_attrs(&mut self,
437 attrs: &[ast::Attribute],
439 // Parse all of the lint attributes, and then add them all to the
440 // current dictionary of lint information. Along the way, keep a history
441 // of what we changed so we can roll everything back after invoking the
444 do each_lint(self.tcx.sess, attrs) |meta, level, lintname| {
445 match self.dict.find_equiv(&lintname) {
450 format!("unknown `{}` attribute: `{}`",
451 level_to_str(level), lintname));
454 let lint = lint.lint;
455 let now = self.get_level(lint);
456 if now == forbid && level != forbid {
457 self.tcx.sess.span_err(meta.span,
458 format!("{}({}) overruled by outer forbid({})",
460 lintname, lintname));
461 } else if now != level {
462 let src = self.get_source(lint);
463 self.lint_stack.push((lint, now, src));
465 self.set_level(lint, level, Node(meta.span));
472 let old_is_doc_hidden = self.is_doc_hidden;
473 self.is_doc_hidden = self.is_doc_hidden ||
474 attrs.iter().any(|attr| ("doc" == attr.name() && match attr.meta_item_list()
476 Some(l) => attr::contains_name(l, "hidden") }));
481 self.is_doc_hidden = old_is_doc_hidden;
483 let (lint, lvl, src) = self.lint_stack.pop();
484 self.set_level(lint, lvl, src);
488 fn visit_ids(&self, f: |&mut ast_util::IdVisitor<Context>|) {
489 let mut v = ast_util::IdVisitor {
491 pass_through_items: false,
492 visited_outermost: false,
498 pub fn each_lint(sess: session::Session,
499 attrs: &[ast::Attribute],
500 f: |@ast::MetaItem, level, @str| -> bool)
502 let xs = [allow, warn, deny, forbid];
503 for &level in xs.iter() {
504 let level_name = level_to_str(level);
505 for attr in attrs.iter().filter(|m| level_name == m.name()) {
506 let meta = attr.node.value;
507 let metas = match meta.node {
508 ast::MetaList(_, ref metas) => metas,
510 sess.span_err(meta.span, "malformed lint attribute");
514 for meta in metas.iter() {
516 ast::MetaWord(lintname) => {
517 if !f(*meta, level, lintname) {
522 sess.span_err(meta.span, "malformed lint attribute");
531 fn check_while_true_expr(cx: &Context, e: &ast::Expr) {
533 ast::ExprWhile(cond, _) => {
535 ast::ExprLit(@codemap::Spanned {
536 node: ast::lit_bool(true), _}) =>
538 cx.span_lint(while_true, e.span,
539 "denote infinite loops with loop { ... }");
548 fn check_type_limits(cx: &Context, e: &ast::Expr) {
549 return match e.node {
550 ast::ExprBinary(_, binop, l, r) => {
551 if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) {
552 cx.span_lint(type_limits, e.span,
553 "comparison is useless due to type limits");
556 ast::ExprLit(lit) => {
557 match ty::get(ty::expr_ty(cx.tcx, e)).sty {
559 let int_type = if t == ast::ty_i {
560 cx.tcx.sess.targ_cfg.int_type
562 let (min, max) = int_ty_range(int_type);
563 let mut lit_val: i64 = match lit.node {
564 ast::lit_int(v, _) => v,
565 ast::lit_uint(v, _) => v as i64,
566 ast::lit_int_unsuffixed(v) => v,
569 if cx.negated_expr_id == e.id {
572 if lit_val < min || lit_val > max {
573 cx.span_lint(type_overflow, e.span,
574 "literal out of range for its type");
578 let uint_type = if t == ast::ty_u {
579 cx.tcx.sess.targ_cfg.uint_type
581 let (min, max) = uint_ty_range(uint_type);
582 let lit_val: u64 = match lit.node {
583 ast::lit_int(v, _) => v as u64,
584 ast::lit_uint(v, _) => v,
585 ast::lit_int_unsuffixed(v) => v as u64,
588 if lit_val < min || lit_val > max {
589 cx.span_lint(type_overflow, e.span,
590 "literal out of range for its type");
600 fn is_valid<T:cmp::Ord>(binop: ast::BinOp, v: T,
601 min: T, max: T) -> bool {
603 ast::BiLt => v <= max,
604 ast::BiLe => v < max,
605 ast::BiGt => v >= min,
606 ast::BiGe => v > min,
607 ast::BiEq | ast::BiNe => v >= min && v <= max,
612 fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
614 ast::BiLt => ast::BiGt,
615 ast::BiLe => ast::BiGe,
616 ast::BiGt => ast::BiLt,
617 ast::BiGe => ast::BiLe,
622 // for int & uint, be conservative with the warnings, so that the
623 // warnings are consistent between 32- and 64-bit platforms
624 fn int_ty_range(int_ty: ast::int_ty) -> (i64, i64) {
626 ast::ty_i => (i64::min_value, i64::max_value),
627 ast::ty_i8 => (i8::min_value as i64, i8::max_value as i64),
628 ast::ty_i16 => (i16::min_value as i64, i16::max_value as i64),
629 ast::ty_i32 => (i32::min_value as i64, i32::max_value as i64),
630 ast::ty_i64 => (i64::min_value, i64::max_value)
634 fn uint_ty_range(uint_ty: ast::uint_ty) -> (u64, u64) {
636 ast::ty_u => (u64::min_value, u64::max_value),
637 ast::ty_u8 => (u8::min_value as u64, u8::max_value as u64),
638 ast::ty_u16 => (u16::min_value as u64, u16::max_value as u64),
639 ast::ty_u32 => (u32::min_value as u64, u32::max_value as u64),
640 ast::ty_u64 => (u64::min_value, u64::max_value)
644 fn check_limits(tcx: ty::ctxt, binop: ast::BinOp,
645 l: &ast::Expr, r: &ast::Expr) -> bool {
646 let (lit, expr, swap) = match (&l.node, &r.node) {
647 (&ast::ExprLit(_), _) => (l, r, true),
648 (_, &ast::ExprLit(_)) => (r, l, false),
651 // Normalize the binop so that the literal is always on the RHS in
653 let norm_binop = if swap { rev_binop(binop) } else { binop };
654 match ty::get(ty::expr_ty(tcx, expr)).sty {
655 ty::ty_int(int_ty) => {
656 let (min, max) = int_ty_range(int_ty);
657 let lit_val: i64 = match lit.node {
658 ast::ExprLit(li) => match li.node {
659 ast::lit_int(v, _) => v,
660 ast::lit_uint(v, _) => v as i64,
661 ast::lit_int_unsuffixed(v) => v,
666 is_valid(norm_binop, lit_val, min, max)
668 ty::ty_uint(uint_ty) => {
669 let (min, max): (u64, u64) = uint_ty_range(uint_ty);
670 let lit_val: u64 = match lit.node {
671 ast::ExprLit(li) => match li.node {
672 ast::lit_int(v, _) => v as u64,
673 ast::lit_uint(v, _) => v,
674 ast::lit_int_unsuffixed(v) => v as u64,
679 is_valid(norm_binop, lit_val, min, max)
685 fn is_comparison(binop: ast::BinOp) -> bool {
687 ast::BiEq | ast::BiLt | ast::BiLe |
688 ast::BiNe | ast::BiGe | ast::BiGt => true,
694 fn check_item_ctypes(cx: &Context, it: &ast::item) {
695 fn check_ty(cx: &Context, ty: &ast::Ty) {
697 ast::ty_path(_, _, id) => {
698 match cx.tcx.def_map.get_copy(&id) {
699 ast::DefPrimTy(ast::ty_int(ast::ty_i)) => {
700 cx.span_lint(ctypes, ty.span,
701 "found rust type `int` in foreign module, while \
702 libc::c_int or libc::c_long should be used");
704 ast::DefPrimTy(ast::ty_uint(ast::ty_u)) => {
705 cx.span_lint(ctypes, ty.span,
706 "found rust type `uint` in foreign module, while \
707 libc::c_uint or libc::c_ulong should be used");
709 ast::DefTy(def_id) => {
710 if !adt::is_ffi_safe(cx.tcx, def_id) {
711 cx.span_lint(ctypes, ty.span,
712 "found enum type without foreign-function-safe \
713 representation annotation in foreign module");
714 // NOTE this message could be more helpful
720 ast::ty_ptr(ref mt) => { check_ty(cx, mt.ty) }
725 fn check_foreign_fn(cx: &Context, decl: &ast::fn_decl) {
726 for input in decl.inputs.iter() {
727 check_ty(cx, &input.ty);
729 check_ty(cx, &decl.output)
733 ast::item_foreign_mod(ref nmod) if !nmod.abis.is_intrinsic() => {
734 for ni in nmod.items.iter() {
736 ast::foreign_item_fn(ref decl, _) => {
737 check_foreign_fn(cx, decl);
739 ast::foreign_item_static(ref t, _) => { check_ty(cx, t); }
743 _ => {/* nothing to do */ }
747 fn check_heap_type(cx: &Context, span: Span, ty: ty::t) {
748 let xs = [managed_heap_memory, owned_heap_memory, heap_memory];
749 for &lint in xs.iter() {
750 if cx.get_level(lint) == allow { continue }
754 ty::fold_ty(cx.tcx, ty, |t| {
755 match ty::get(t).sty {
756 ty::ty_box(_) => n_box += 1,
757 ty::ty_uniq(_) => n_uniq += 1,
763 if n_uniq > 0 && lint != managed_heap_memory {
764 let s = ty_to_str(cx.tcx, ty);
765 let m = format!("type uses owned (~ type) pointers: {}", s);
766 cx.span_lint(lint, span, m);
769 if n_box > 0 && lint != owned_heap_memory {
770 let s = ty_to_str(cx.tcx, ty);
771 let m = format!("type uses managed (@ type) pointers: {}", s);
772 cx.span_lint(lint, span, m);
777 fn check_heap_item(cx: &Context, it: &ast::item) {
782 ast::item_struct(*) => check_heap_type(cx, it.span,
783 ty::node_id_to_type(cx.tcx,
788 // If it's a struct, we also have to check the fields' types
790 ast::item_struct(struct_def, _) => {
791 for struct_field in struct_def.fields.iter() {
792 check_heap_type(cx, struct_field.span,
793 ty::node_id_to_type(cx.tcx,
794 struct_field.node.id));
801 static crate_attrs: &'static [&'static str] = &[
802 "crate_type", "link", "feature", "no_uv", "no_main", "no_std",
803 "desc", "comment", "license", "copyright", // not used in rustc now
807 static obsolete_attrs: &'static [(&'static str, &'static str)] = &[
808 ("abi", "Use `extern \"abi\" fn` instead"),
809 ("auto_encode", "Use `#[deriving(Encodable)]` instead"),
810 ("auto_decode", "Use `#[deriving(Decodable)]` instead"),
811 ("fast_ffi", "Remove it"),
812 ("fixed_stack_segment", "Remove it"),
813 ("rust_stack", "Remove it"),
816 static other_attrs: &'static [&'static str] = &[
818 "address_insignificant", // can be crate-level too
819 "thread_local", // for statics
820 "allow", "deny", "forbid", "warn", // lint options
821 "deprecated", "experimental", "unstable", "stable", "locked", "frozen", //item stability
822 "crate_map", "cfg", "doc", "export_name", "link_section", "no_freeze",
823 "no_mangle", "no_send", "static_assert", "unsafe_no_drop_flag",
824 "packed", "simd", "repr", "deriving", "unsafe_destructor",
827 "path", "link_name", "link_args", "nolink", "macro_escape", "no_implicit_prelude",
830 "test", "bench", "should_fail", "ignore", "inline", "lang", "main", "start",
831 "no_split_stack", "cold",
833 // internal attribute: bypass privacy inside items
834 "!resolve_unexported",
837 fn check_crate_attrs_usage(cx: &Context, attrs: &[ast::Attribute]) {
839 for attr in attrs.iter() {
840 let name = attr.node.value.name();
841 let mut iter = crate_attrs.iter().chain(other_attrs.iter());
842 if !iter.any(|other_attr| { name.equiv(other_attr) }) {
843 cx.span_lint(attribute_usage, attr.span, "unknown crate attribute");
848 fn check_attrs_usage(cx: &Context, attrs: &[ast::Attribute]) {
849 // check if element has crate-level, obsolete, or any unknown attributes.
851 for attr in attrs.iter() {
852 let name = attr.node.value.name();
853 for crate_attr in crate_attrs.iter() {
854 if name.equiv(crate_attr) {
855 let msg = match attr.node.style {
856 ast::AttrOuter => "crate-level attribute should be an inner attribute: \
857 add semicolon at end",
858 ast::AttrInner => "crate-level attribute should be in the root module",
860 cx.span_lint(attribute_usage, attr.span, msg);
865 for &(obs_attr, obs_alter) in obsolete_attrs.iter() {
866 if name.equiv(&obs_attr) {
867 cx.span_lint(attribute_usage, attr.span,
868 format!("obsolete attribute: {:s}", obs_alter));
873 if !other_attrs.iter().any(|other_attr| { name.equiv(other_attr) }) {
874 cx.span_lint(attribute_usage, attr.span, "unknown attribute");
879 fn check_heap_expr(cx: &Context, e: &ast::Expr) {
880 let ty = ty::expr_ty(cx.tcx, e);
881 check_heap_type(cx, e.span, ty);
884 fn check_path_statement(cx: &Context, s: &ast::Stmt) {
886 ast::StmtSemi(@ast::Expr { node: ast::ExprPath(_), _ }, _) => {
887 cx.span_lint(path_statement, s.span,
888 "path statement with no effect");
894 fn check_item_non_camel_case_types(cx: &Context, it: &ast::item) {
895 fn is_camel_case(cx: ty::ctxt, ident: ast::Ident) -> bool {
896 let ident = cx.sess.str_of(ident);
897 assert!(!ident.is_empty());
898 let ident = ident.trim_chars(&'_');
900 // start with a non-lowercase letter rather than non-uppercase
901 // ones (some scripts don't have a concept of upper/lowercase)
902 !ident.char_at(0).is_lowercase() &&
903 !ident.contains_char('_')
906 fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
907 if !is_camel_case(cx.tcx, ident) {
909 non_camel_case_types, span,
910 format!("{} `{}` should have a camel case identifier",
911 sort, cx.tcx.sess.str_of(ident)));
916 ast::item_ty(*) | ast::item_struct(*) => {
917 check_case(cx, "type", it.ident, it.span)
919 ast::item_trait(*) => {
920 check_case(cx, "trait", it.ident, it.span)
922 ast::item_enum(ref enum_definition, _) => {
923 check_case(cx, "type", it.ident, it.span);
924 for variant in enum_definition.variants.iter() {
925 check_case(cx, "variant", variant.node.name, variant.span);
932 fn check_item_non_uppercase_statics(cx: &Context, it: &ast::item) {
934 // only check static constants
935 ast::item_static(_, ast::MutImmutable, _) => {
936 let s = cx.tcx.sess.str_of(it.ident);
937 // check for lowercase letters rather than non-uppercase
938 // ones (some scripts don't have a concept of
940 if s.chars().any(|c| c.is_lowercase()) {
941 cx.span_lint(non_uppercase_statics, it.span,
942 "static constant should have an uppercase identifier");
949 fn check_pat_non_uppercase_statics(cx: &Context, p: &ast::Pat) {
950 // Lint for constants that look like binding identifiers (#7526)
951 match (&p.node, cx.tcx.def_map.find(&p.id)) {
952 (&ast::PatIdent(_, ref path, _), Some(&ast::DefStatic(_, false))) => {
953 // last identifier alone is right choice for this lint.
954 let ident = path.segments.last().identifier;
955 let s = cx.tcx.sess.str_of(ident);
956 if s.chars().any(|c| c.is_lowercase()) {
957 cx.span_lint(non_uppercase_pattern_statics, path.span,
958 "static constant in pattern should be all caps");
965 fn check_unused_unsafe(cx: &Context, e: &ast::Expr) {
967 // Don't warn about generated blocks, that'll just pollute the output.
968 ast::ExprBlock(ref blk) => {
969 if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
970 !cx.tcx.used_unsafe.contains(&blk.id) {
971 cx.span_lint(unused_unsafe, blk.span,
972 "unnecessary `unsafe` block");
979 fn check_unsafe_block(cx: &Context, e: &ast::Expr) {
981 // Don't warn about generated blocks, that'll just pollute the output.
982 ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => {
983 cx.span_lint(unsafe_block, blk.span, "usage of an `unsafe` block");
989 fn check_unused_mut_pat(cx: &Context, p: &ast::Pat) {
991 ast::PatIdent(ast::BindByValue(ast::MutMutable),
992 ref path, _) if pat_util::pat_is_binding(cx.tcx.def_map, p)=> {
993 // `let mut _a = 1;` doesn't need a warning.
994 let initial_underscore = match path.segments {
995 [ast::PathSegment { identifier: id, _ }] => {
996 cx.tcx.sess.str_of(id).starts_with("_")
999 cx.tcx.sess.span_bug(p.span,
1000 "mutable binding that doesn't \
1001 consist of exactly one segment");
1005 if !initial_underscore && !cx.tcx.used_mut_nodes.contains(&p.id) {
1006 cx.span_lint(unused_mut, p.span,
1007 "variable does not need to be mutable");
1014 fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
1015 // Warn if string and vector literals with sigils are immediately borrowed.
1016 // Those can have the sigil removed.
1018 ast::ExprVstore(e2, ast::ExprVstoreUniq) |
1019 ast::ExprVstore(e2, ast::ExprVstoreBox) => {
1021 ast::ExprLit(@codemap::Spanned{node: ast::lit_str(*), _}) |
1022 ast::ExprVec(*) => {}
1030 match cx.tcx.adjustments.find_copy(&e.id) {
1031 Some(@ty::AutoDerefRef(ty::AutoDerefRef {
1032 autoref: Some(ty::AutoBorrowVec(*)), _ })) => {
1033 cx.span_lint(unnecessary_allocation, e.span,
1034 "unnecessary allocation, the sigil can be removed");
1041 fn check_missing_doc_attrs(cx: &Context,
1043 attrs: &[ast::Attribute],
1045 desc: &'static str) {
1046 // If we're building a test harness, then warning about
1047 // documentation is probably not really relevant right now.
1048 if cx.tcx.sess.opts.test { return }
1050 // `#[doc(hidden)]` disables missing_doc check.
1051 if cx.is_doc_hidden { return }
1053 // Only check publicly-visible items, using the result from the
1055 if !cx.exported_items.contains(&id) { return }
1057 if !attrs.iter().any(|a| a.node.is_sugared_doc) {
1058 cx.span_lint(missing_doc, sp,
1059 format!("missing documentation for {}", desc));
1063 fn check_missing_doc_item(cx: &mut Context, it: &ast::item) { // XXX doesn't need to be mut
1064 let desc = match it.node {
1065 ast::item_fn(*) => "a function",
1066 ast::item_mod(*) => "a module",
1067 ast::item_enum(*) => "an enum",
1068 ast::item_struct(*) => "a struct",
1069 ast::item_trait(*) => "a trait",
1072 check_missing_doc_attrs(cx, it.id, it.attrs, it.span, desc);
1075 fn check_missing_doc_method(cx: &Context, m: &ast::method) {
1076 let did = ast::DefId {
1077 crate: ast::LOCAL_CRATE,
1080 match cx.tcx.methods.find(&did) {
1081 None => cx.tcx.sess.span_bug(m.span, "missing method descriptor?!"),
1083 match md.container {
1084 // Always check default methods defined on traits.
1085 ty::TraitContainer(*) => {}
1086 // For methods defined on impls, it depends on whether
1087 // it is an implementation for a trait or is a plain
1089 ty::ImplContainer(cid) => {
1090 match ty::impl_trait_ref(cx.tcx, cid) {
1091 Some(*) => return, // impl for trait: don't doc
1092 None => {} // plain impl: doc according to privacy
1098 check_missing_doc_attrs(cx, m.id, m.attrs, m.span, "a method");
1101 fn check_missing_doc_ty_method(cx: &Context, tm: &ast::TypeMethod) {
1102 check_missing_doc_attrs(cx, tm.id, tm.attrs, tm.span, "a type method");
1105 fn check_missing_doc_struct_field(cx: &Context, sf: &ast::struct_field) {
1106 match sf.node.kind {
1107 ast::named_field(_, vis) if vis != ast::private =>
1108 check_missing_doc_attrs(cx, cx.cur_struct_def_id, sf.node.attrs,
1109 sf.span, "a struct field"),
1114 fn check_missing_doc_variant(cx: &Context, v: &ast::variant) {
1115 check_missing_doc_attrs(cx, v.node.id, v.node.attrs, v.span, "a variant");
1118 /// Checks for use of items with #[deprecated], #[experimental] and
1119 /// #[unstable] (or none of them) attributes.
1120 fn check_stability(cx: &Context, e: &ast::Expr) {
1121 let def = match e.node {
1122 ast::ExprMethodCall(*) |
1124 ast::ExprStruct(*) => {
1125 match cx.tcx.def_map.find(&e.id) {
1133 let id = ast_util::def_id_of_def(def);
1135 let stability = if ast_util::is_local(id) {
1137 match cx.tcx.items.find(&id.node) {
1139 let s = do ast_node.with_attrs |attrs| {
1141 attr::find_stability(a.iter().map(|a| a.meta()))
1147 // no possibility of having attributes
1148 // (e.g. it's a local variable), so just
1153 _ => cx.tcx.sess.bug(format!("handle_def: {:?} not found", id))
1159 // run through all the attributes and take the first
1161 do csearch::get_item_attrs(cx.tcx.cstore, id) |meta_items| {
1163 s = attr::find_stability(meta_items.move_iter())
1169 let (lint, label) = match stability {
1170 // no stability attributes == Unstable
1171 None => (unstable, "unmarked"),
1172 Some(attr::Stability { level: attr::Unstable, _ }) =>
1173 (unstable, "unstable"),
1174 Some(attr::Stability { level: attr::Experimental, _ }) =>
1175 (experimental, "experimental"),
1176 Some(attr::Stability { level: attr::Deprecated, _ }) =>
1177 (deprecated, "deprecated"),
1181 let msg = match stability {
1182 Some(attr::Stability { text: Some(ref s), _ }) => {
1183 format!("use of {} item: {}", label, *s)
1185 _ => format!("use of {} item", label)
1188 cx.span_lint(lint, e.span, msg);
1191 impl<'self> Visitor<()> for Context<'self> {
1192 fn visit_item(&mut self, it: @ast::item, _: ()) {
1193 do self.with_lint_attrs(it.attrs) |cx| {
1194 check_item_ctypes(cx, it);
1195 check_item_non_camel_case_types(cx, it);
1196 check_item_non_uppercase_statics(cx, it);
1197 check_heap_item(cx, it);
1198 check_missing_doc_item(cx, it);
1199 check_attrs_usage(cx, it.attrs);
1201 do cx.visit_ids |v| {
1202 v.visit_item(it, ());
1205 visit::walk_item(cx, it, ());
1209 fn visit_foreign_item(&mut self, it: @ast::foreign_item, _: ()) {
1210 do self.with_lint_attrs(it.attrs) |cx| {
1211 check_attrs_usage(cx, it.attrs);
1212 visit::walk_foreign_item(cx, it, ());
1216 fn visit_view_item(&mut self, i: &ast::view_item, _: ()) {
1217 do self.with_lint_attrs(i.attrs) |cx| {
1218 check_attrs_usage(cx, i.attrs);
1219 visit::walk_view_item(cx, i, ());
1223 fn visit_pat(&mut self, p: &ast::Pat, _: ()) {
1224 check_pat_non_uppercase_statics(self, p);
1225 check_unused_mut_pat(self, p);
1227 visit::walk_pat(self, p, ());
1230 fn visit_expr(&mut self, e: @ast::Expr, _: ()) {
1232 ast::ExprUnary(_, ast::UnNeg, expr) => {
1233 // propagate negation, if the negation itself isn't negated
1234 if self.negated_expr_id != e.id {
1235 self.negated_expr_id = expr.id;
1238 ast::ExprParen(expr) => if self.negated_expr_id == e.id {
1239 self.negated_expr_id = expr.id
1244 check_while_true_expr(self, e);
1245 check_stability(self, e);
1246 check_unused_unsafe(self, e);
1247 check_unsafe_block(self, e);
1248 check_unnecessary_allocation(self, e);
1249 check_heap_expr(self, e);
1251 check_type_limits(self, e);
1253 visit::walk_expr(self, e, ());
1256 fn visit_stmt(&mut self, s: @ast::Stmt, _: ()) {
1257 check_path_statement(self, s);
1259 visit::walk_stmt(self, s, ());
1262 fn visit_fn(&mut self, fk: &visit::fn_kind, decl: &ast::fn_decl,
1263 body: &ast::Block, span: Span, id: ast::NodeId, _: ()) {
1264 let recurse = |this: &mut Context| {
1265 visit::walk_fn(this, fk, decl, body, span, id, ());
1269 visit::fk_method(_, _, m) => {
1270 do self.with_lint_attrs(m.attrs) |cx| {
1271 check_missing_doc_method(cx, m);
1272 check_attrs_usage(cx, m.attrs);
1274 do cx.visit_ids |v| {
1275 v.visit_fn(fk, decl, body, span, id, ());
1285 fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) {
1286 do self.with_lint_attrs(t.attrs) |cx| {
1287 check_missing_doc_ty_method(cx, t);
1288 check_attrs_usage(cx, t.attrs);
1290 visit::walk_ty_method(cx, t, ());
1294 fn visit_struct_def(&mut self,
1295 s: @ast::struct_def,
1300 let old_id = self.cur_struct_def_id;
1301 self.cur_struct_def_id = id;
1302 visit::walk_struct_def(self, s, i, g, id, ());
1303 self.cur_struct_def_id = old_id;
1306 fn visit_struct_field(&mut self, s: @ast::struct_field, _: ()) {
1307 do self.with_lint_attrs(s.node.attrs) |cx| {
1308 check_missing_doc_struct_field(cx, s);
1309 check_attrs_usage(cx, s.node.attrs);
1311 visit::walk_struct_field(cx, s, ());
1315 fn visit_variant(&mut self, v: &ast::variant, g: &ast::Generics, _: ()) {
1316 do self.with_lint_attrs(v.node.attrs) |cx| {
1317 check_missing_doc_variant(cx, v);
1318 check_attrs_usage(cx, v.node.attrs);
1320 visit::walk_variant(cx, v, g, ());
1325 impl<'self> ast_util::IdVisitingOperation for Context<'self> {
1326 fn visit_id(&self, id: ast::NodeId) {
1327 match self.tcx.sess.lints.pop(&id) {
1330 for (lint, span, msg) in l.move_iter() {
1331 self.span_lint(lint, span, msg)
1338 pub fn check_crate(tcx: ty::ctxt,
1339 exported_items: &privacy::ExportedItems,
1340 crate: &ast::Crate) {
1341 let mut cx = Context {
1342 dict: @get_lint_dict(),
1343 cur: SmallIntMap::new(),
1345 exported_items: exported_items,
1346 cur_struct_def_id: -1,
1347 is_doc_hidden: false,
1352 // Install default lint levels, followed by the command line levels, and
1353 // then actually visit the whole crate.
1354 for (_, spec) in cx.dict.iter() {
1355 cx.set_level(spec.lint, spec.default, Default);
1357 for &(lint, level) in tcx.sess.opts.lint_opts.iter() {
1358 cx.set_level(lint, level, CommandLine);
1360 do cx.with_lint_attrs(crate.attrs) |cx| {
1361 do cx.visit_ids |v| {
1362 v.visited_outermost = true;
1363 visit::walk_crate(v, crate, ());
1366 check_crate_attrs_usage(cx, crate.attrs);
1368 visit::walk_crate(cx, crate, ());
1371 // If we missed any lints added to the session, then there's a bug somewhere
1372 // in the iteration code.
1373 for (id, v) in tcx.sess.lints.iter() {
1374 for &(lint, span, ref msg) in v.iter() {
1375 tcx.sess.span_bug(span, format!("unprocessed lint {:?} at {}: {}",
1377 ast_map::node_id_to_str(tcx.items,
1379 token::get_ident_interner()),
1384 tcx.sess.abort_if_errors();