]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/lint.rs
Get "make check" to work with unused-attribute
[rust.git] / src / librustc / middle / lint.rs
1 // Copyright 2012-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 '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.
16 //!
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.
23 //!
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.
30 //!
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.
35
36 #![allow(non_camel_case_types)]
37
38 use driver::session;
39 use metadata::csearch;
40 use middle::dead::DEAD_CODE_LINT_STR;
41 use middle::pat_util;
42 use middle::privacy;
43 use middle::trans::adt; // for `adt::is_ffi_safe`
44 use middle::ty;
45 use middle::typeck::astconv::{ast_ty_to_ty, AstConv};
46 use middle::typeck::infer;
47 use middle::typeck;
48 use util::ppaux::{ty_to_str};
49 use util::nodemap::NodeSet;
50
51 use std::cmp;
52 use collections::HashMap;
53 use std::i16;
54 use std::i32;
55 use std::i64;
56 use std::i8;
57 use std::rc::Rc;
58 use std::to_str::ToStr;
59 use std::u16;
60 use std::u32;
61 use std::u64;
62 use std::u8;
63 use collections::SmallIntMap;
64 use syntax::abi;
65 use syntax::ast_map;
66 use syntax::ast_util::IdVisitingOperation;
67 use syntax::attr::AttrMetaMethods;
68 use syntax::attr;
69 use syntax::codemap::Span;
70 use syntax::parse::token::InternedString;
71 use syntax::parse::token;
72 use syntax::visit::Visitor;
73 use syntax::{ast, ast_util, visit};
74
75 #[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
76 pub enum Lint {
77     CTypes,
78     UnusedImports,
79     UnnecessaryQualification,
80     WhileTrue,
81     PathStatement,
82     UnrecognizedLint,
83     NonCamelCaseTypes,
84     NonUppercaseStatics,
85     NonUppercasePatternStatics,
86     UppercaseVariables,
87     UnnecessaryParens,
88     TypeLimits,
89     TypeOverflow,
90     UnusedUnsafe,
91     UnsafeBlock,
92     AttributeUsage,
93     UnusedAttribute,
94     UnknownFeatures,
95     UnknownCrateType,
96     UnsignedNegate,
97
98     ManagedHeapMemory,
99     OwnedHeapMemory,
100     HeapMemory,
101
102     UnusedVariable,
103     DeadAssignment,
104     UnusedMut,
105     UnnecessaryAllocation,
106     DeadCode,
107     VisiblePrivateTypes,
108     UnnecessaryTypecast,
109
110     MissingDoc,
111     UnreachableCode,
112
113     Deprecated,
114     Experimental,
115     Unstable,
116
117     UnusedMustUse,
118     UnusedResult,
119
120     DeprecatedOwnedVector,
121
122     Warnings,
123
124     RawPointerDeriving,
125 }
126
127 pub fn level_to_str(lv: Level) -> &'static str {
128     match lv {
129       Allow => "allow",
130       Warn => "warn",
131       Deny => "deny",
132       Forbid => "forbid"
133     }
134 }
135
136 #[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
137 pub enum Level {
138     Allow, Warn, Deny, Forbid
139 }
140
141 #[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
142 pub struct LintSpec {
143     pub default: Level,
144     pub lint: Lint,
145     pub desc: &'static str,
146 }
147
148 pub type LintDict = HashMap<&'static str, LintSpec>;
149
150 #[deriving(Eq)]
151 enum LintSource {
152     Node(Span),
153     Default,
154     CommandLine
155 }
156
157 static lint_table: &'static [(&'static str, LintSpec)] = &[
158     ("ctypes",
159      LintSpec {
160         lint: CTypes,
161         desc: "proper use of libc types in foreign modules",
162         default: Warn
163      }),
164
165     ("unused_imports",
166      LintSpec {
167         lint: UnusedImports,
168         desc: "imports that are never used",
169         default: Warn
170      }),
171
172     ("unnecessary_qualification",
173      LintSpec {
174         lint: UnnecessaryQualification,
175         desc: "detects unnecessarily qualified names",
176         default: Allow
177      }),
178
179     ("while_true",
180      LintSpec {
181         lint: WhileTrue,
182         desc: "suggest using `loop { }` instead of `while true { }`",
183         default: Warn
184      }),
185
186     ("path_statement",
187      LintSpec {
188         lint: PathStatement,
189         desc: "path statements with no effect",
190         default: Warn
191      }),
192
193     ("unrecognized_lint",
194      LintSpec {
195         lint: UnrecognizedLint,
196         desc: "unrecognized lint attribute",
197         default: Warn
198      }),
199
200     ("non_camel_case_types",
201      LintSpec {
202         lint: NonCamelCaseTypes,
203         desc: "types, variants and traits should have camel case names",
204         default: Warn
205      }),
206
207     ("non_uppercase_statics",
208      LintSpec {
209          lint: NonUppercaseStatics,
210          desc: "static constants should have uppercase identifiers",
211          default: Allow
212      }),
213
214     ("non_uppercase_pattern_statics",
215      LintSpec {
216          lint: NonUppercasePatternStatics,
217          desc: "static constants in match patterns should be all caps",
218          default: Warn
219      }),
220
221     ("uppercase_variables",
222      LintSpec {
223          lint: UppercaseVariables,
224          desc: "variable and structure field names should start with a lowercase character",
225          default: Warn
226      }),
227
228      ("unnecessary_parens",
229      LintSpec {
230         lint: UnnecessaryParens,
231         desc: "`if`, `match`, `while` and `return` do not need parentheses",
232         default: Warn
233      }),
234
235     ("managed_heap_memory",
236      LintSpec {
237         lint: ManagedHeapMemory,
238         desc: "use of managed (@ type) heap memory",
239         default: Allow
240      }),
241
242     ("owned_heap_memory",
243      LintSpec {
244         lint: OwnedHeapMemory,
245         desc: "use of owned (Box type) heap memory",
246         default: Allow
247      }),
248
249     ("heap_memory",
250      LintSpec {
251         lint: HeapMemory,
252         desc: "use of any (Box type or @ type) heap memory",
253         default: Allow
254      }),
255
256     ("type_limits",
257      LintSpec {
258         lint: TypeLimits,
259         desc: "comparisons made useless by limits of the types involved",
260         default: Warn
261      }),
262
263     ("type_overflow",
264      LintSpec {
265         lint: TypeOverflow,
266         desc: "literal out of range for its type",
267         default: Warn
268      }),
269
270
271     ("unused_unsafe",
272      LintSpec {
273         lint: UnusedUnsafe,
274         desc: "unnecessary use of an `unsafe` block",
275         default: Warn
276     }),
277
278     ("unsafe_block",
279      LintSpec {
280         lint: UnsafeBlock,
281         desc: "usage of an `unsafe` block",
282         default: Allow
283     }),
284
285     ("attribute_usage",
286      LintSpec {
287         lint: AttributeUsage,
288         desc: "detects bad use of attributes",
289         default: Warn
290     }),
291
292     ("unused_attribute",
293      LintSpec {
294          lint: UnusedAttribute,
295          desc: "detects attributes that were not used by the compiler",
296          default: Warn
297     }),
298
299     ("unused_variable",
300      LintSpec {
301         lint: UnusedVariable,
302         desc: "detect variables which are not used in any way",
303         default: Warn
304     }),
305
306     ("dead_assignment",
307      LintSpec {
308         lint: DeadAssignment,
309         desc: "detect assignments that will never be read",
310         default: Warn
311     }),
312
313     ("unnecessary_typecast",
314      LintSpec {
315         lint: UnnecessaryTypecast,
316         desc: "detects unnecessary type casts, that can be removed",
317         default: Allow,
318     }),
319
320     ("unused_mut",
321      LintSpec {
322         lint: UnusedMut,
323         desc: "detect mut variables which don't need to be mutable",
324         default: Warn
325     }),
326
327     ("unnecessary_allocation",
328      LintSpec {
329         lint: UnnecessaryAllocation,
330         desc: "detects unnecessary allocations that can be eliminated",
331         default: Warn
332     }),
333
334     (DEAD_CODE_LINT_STR,
335      LintSpec {
336         lint: DeadCode,
337         desc: "detect piece of code that will never be used",
338         default: Warn
339     }),
340     ("visible_private_types",
341      LintSpec {
342         lint: VisiblePrivateTypes,
343         desc: "detect use of private types in exported type signatures",
344         default: Warn
345     }),
346
347     ("missing_doc",
348      LintSpec {
349         lint: MissingDoc,
350         desc: "detects missing documentation for public members",
351         default: Allow
352     }),
353
354     ("unreachable_code",
355      LintSpec {
356         lint: UnreachableCode,
357         desc: "detects unreachable code",
358         default: Warn
359     }),
360
361     ("deprecated",
362      LintSpec {
363         lint: Deprecated,
364         desc: "detects use of #[deprecated] items",
365         default: Warn
366     }),
367
368     ("experimental",
369      LintSpec {
370         lint: Experimental,
371         desc: "detects use of #[experimental] items",
372         default: Warn
373     }),
374
375     ("unstable",
376      LintSpec {
377         lint: Unstable,
378         desc: "detects use of #[unstable] items (incl. items with no stability attribute)",
379         default: Allow
380     }),
381
382     ("warnings",
383      LintSpec {
384         lint: Warnings,
385         desc: "mass-change the level for lints which produce warnings",
386         default: Warn
387     }),
388
389     ("unknown_features",
390      LintSpec {
391         lint: UnknownFeatures,
392         desc: "unknown features found in crate-level #[feature] directives",
393         default: Deny,
394     }),
395
396     ("unknown_crate_type",
397     LintSpec {
398         lint: UnknownCrateType,
399         desc: "unknown crate type found in #[crate_type] directive",
400         default: Deny,
401     }),
402
403     ("unsigned_negate",
404     LintSpec {
405         lint: UnsignedNegate,
406         desc: "using an unary minus operator on unsigned type",
407         default: Warn
408     }),
409
410     ("unused_must_use",
411     LintSpec {
412         lint: UnusedMustUse,
413         desc: "unused result of a type flagged as #[must_use]",
414         default: Warn,
415     }),
416
417     ("unused_result",
418     LintSpec {
419         lint: UnusedResult,
420         desc: "unused result of an expression in a statement",
421         default: Allow,
422     }),
423
424     ("deprecated_owned_vector",
425      LintSpec {
426         lint: DeprecatedOwnedVector,
427         desc: "use of a `~[T]` vector",
428         default: Allow,
429     }),
430
431     ("raw_pointer_deriving",
432      LintSpec {
433         lint: RawPointerDeriving,
434         desc: "uses of #[deriving] with raw pointers are rarely correct",
435         default: Warn,
436     }),
437 ];
438
439 /*
440   Pass names should not contain a '-', as the compiler normalizes
441   '-' to '_' in command-line flags
442  */
443 pub fn get_lint_dict() -> LintDict {
444     lint_table.iter().map(|&(k, v)| (k, v)).collect()
445 }
446
447 struct Context<'a> {
448     // All known lint modes (string versions)
449     dict: LintDict,
450     // Current levels of each lint warning
451     cur: SmallIntMap<(Level, LintSource)>,
452     // context we're checking in (used to access fields like sess)
453     tcx: &'a ty::ctxt,
454     // Items exported by the crate; used by the missing_doc lint.
455     exported_items: &'a privacy::ExportedItems,
456     // The id of the current `ast::StructDef` being walked.
457     cur_struct_def_id: ast::NodeId,
458     // Whether some ancestor of the current node was marked
459     // #[doc(hidden)].
460     is_doc_hidden: bool,
461
462     // When recursing into an attributed node of the ast which modifies lint
463     // levels, this stack keeps track of the previous lint levels of whatever
464     // was modified.
465     lint_stack: Vec<(Lint, Level, LintSource)>,
466
467     // id of the last visited negated expression
468     negated_expr_id: ast::NodeId,
469
470     // ids of structs/enums which have been checked for raw_pointer_deriving
471     checked_raw_pointers: NodeSet,
472 }
473
474 impl<'a> Context<'a> {
475     fn get_level(&self, lint: Lint) -> Level {
476         match self.cur.find(&(lint as uint)) {
477           Some(&(lvl, _)) => lvl,
478           None => Allow
479         }
480     }
481
482     fn get_source(&self, lint: Lint) -> LintSource {
483         match self.cur.find(&(lint as uint)) {
484           Some(&(_, src)) => src,
485           None => Default
486         }
487     }
488
489     fn set_level(&mut self, lint: Lint, level: Level, src: LintSource) {
490         if level == Allow {
491             self.cur.remove(&(lint as uint));
492         } else {
493             self.cur.insert(lint as uint, (level, src));
494         }
495     }
496
497     fn lint_to_str(&self, lint: Lint) -> &'static str {
498         for (k, v) in self.dict.iter() {
499             if v.lint == lint {
500                 return *k;
501             }
502         }
503         fail!("unregistered lint {:?}", lint);
504     }
505
506     fn span_lint(&self, lint: Lint, span: Span, msg: &str) {
507         let (level, src) = match self.cur.find(&(lint as uint)) {
508             None => { return }
509             Some(&(Warn, src)) => (self.get_level(Warnings), src),
510             Some(&pair) => pair,
511         };
512         if level == Allow { return }
513
514         let mut note = None;
515         let msg = match src {
516             Default => {
517                 format_strbuf!("{}, \\#[{}({})] on by default",
518                                msg,
519                                level_to_str(level),
520                                self.lint_to_str(lint))
521             },
522             CommandLine => {
523                 format!("{} [-{} {}]", msg,
524                     match level {
525                         Warn => 'W', Deny => 'D', Forbid => 'F',
526                         Allow => fail!()
527                     }, self.lint_to_str(lint).replace("_", "-"))
528             },
529             Node(src) => {
530                 note = Some(src);
531                 msg.to_str()
532             }
533         };
534         match level {
535             Warn => self.tcx.sess.span_warn(span, msg.as_slice()),
536             Deny | Forbid => self.tcx.sess.span_err(span, msg.as_slice()),
537             Allow => fail!(),
538         }
539
540         for &span in note.iter() {
541             self.tcx.sess.span_note(span, "lint level defined here");
542         }
543     }
544
545     /**
546      * Merge the lints specified by any lint attributes into the
547      * current lint context, call the provided function, then reset the
548      * lints in effect to their previous state.
549      */
550     fn with_lint_attrs(&mut self,
551                        attrs: &[ast::Attribute],
552                        f: |&mut Context|) {
553         // Parse all of the lint attributes, and then add them all to the
554         // current dictionary of lint information. Along the way, keep a history
555         // of what we changed so we can roll everything back after invoking the
556         // specified closure
557         let mut pushed = 0u;
558         each_lint(&self.tcx.sess, attrs, |meta, level, lintname| {
559             match self.dict.find_equiv(&lintname) {
560                 None => {
561                     self.span_lint(
562                         UnrecognizedLint,
563                         meta.span,
564                         format!("unknown `{}` attribute: `{}`",
565                                 level_to_str(level), lintname).as_slice());
566                 }
567                 Some(lint) => {
568                     let lint = lint.lint;
569                     let now = self.get_level(lint);
570                     if now == Forbid && level != Forbid {
571                         self.tcx.sess.span_err(meta.span,
572                         format!("{}({}) overruled by outer forbid({})",
573                                 level_to_str(level),
574                                 lintname,
575                                 lintname).as_slice());
576                     } else if now != level {
577                         let src = self.get_source(lint);
578                         self.lint_stack.push((lint, now, src));
579                         pushed += 1;
580                         self.set_level(lint, level, Node(meta.span));
581                     }
582                 }
583             }
584             true
585         });
586
587         let old_is_doc_hidden = self.is_doc_hidden;
588         self.is_doc_hidden =
589             self.is_doc_hidden ||
590             attrs.iter()
591                  .any(|attr| {
592                      attr.name().equiv(&("doc")) &&
593                      match attr.meta_item_list() {
594                          None => false,
595                          Some(l) => {
596                              attr::contains_name(l.as_slice(), "hidden")
597                          }
598                      }
599                  });
600
601         f(self);
602
603         // rollback
604         self.is_doc_hidden = old_is_doc_hidden;
605         for _ in range(0, pushed) {
606             let (lint, lvl, src) = self.lint_stack.pop().unwrap();
607             self.set_level(lint, lvl, src);
608         }
609     }
610
611     fn visit_ids(&self, f: |&mut ast_util::IdVisitor<Context>|) {
612         let mut v = ast_util::IdVisitor {
613             operation: self,
614             pass_through_items: false,
615             visited_outermost: false,
616         };
617         f(&mut v);
618     }
619 }
620
621 // Check that every lint from the list of attributes satisfies `f`.
622 // Return true if that's the case. Otherwise return false.
623 pub fn each_lint(sess: &session::Session,
624                  attrs: &[ast::Attribute],
625                  f: |@ast::MetaItem, Level, InternedString| -> bool)
626                  -> bool {
627     let xs = [Allow, Warn, Deny, Forbid];
628     for &level in xs.iter() {
629         let level_name = level_to_str(level);
630         for attr in attrs.iter().filter(|m| m.check_name(level_name)) {
631             let meta = attr.node.value;
632             let metas = match meta.node {
633                 ast::MetaList(_, ref metas) => metas,
634                 _ => {
635                     sess.span_err(meta.span, "malformed lint attribute");
636                     continue;
637                 }
638             };
639             for meta in metas.iter() {
640                 match meta.node {
641                     ast::MetaWord(ref lintname) => {
642                         if !f(*meta, level, (*lintname).clone()) {
643                             return false;
644                         }
645                     }
646                     _ => {
647                         sess.span_err(meta.span, "malformed lint attribute");
648                     }
649                 }
650             }
651         }
652     }
653     true
654 }
655
656 // Check from a list of attributes if it contains the appropriate
657 // `#[level(lintname)]` attribute (e.g. `#[allow(dead_code)]).
658 pub fn contains_lint(attrs: &[ast::Attribute],
659                      level: Level,
660                      lintname: &'static str)
661                      -> bool {
662     let level_name = level_to_str(level);
663     for attr in attrs.iter().filter(|m| m.name().equiv(&level_name)) {
664         if attr.meta_item_list().is_none() {
665             continue
666         }
667         let list = attr.meta_item_list().unwrap();
668         for meta_item in list.iter() {
669             if meta_item.name().equiv(&lintname) {
670                 return true;
671             }
672         }
673     }
674     false
675 }
676
677 fn check_while_true_expr(cx: &Context, e: &ast::Expr) {
678     match e.node {
679         ast::ExprWhile(cond, _) => {
680             match cond.node {
681                 ast::ExprLit(lit) => {
682                     match lit.node {
683                         ast::LitBool(true) => {
684                             cx.span_lint(WhileTrue,
685                                          e.span,
686                                          "denote infinite loops with loop \
687                                           { ... }");
688                         }
689                         _ => {}
690                     }
691                 }
692                 _ => ()
693             }
694         }
695         _ => ()
696     }
697 }
698 impl<'a> AstConv for Context<'a>{
699     fn tcx<'a>(&'a self) -> &'a ty::ctxt { self.tcx }
700
701     fn get_item_ty(&self, id: ast::DefId) -> ty::ty_param_bounds_and_ty {
702         ty::lookup_item_type(self.tcx, id)
703     }
704
705     fn get_trait_def(&self, id: ast::DefId) -> Rc<ty::TraitDef> {
706         ty::lookup_trait_def(self.tcx, id)
707     }
708
709     fn ty_infer(&self, _span: Span) -> ty::t {
710         infer::new_infer_ctxt(self.tcx).next_ty_var()
711     }
712 }
713
714
715 fn check_unused_casts(cx: &Context, e: &ast::Expr) {
716     return match e.node {
717         ast::ExprCast(expr, ty) => {
718             let t_t = ast_ty_to_ty(cx, &infer::new_infer_ctxt(cx.tcx), ty);
719             if  ty::get(ty::expr_ty(cx.tcx, expr)).sty == ty::get(t_t).sty {
720                 cx.span_lint(UnnecessaryTypecast, ty.span,
721                              "unnecessary type cast");
722             }
723         }
724         _ => ()
725     };
726 }
727
728 fn check_type_limits(cx: &Context, e: &ast::Expr) {
729     return match e.node {
730         ast::ExprUnary(ast::UnNeg, ex) => {
731             match ex.node  {
732                 ast::ExprLit(lit) => {
733                     match lit.node {
734                         ast::LitUint(..) => {
735                             cx.span_lint(UnsignedNegate, e.span,
736                                          "negation of unsigned int literal may be unintentional");
737                         },
738                         _ => ()
739                     }
740                 },
741                 _ => {
742                     let t = ty::expr_ty(cx.tcx, ex);
743                     match ty::get(t).sty {
744                         ty::ty_uint(_) => {
745                             cx.span_lint(UnsignedNegate, e.span,
746                                          "negation of unsigned int variable may be unintentional");
747                         },
748                         _ => ()
749                     }
750                 }
751             }
752         },
753         ast::ExprBinary(binop, l, r) => {
754             if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) {
755                 cx.span_lint(TypeLimits, e.span,
756                              "comparison is useless due to type limits");
757             }
758         },
759         ast::ExprLit(lit) => {
760             match ty::get(ty::expr_ty(cx.tcx, e)).sty {
761                 ty::ty_int(t) => {
762                     let int_type = if t == ast::TyI {
763                         cx.tcx.sess.targ_cfg.int_type
764                     } else { t };
765                     let (min, max) = int_ty_range(int_type);
766                     let mut lit_val: i64 = match lit.node {
767                         ast::LitInt(v, _) => v,
768                         ast::LitUint(v, _) => v as i64,
769                         ast::LitIntUnsuffixed(v) => v,
770                         _ => fail!()
771                     };
772                     if cx.negated_expr_id == e.id {
773                         lit_val *= -1;
774                     }
775                     if  lit_val < min || lit_val > max {
776                         cx.span_lint(TypeOverflow, e.span,
777                                      "literal out of range for its type");
778                     }
779                 },
780                 ty::ty_uint(t) => {
781                     let uint_type = if t == ast::TyU {
782                         cx.tcx.sess.targ_cfg.uint_type
783                     } else { t };
784                     let (min, max) = uint_ty_range(uint_type);
785                     let lit_val: u64 = match lit.node {
786                         ast::LitInt(v, _) => v as u64,
787                         ast::LitUint(v, _) => v,
788                         ast::LitIntUnsuffixed(v) => v as u64,
789                         _ => fail!()
790                     };
791                     if  lit_val < min || lit_val > max {
792                         cx.span_lint(TypeOverflow, e.span,
793                                      "literal out of range for its type");
794                     }
795                 },
796
797                 _ => ()
798             };
799         },
800         _ => ()
801     };
802
803     fn is_valid<T:cmp::Ord>(binop: ast::BinOp, v: T,
804                             min: T, max: T) -> bool {
805         match binop {
806             ast::BiLt => v >  min && v <= max,
807             ast::BiLe => v >= min && v <  max,
808             ast::BiGt => v >= min && v <  max,
809             ast::BiGe => v >  min && v <= max,
810             ast::BiEq | ast::BiNe => v >= min && v <= max,
811             _ => fail!()
812         }
813     }
814
815     fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
816         match binop {
817             ast::BiLt => ast::BiGt,
818             ast::BiLe => ast::BiGe,
819             ast::BiGt => ast::BiLt,
820             ast::BiGe => ast::BiLe,
821             _ => binop
822         }
823     }
824
825     // for int & uint, be conservative with the warnings, so that the
826     // warnings are consistent between 32- and 64-bit platforms
827     fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) {
828         match int_ty {
829             ast::TyI =>    (i64::MIN,        i64::MAX),
830             ast::TyI8 =>   (i8::MIN  as i64, i8::MAX  as i64),
831             ast::TyI16 =>  (i16::MIN as i64, i16::MAX as i64),
832             ast::TyI32 =>  (i32::MIN as i64, i32::MAX as i64),
833             ast::TyI64 =>  (i64::MIN,        i64::MAX)
834         }
835     }
836
837     fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
838         match uint_ty {
839             ast::TyU =>   (u64::MIN,         u64::MAX),
840             ast::TyU8 =>  (u8::MIN   as u64, u8::MAX   as u64),
841             ast::TyU16 => (u16::MIN  as u64, u16::MAX  as u64),
842             ast::TyU32 => (u32::MIN  as u64, u32::MAX  as u64),
843             ast::TyU64 => (u64::MIN,         u64::MAX)
844         }
845     }
846
847     fn check_limits(tcx: &ty::ctxt, binop: ast::BinOp,
848                     l: &ast::Expr, r: &ast::Expr) -> bool {
849         let (lit, expr, swap) = match (&l.node, &r.node) {
850             (&ast::ExprLit(_), _) => (l, r, true),
851             (_, &ast::ExprLit(_)) => (r, l, false),
852             _ => return true
853         };
854         // Normalize the binop so that the literal is always on the RHS in
855         // the comparison
856         let norm_binop = if swap { rev_binop(binop) } else { binop };
857         match ty::get(ty::expr_ty(tcx, expr)).sty {
858             ty::ty_int(int_ty) => {
859                 let (min, max) = int_ty_range(int_ty);
860                 let lit_val: i64 = match lit.node {
861                     ast::ExprLit(li) => match li.node {
862                         ast::LitInt(v, _) => v,
863                         ast::LitUint(v, _) => v as i64,
864                         ast::LitIntUnsuffixed(v) => v,
865                         _ => return true
866                     },
867                     _ => fail!()
868                 };
869                 is_valid(norm_binop, lit_val, min, max)
870             }
871             ty::ty_uint(uint_ty) => {
872                 let (min, max): (u64, u64) = uint_ty_range(uint_ty);
873                 let lit_val: u64 = match lit.node {
874                     ast::ExprLit(li) => match li.node {
875                         ast::LitInt(v, _) => v as u64,
876                         ast::LitUint(v, _) => v,
877                         ast::LitIntUnsuffixed(v) => v as u64,
878                         _ => return true
879                     },
880                     _ => fail!()
881                 };
882                 is_valid(norm_binop, lit_val, min, max)
883             }
884             _ => true
885         }
886     }
887
888     fn is_comparison(binop: ast::BinOp) -> bool {
889         match binop {
890             ast::BiEq | ast::BiLt | ast::BiLe |
891             ast::BiNe | ast::BiGe | ast::BiGt => true,
892             _ => false
893         }
894     }
895 }
896
897 fn check_item_ctypes(cx: &Context, it: &ast::Item) {
898     fn check_ty(cx: &Context, ty: &ast::Ty) {
899         match ty.node {
900             ast::TyPath(_, _, id) => {
901                 match cx.tcx.def_map.borrow().get_copy(&id) {
902                     ast::DefPrimTy(ast::TyInt(ast::TyI)) => {
903                         cx.span_lint(CTypes, ty.span,
904                                 "found rust type `int` in foreign module, while \
905                                 libc::c_int or libc::c_long should be used");
906                     }
907                     ast::DefPrimTy(ast::TyUint(ast::TyU)) => {
908                         cx.span_lint(CTypes, ty.span,
909                                 "found rust type `uint` in foreign module, while \
910                                 libc::c_uint or libc::c_ulong should be used");
911                     }
912                     ast::DefTy(def_id) => {
913                         if !adt::is_ffi_safe(cx.tcx, def_id) {
914                             cx.span_lint(CTypes, ty.span,
915                                          "found enum type without foreign-function-safe \
916                                           representation annotation in foreign module");
917                             // hmm... this message could be more helpful
918                         }
919                     }
920                     _ => ()
921                 }
922             }
923             ast::TyPtr(ref mt) => { check_ty(cx, mt.ty) }
924             _ => {}
925         }
926     }
927
928     fn check_foreign_fn(cx: &Context, decl: &ast::FnDecl) {
929         for input in decl.inputs.iter() {
930             check_ty(cx, input.ty);
931         }
932         check_ty(cx, decl.output)
933     }
934
935     match it.node {
936       ast::ItemForeignMod(ref nmod) if nmod.abi != abi::RustIntrinsic => {
937         for ni in nmod.items.iter() {
938             match ni.node {
939                 ast::ForeignItemFn(decl, _) => check_foreign_fn(cx, decl),
940                 ast::ForeignItemStatic(t, _) => check_ty(cx, t)
941             }
942         }
943       }
944       _ => {/* nothing to do */ }
945     }
946 }
947
948 fn check_heap_type(cx: &Context, span: Span, ty: ty::t) {
949     let xs = [ManagedHeapMemory, OwnedHeapMemory, HeapMemory];
950     for &lint in xs.iter() {
951         if cx.get_level(lint) == Allow { continue }
952
953         let mut n_box = 0;
954         let mut n_uniq = 0;
955         ty::fold_ty(cx.tcx, ty, |t| {
956             match ty::get(t).sty {
957                 ty::ty_box(_) => {
958                     n_box += 1;
959                 }
960                 ty::ty_uniq(_) |
961                 ty::ty_trait(box ty::TyTrait {
962                     store: ty::UniqTraitStore, ..
963                 }) |
964                 ty::ty_closure(box ty::ClosureTy {
965                     store: ty::UniqTraitStore,
966                     ..
967                 }) => {
968                     n_uniq += 1;
969                 }
970
971                 _ => ()
972             };
973             t
974         });
975
976         if n_uniq > 0 && lint != ManagedHeapMemory {
977             let s = ty_to_str(cx.tcx, ty);
978             let m = format!("type uses owned (Box type) pointers: {}", s);
979             cx.span_lint(lint, span, m.as_slice());
980         }
981
982         if n_box > 0 && lint != OwnedHeapMemory {
983             let s = ty_to_str(cx.tcx, ty);
984             let m = format!("type uses managed (@ type) pointers: {}", s);
985             cx.span_lint(lint, span, m.as_slice());
986         }
987     }
988 }
989
990 fn check_heap_item(cx: &Context, it: &ast::Item) {
991     match it.node {
992         ast::ItemFn(..) |
993         ast::ItemTy(..) |
994         ast::ItemEnum(..) |
995         ast::ItemStruct(..) => check_heap_type(cx, it.span,
996                                                ty::node_id_to_type(cx.tcx,
997                                                                    it.id)),
998         _ => ()
999     }
1000
1001     // If it's a struct, we also have to check the fields' types
1002     match it.node {
1003         ast::ItemStruct(struct_def, _) => {
1004             for struct_field in struct_def.fields.iter() {
1005                 check_heap_type(cx, struct_field.span,
1006                                 ty::node_id_to_type(cx.tcx,
1007                                                     struct_field.node.id));
1008             }
1009         }
1010         _ => ()
1011     }
1012 }
1013
1014 struct RawPtrDerivingVisitor<'a> {
1015     cx: &'a Context<'a>
1016 }
1017
1018 impl<'a> Visitor<()> for RawPtrDerivingVisitor<'a> {
1019     fn visit_ty(&mut self, ty: &ast::Ty, _: ()) {
1020         static MSG: &'static str = "use of `#[deriving]` with a raw pointer";
1021         match ty.node {
1022             ast::TyPtr(..) => self.cx.span_lint(RawPointerDeriving, ty.span, MSG),
1023             _ => {}
1024         }
1025         visit::walk_ty(self, ty, ());
1026     }
1027     // explicit override to a no-op to reduce code bloat
1028     fn visit_expr(&mut self, _: &ast::Expr, _: ()) {}
1029     fn visit_block(&mut self, _: &ast::Block, _: ()) {}
1030 }
1031
1032 fn check_raw_ptr_deriving(cx: &mut Context, item: &ast::Item) {
1033     if !attr::contains_name(item.attrs.as_slice(), "automatically_derived") {
1034         return
1035     }
1036     let did = match item.node {
1037         ast::ItemImpl(..) => {
1038             match ty::get(ty::node_id_to_type(cx.tcx, item.id)).sty {
1039                 ty::ty_enum(did, _) => did,
1040                 ty::ty_struct(did, _) => did,
1041                 _ => return,
1042             }
1043         }
1044         _ => return,
1045     };
1046     if !ast_util::is_local(did) { return }
1047     let item = match cx.tcx.map.find(did.node) {
1048         Some(ast_map::NodeItem(item)) => item,
1049         _ => return,
1050     };
1051     if !cx.checked_raw_pointers.insert(item.id) { return }
1052     match item.node {
1053         ast::ItemStruct(..) | ast::ItemEnum(..) => {
1054             let mut visitor = RawPtrDerivingVisitor { cx: cx };
1055             visit::walk_item(&mut visitor, item, ());
1056         }
1057         _ => {}
1058     }
1059 }
1060
1061 static crate_attrs: &'static [&'static str] = &[
1062     "crate_type", "feature", "no_start", "no_main", "no_std", "crate_id",
1063     "desc", "comment", "license", "copyright", // not used in rustc now
1064     "no_builtins",
1065 ];
1066
1067
1068 static obsolete_attrs: &'static [(&'static str, &'static str)] = &[
1069     ("abi", "Use `extern \"abi\" fn` instead"),
1070     ("auto_encode", "Use `#[deriving(Encodable)]` instead"),
1071     ("auto_decode", "Use `#[deriving(Decodable)]` instead"),
1072     ("fast_ffi", "Remove it"),
1073     ("fixed_stack_segment", "Remove it"),
1074     ("rust_stack", "Remove it"),
1075 ];
1076
1077 static other_attrs: &'static [&'static str] = &[
1078     // item-level
1079     "address_insignificant", // can be crate-level too
1080     "thread_local", // for statics
1081     "allow", "deny", "forbid", "warn", // lint options
1082     "deprecated", "experimental", "unstable", "stable", "locked", "frozen", //item stability
1083     "cfg", "doc", "export_name", "link_section",
1084     "no_mangle", "static_assert", "unsafe_no_drop_flag", "packed",
1085     "simd", "repr", "deriving", "unsafe_destructor", "link", "phase",
1086     "macro_export", "must_use", "automatically_derived",
1087
1088     //mod-level
1089     "path", "link_name", "link_args", "macro_escape", "no_implicit_prelude",
1090
1091     // fn-level
1092     "test", "bench", "should_fail", "ignore", "inline", "lang", "main", "start",
1093     "no_split_stack", "cold", "macro_registrar", "linkage",
1094
1095     // internal attribute: bypass privacy inside items
1096     "!resolve_unexported",
1097 ];
1098
1099 fn check_crate_attrs_usage(cx: &Context, attrs: &[ast::Attribute]) {
1100
1101     for attr in attrs.iter() {
1102         let name = attr.node.value.name();
1103         let mut iter = crate_attrs.iter().chain(other_attrs.iter());
1104         if !iter.any(|other_attr| { name.equiv(other_attr) }) {
1105             cx.span_lint(AttributeUsage, attr.span, "unknown crate attribute");
1106         }
1107         if name.equiv(&("link")) {
1108             cx.tcx.sess.span_err(attr.span,
1109                                  "obsolete crate `link` attribute");
1110             cx.tcx.sess.note("the link attribute has been superceded by the crate_id \
1111                              attribute, which has the format `#[crate_id = \"name#version\"]`");
1112         }
1113     }
1114 }
1115
1116 fn check_attrs_usage(cx: &Context, attrs: &[ast::Attribute]) {
1117     // check if element has crate-level, obsolete, or any unknown attributes.
1118
1119     for attr in attrs.iter() {
1120         let name = attr.node.value.name();
1121         for crate_attr in crate_attrs.iter() {
1122             if name.equiv(crate_attr) {
1123                 let msg = match attr.node.style {
1124                     ast::AttrOuter => "crate-level attribute should be an inner attribute: \
1125                                        add an exclamation mark: #![foo]",
1126                     ast::AttrInner => "crate-level attribute should be in the root module",
1127                 };
1128                 cx.span_lint(AttributeUsage, attr.span, msg);
1129                 return;
1130             }
1131         }
1132
1133         for &(obs_attr, obs_alter) in obsolete_attrs.iter() {
1134             if name.equiv(&obs_attr) {
1135                 cx.span_lint(AttributeUsage, attr.span,
1136                              format!("obsolete attribute: {:s}",
1137                                      obs_alter).as_slice());
1138                 return;
1139             }
1140         }
1141
1142         if !other_attrs.iter().any(|other_attr| { name.equiv(other_attr) }) {
1143             cx.span_lint(AttributeUsage, attr.span, "unknown attribute");
1144         }
1145     }
1146 }
1147
1148 fn check_unused_attribute(cx: &Context, attrs: &[ast::Attribute]) {
1149     for attr in attrs.iter() {
1150         // whitelist docs since rustdoc looks at them
1151         attr.check_name("automatically_derived");
1152         attr.check_name("doc");
1153
1154         // these are processed in trans, which happens after the lint pass
1155         attr.check_name("address_insignificant");
1156         attr.check_name("cold");
1157         attr.check_name("inline");
1158         attr.check_name("link");
1159         attr.check_name("link_name");
1160         attr.check_name("link_section");
1161         attr.check_name("no_builtins");
1162         attr.check_name("no_mangle");
1163         attr.check_name("no_split_stack");
1164         attr.check_name("packed");
1165         attr.check_name("static_assert");
1166         attr.check_name("thread_local");
1167
1168         // not used anywhere (!?) but apparently we want to keep them around
1169         attr.check_name("comment");
1170         attr.check_name("desc");
1171         attr.check_name("license");
1172
1173         // these are only looked at on-demand so we can't guarantee they'll have
1174         // already been checked
1175         attr.check_name("deprecated");
1176         attr.check_name("experimental");
1177         attr.check_name("frozen");
1178         attr.check_name("locked");
1179         attr.check_name("must_use");
1180         attr.check_name("stable");
1181         attr.check_name("unstable");
1182
1183         if !attr::is_used(attr) {
1184             cx.span_lint(UnusedAttribute, attr.span, "unused attribute");
1185         }
1186     }
1187 }
1188
1189 fn check_heap_expr(cx: &Context, e: &ast::Expr) {
1190     let ty = ty::expr_ty(cx.tcx, e);
1191     check_heap_type(cx, e.span, ty);
1192 }
1193
1194 fn check_path_statement(cx: &Context, s: &ast::Stmt) {
1195     match s.node {
1196         ast::StmtSemi(expr, _) => {
1197             match expr.node {
1198                 ast::ExprPath(_) => {
1199                     cx.span_lint(PathStatement,
1200                                  s.span,
1201                                  "path statement with no effect");
1202                 }
1203                 _ => {}
1204             }
1205         }
1206         _ => ()
1207     }
1208 }
1209
1210 fn check_unused_result(cx: &Context, s: &ast::Stmt) {
1211     let expr = match s.node {
1212         ast::StmtSemi(expr, _) => expr,
1213         _ => return
1214     };
1215     let t = ty::expr_ty(cx.tcx, expr);
1216     match ty::get(t).sty {
1217         ty::ty_nil | ty::ty_bot | ty::ty_bool => return,
1218         _ => {}
1219     }
1220     match expr.node {
1221         ast::ExprRet(..) => return,
1222         _ => {}
1223     }
1224
1225     let t = ty::expr_ty(cx.tcx, expr);
1226     let mut warned = false;
1227     match ty::get(t).sty {
1228         ty::ty_struct(did, _) |
1229         ty::ty_enum(did, _) => {
1230             if ast_util::is_local(did) {
1231                 match cx.tcx.map.get(did.node) {
1232                     ast_map::NodeItem(it) => {
1233                         if attr::contains_name(it.attrs.as_slice(),
1234                                                "must_use") {
1235                             cx.span_lint(UnusedMustUse, s.span,
1236                                          "unused result which must be used");
1237                             warned = true;
1238                         }
1239                     }
1240                     _ => {}
1241                 }
1242             } else {
1243                 csearch::get_item_attrs(&cx.tcx.sess.cstore, did, |attrs| {
1244                     if attr::contains_name(attrs.as_slice(), "must_use") {
1245                         cx.span_lint(UnusedMustUse, s.span,
1246                                      "unused result which must be used");
1247                         warned = true;
1248                     }
1249                 });
1250             }
1251         }
1252         _ => {}
1253     }
1254     if !warned {
1255         cx.span_lint(UnusedResult, s.span, "unused result");
1256     }
1257 }
1258
1259 fn check_deprecated_owned_vector(cx: &Context, e: &ast::Expr) {
1260     let t = ty::expr_ty(cx.tcx, e);
1261     match ty::get(t).sty {
1262         ty::ty_uniq(t) => match ty::get(t).sty {
1263             ty::ty_vec(_, None) => {
1264                 cx.span_lint(DeprecatedOwnedVector, e.span,
1265                              "use of deprecated `~[]` vector; replaced by `std::vec::Vec`")
1266             }
1267             _ => {}
1268         },
1269         _ => {}
1270     }
1271 }
1272
1273 fn check_item_non_camel_case_types(cx: &Context, it: &ast::Item) {
1274     fn is_camel_case(ident: ast::Ident) -> bool {
1275         let ident = token::get_ident(ident);
1276         assert!(!ident.get().is_empty());
1277         let ident = ident.get().trim_chars('_');
1278
1279         // start with a non-lowercase letter rather than non-uppercase
1280         // ones (some scripts don't have a concept of upper/lowercase)
1281         !ident.char_at(0).is_lowercase() && !ident.contains_char('_')
1282     }
1283
1284     fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
1285         if !is_camel_case(ident) {
1286             cx.span_lint(
1287                 NonCamelCaseTypes, span,
1288                 format!("{} `{}` should have a camel case identifier",
1289                     sort, token::get_ident(ident)).as_slice());
1290         }
1291     }
1292
1293     match it.node {
1294         ast::ItemTy(..) | ast::ItemStruct(..) => {
1295             check_case(cx, "type", it.ident, it.span)
1296         }
1297         ast::ItemTrait(..) => {
1298             check_case(cx, "trait", it.ident, it.span)
1299         }
1300         ast::ItemEnum(ref enum_definition, _) => {
1301             check_case(cx, "type", it.ident, it.span);
1302             for variant in enum_definition.variants.iter() {
1303                 check_case(cx, "variant", variant.node.name, variant.span);
1304             }
1305         }
1306         _ => ()
1307     }
1308 }
1309
1310 fn check_item_non_uppercase_statics(cx: &Context, it: &ast::Item) {
1311     match it.node {
1312         // only check static constants
1313         ast::ItemStatic(_, ast::MutImmutable, _) => {
1314             let s = token::get_ident(it.ident);
1315             // check for lowercase letters rather than non-uppercase
1316             // ones (some scripts don't have a concept of
1317             // upper/lowercase)
1318             if s.get().chars().any(|c| c.is_lowercase()) {
1319                 cx.span_lint(NonUppercaseStatics, it.span,
1320                              "static constant should have an uppercase identifier");
1321             }
1322         }
1323         _ => {}
1324     }
1325 }
1326
1327 fn check_pat_non_uppercase_statics(cx: &Context, p: &ast::Pat) {
1328     // Lint for constants that look like binding identifiers (#7526)
1329     match (&p.node, cx.tcx.def_map.borrow().find(&p.id)) {
1330         (&ast::PatIdent(_, ref path, _), Some(&ast::DefStatic(_, false))) => {
1331             // last identifier alone is right choice for this lint.
1332             let ident = path.segments.last().unwrap().identifier;
1333             let s = token::get_ident(ident);
1334             if s.get().chars().any(|c| c.is_lowercase()) {
1335                 cx.span_lint(NonUppercasePatternStatics, path.span,
1336                              "static constant in pattern should be all caps");
1337             }
1338         }
1339         _ => {}
1340     }
1341 }
1342
1343 fn check_pat_uppercase_variable(cx: &Context, p: &ast::Pat) {
1344     match &p.node {
1345         &ast::PatIdent(_, ref path, _) => {
1346             match cx.tcx.def_map.borrow().find(&p.id) {
1347                 Some(&ast::DefLocal(_, _)) | Some(&ast::DefBinding(_, _)) |
1348                         Some(&ast::DefArg(_, _)) => {
1349                     // last identifier alone is right choice for this lint.
1350                     let ident = path.segments.last().unwrap().identifier;
1351                     let s = token::get_ident(ident);
1352                     if s.get().len() > 0 && s.get().char_at(0).is_uppercase() {
1353                         cx.span_lint(
1354                             UppercaseVariables,
1355                             path.span,
1356                             "variable names should start with a lowercase character");
1357                     }
1358                 }
1359                 _ => {}
1360             }
1361         }
1362         _ => {}
1363     }
1364 }
1365
1366 fn check_struct_uppercase_variable(cx: &Context, s: &ast::StructDef) {
1367     for sf in s.fields.iter() {
1368         match sf.node {
1369             ast::StructField_ { kind: ast::NamedField(ident, _), .. } => {
1370                 let s = token::get_ident(ident);
1371                 if s.get().char_at(0).is_uppercase() {
1372                     cx.span_lint(
1373                         UppercaseVariables,
1374                         sf.span,
1375                         "structure field names should start with a lowercase character");
1376                 }
1377             }
1378             _ => {}
1379         }
1380     }
1381 }
1382
1383 fn check_unnecessary_parens_core(cx: &Context, value: &ast::Expr, msg: &str) {
1384     match value.node {
1385         ast::ExprParen(_) => {
1386             cx.span_lint(UnnecessaryParens, value.span,
1387                          format!("unnecessary parentheses around {}",
1388                                  msg).as_slice())
1389         }
1390         _ => {}
1391     }
1392 }
1393
1394 fn check_unnecessary_parens_expr(cx: &Context, e: &ast::Expr) {
1395     let (value, msg) = match e.node {
1396         ast::ExprIf(cond, _, _) => (cond, "`if` condition"),
1397         ast::ExprWhile(cond, _) => (cond, "`while` condition"),
1398         ast::ExprMatch(head, _) => (head, "`match` head expression"),
1399         ast::ExprRet(Some(value)) => (value, "`return` value"),
1400         ast::ExprAssign(_, value) => (value, "assigned value"),
1401         ast::ExprAssignOp(_, _, value) => (value, "assigned value"),
1402         _ => return
1403     };
1404     check_unnecessary_parens_core(cx, value, msg);
1405 }
1406
1407 fn check_unnecessary_parens_stmt(cx: &Context, s: &ast::Stmt) {
1408     let (value, msg) = match s.node {
1409         ast::StmtDecl(decl, _) => match decl.node {
1410             ast::DeclLocal(local) => match local.init {
1411                 Some(value) => (value, "assigned value"),
1412                 None => return
1413             },
1414             _ => return
1415         },
1416         _ => return
1417     };
1418     check_unnecessary_parens_core(cx, value, msg);
1419 }
1420
1421 fn check_unused_unsafe(cx: &Context, e: &ast::Expr) {
1422     match e.node {
1423         // Don't warn about generated blocks, that'll just pollute the output.
1424         ast::ExprBlock(ref blk) => {
1425             if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
1426                 !cx.tcx.used_unsafe.borrow().contains(&blk.id) {
1427                 cx.span_lint(UnusedUnsafe, blk.span,
1428                              "unnecessary `unsafe` block");
1429             }
1430         }
1431         _ => ()
1432     }
1433 }
1434
1435 fn check_unsafe_block(cx: &Context, e: &ast::Expr) {
1436     match e.node {
1437         // Don't warn about generated blocks, that'll just pollute the output.
1438         ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => {
1439             cx.span_lint(UnsafeBlock, blk.span, "usage of an `unsafe` block");
1440         }
1441         _ => ()
1442     }
1443 }
1444
1445 fn check_unused_mut_pat(cx: &Context, pats: &[@ast::Pat]) {
1446     // collect all mutable pattern and group their NodeIDs by their Identifier to
1447     // avoid false warnings in match arms with multiple patterns
1448     let mut mutables = HashMap::new();
1449     for &p in pats.iter() {
1450         pat_util::pat_bindings(&cx.tcx.def_map, p, |mode, id, _, path| {
1451             match mode {
1452                 ast::BindByValue(ast::MutMutable) => {
1453                     if path.segments.len() != 1 {
1454                         cx.tcx.sess.span_bug(p.span,
1455                                              "mutable binding that doesn't consist \
1456                                               of exactly one segment");
1457                     }
1458                     let ident = path.segments.get(0).identifier;
1459                     if !token::get_ident(ident).get().starts_with("_") {
1460                         mutables.insert_or_update_with(ident.name as uint, vec!(id), |_, old| {
1461                             old.push(id);
1462                         });
1463                     }
1464                 }
1465                 _ => {
1466                 }
1467             }
1468         });
1469     }
1470
1471     let used_mutables = cx.tcx.used_mut_nodes.borrow();
1472     for (_, v) in mutables.iter() {
1473         if !v.iter().any(|e| used_mutables.contains(e)) {
1474             cx.span_lint(UnusedMut, cx.tcx.map.span(*v.get(0)),
1475                          "variable does not need to be mutable");
1476         }
1477     }
1478 }
1479
1480 enum Allocation {
1481     VectorAllocation,
1482     BoxAllocation
1483 }
1484
1485 fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
1486     // Warn if string and vector literals with sigils, or boxing expressions,
1487     // are immediately borrowed.
1488     let allocation = match e.node {
1489         ast::ExprVstore(e2, ast::ExprVstoreUniq) => {
1490             match e2.node {
1491                 ast::ExprLit(lit) if ast_util::lit_is_str(lit) => {
1492                     VectorAllocation
1493                 }
1494                 ast::ExprVec(..) => VectorAllocation,
1495                 _ => return
1496             }
1497         }
1498         ast::ExprUnary(ast::UnUniq, _) |
1499         ast::ExprUnary(ast::UnBox, _) => BoxAllocation,
1500
1501         _ => return
1502     };
1503
1504     let report = |msg| {
1505         cx.span_lint(UnnecessaryAllocation, e.span, msg);
1506     };
1507
1508     match cx.tcx.adjustments.borrow().find(&e.id) {
1509         Some(adjustment) => {
1510             match *adjustment {
1511                 ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => {
1512                     match (allocation, autoref) {
1513                         (VectorAllocation, Some(ty::AutoBorrowVec(..))) => {
1514                             report("unnecessary allocation, the sigil can be \
1515                                     removed");
1516                         }
1517                         (BoxAllocation,
1518                          Some(ty::AutoPtr(_, ast::MutImmutable))) => {
1519                             report("unnecessary allocation, use & instead");
1520                         }
1521                         (BoxAllocation,
1522                          Some(ty::AutoPtr(_, ast::MutMutable))) => {
1523                             report("unnecessary allocation, use &mut \
1524                                     instead");
1525                         }
1526                         _ => ()
1527                     }
1528                 }
1529                 _ => {}
1530             }
1531         }
1532
1533         _ => ()
1534     }
1535 }
1536
1537 fn check_missing_doc_attrs(cx: &Context,
1538                            id: Option<ast::NodeId>,
1539                            attrs: &[ast::Attribute],
1540                            sp: Span,
1541                            desc: &'static str) {
1542     // If we're building a test harness, then warning about
1543     // documentation is probably not really relevant right now.
1544     if cx.tcx.sess.opts.test { return }
1545
1546     // `#[doc(hidden)]` disables missing_doc check.
1547     if cx.is_doc_hidden { return }
1548
1549     // Only check publicly-visible items, using the result from the privacy pass. It's an option so
1550     // the crate root can also use this function (it doesn't have a NodeId).
1551     match id {
1552         Some(ref id) if !cx.exported_items.contains(id) => return,
1553         _ => ()
1554     }
1555
1556     let has_doc = attrs.iter().any(|a| {
1557         match a.node.value.node {
1558             ast::MetaNameValue(ref name, _) if name.equiv(&("doc")) => true,
1559             _ => false
1560         }
1561     });
1562     if !has_doc {
1563         cx.span_lint(MissingDoc,
1564                      sp,
1565                      format!("missing documentation for {}",
1566                              desc).as_slice());
1567     }
1568 }
1569
1570 fn check_missing_doc_item(cx: &Context, it: &ast::Item) {
1571     let desc = match it.node {
1572         ast::ItemFn(..) => "a function",
1573         ast::ItemMod(..) => "a module",
1574         ast::ItemEnum(..) => "an enum",
1575         ast::ItemStruct(..) => "a struct",
1576         ast::ItemTrait(..) => "a trait",
1577         _ => return
1578     };
1579     check_missing_doc_attrs(cx,
1580                             Some(it.id),
1581                             it.attrs.as_slice(),
1582                             it.span,
1583                             desc);
1584 }
1585
1586 fn check_missing_doc_method(cx: &Context, m: &ast::Method) {
1587     let did = ast::DefId {
1588         krate: ast::LOCAL_CRATE,
1589         node: m.id
1590     };
1591
1592     match cx.tcx.methods.borrow().find_copy(&did) {
1593         None => cx.tcx.sess.span_bug(m.span, "missing method descriptor?!"),
1594         Some(md) => {
1595             match md.container {
1596                 // Always check default methods defined on traits.
1597                 ty::TraitContainer(..) => {}
1598                 // For methods defined on impls, it depends on whether
1599                 // it is an implementation for a trait or is a plain
1600                 // impl.
1601                 ty::ImplContainer(cid) => {
1602                     match ty::impl_trait_ref(cx.tcx, cid) {
1603                         Some(..) => return, // impl for trait: don't doc
1604                         None => {} // plain impl: doc according to privacy
1605                     }
1606                 }
1607             }
1608         }
1609     }
1610     check_missing_doc_attrs(cx,
1611                             Some(m.id),
1612                             m.attrs.as_slice(),
1613                             m.span,
1614                             "a method");
1615 }
1616
1617 fn check_missing_doc_ty_method(cx: &Context, tm: &ast::TypeMethod) {
1618     check_missing_doc_attrs(cx,
1619                             Some(tm.id),
1620                             tm.attrs.as_slice(),
1621                             tm.span,
1622                             "a type method");
1623 }
1624
1625 fn check_missing_doc_struct_field(cx: &Context, sf: &ast::StructField) {
1626     match sf.node.kind {
1627         ast::NamedField(_, vis) if vis == ast::Public =>
1628             check_missing_doc_attrs(cx,
1629                                     Some(cx.cur_struct_def_id),
1630                                     sf.node.attrs.as_slice(),
1631                                     sf.span,
1632                                     "a struct field"),
1633         _ => {}
1634     }
1635 }
1636
1637 fn check_missing_doc_variant(cx: &Context, v: &ast::Variant) {
1638     check_missing_doc_attrs(cx,
1639                             Some(v.node.id),
1640                             v.node.attrs.as_slice(),
1641                             v.span,
1642                             "a variant");
1643 }
1644
1645 /// Checks for use of items with #[deprecated], #[experimental] and
1646 /// #[unstable] (or none of them) attributes.
1647 fn check_stability(cx: &Context, e: &ast::Expr) {
1648     let id = match e.node {
1649         ast::ExprPath(..) | ast::ExprStruct(..) => {
1650             match cx.tcx.def_map.borrow().find(&e.id) {
1651                 Some(&def) => ast_util::def_id_of_def(def),
1652                 None => return
1653             }
1654         }
1655         ast::ExprMethodCall(..) => {
1656             let method_call = typeck::MethodCall::expr(e.id);
1657             match cx.tcx.method_map.borrow().find(&method_call) {
1658                 Some(method) => {
1659                     match method.origin {
1660                         typeck::MethodStatic(def_id) => {
1661                             // If this implements a trait method, get def_id
1662                             // of the method inside trait definition.
1663                             // Otherwise, use the current def_id (which refers
1664                             // to the method inside impl).
1665                             ty::trait_method_of_method(
1666                                 cx.tcx, def_id).unwrap_or(def_id)
1667                         }
1668                         typeck::MethodParam(typeck::MethodParam {
1669                             trait_id: trait_id,
1670                             method_num: index,
1671                             ..
1672                         })
1673                         | typeck::MethodObject(typeck::MethodObject {
1674                             trait_id: trait_id,
1675                             method_num: index,
1676                             ..
1677                         }) => ty::trait_method(cx.tcx, trait_id, index).def_id
1678                     }
1679                 }
1680                 None => return
1681             }
1682         }
1683         _ => return
1684     };
1685
1686     let stability = if ast_util::is_local(id) {
1687         // this crate
1688         let s = cx.tcx.map.with_attrs(id.node, |attrs| {
1689             attrs.map(|a| attr::find_stability(a.as_slice()))
1690         });
1691         match s {
1692             Some(s) => s,
1693
1694             // no possibility of having attributes
1695             // (e.g. it's a local variable), so just
1696             // ignore it.
1697             None => return
1698         }
1699     } else {
1700         // cross-crate
1701
1702         let mut s = None;
1703         // run through all the attributes and take the first
1704         // stability one.
1705         csearch::get_item_attrs(&cx.tcx.sess.cstore, id, |attrs| {
1706             if s.is_none() {
1707                 s = attr::find_stability(attrs.as_slice())
1708             }
1709         });
1710         s
1711     };
1712
1713     let (lint, label) = match stability {
1714         // no stability attributes == Unstable
1715         None => (Unstable, "unmarked"),
1716         Some(attr::Stability { level: attr::Unstable, .. }) =>
1717                 (Unstable, "unstable"),
1718         Some(attr::Stability { level: attr::Experimental, .. }) =>
1719                 (Experimental, "experimental"),
1720         Some(attr::Stability { level: attr::Deprecated, .. }) =>
1721                 (Deprecated, "deprecated"),
1722         _ => return
1723     };
1724
1725     let msg = match stability {
1726         Some(attr::Stability { text: Some(ref s), .. }) => {
1727             format!("use of {} item: {}", label, *s)
1728         }
1729         _ => format!("use of {} item", label)
1730     };
1731
1732     cx.span_lint(lint, e.span, msg.as_slice());
1733 }
1734
1735 impl<'a> Visitor<()> for Context<'a> {
1736     fn visit_item(&mut self, it: &ast::Item, _: ()) {
1737         self.with_lint_attrs(it.attrs.as_slice(), |cx| {
1738             check_item_ctypes(cx, it);
1739             check_item_non_camel_case_types(cx, it);
1740             check_item_non_uppercase_statics(cx, it);
1741             check_heap_item(cx, it);
1742             check_missing_doc_item(cx, it);
1743             check_attrs_usage(cx, it.attrs.as_slice());
1744             check_unused_attribute(cx, it.attrs.as_slice());
1745             check_raw_ptr_deriving(cx, it);
1746
1747             cx.visit_ids(|v| v.visit_item(it, ()));
1748
1749             visit::walk_item(cx, it, ());
1750         })
1751     }
1752
1753     fn visit_foreign_item(&mut self, it: &ast::ForeignItem, _: ()) {
1754         self.with_lint_attrs(it.attrs.as_slice(), |cx| {
1755             check_attrs_usage(cx, it.attrs.as_slice());
1756             visit::walk_foreign_item(cx, it, ());
1757         })
1758     }
1759
1760     fn visit_view_item(&mut self, i: &ast::ViewItem, _: ()) {
1761         self.with_lint_attrs(i.attrs.as_slice(), |cx| {
1762             check_attrs_usage(cx, i.attrs.as_slice());
1763
1764             cx.visit_ids(|v| v.visit_view_item(i, ()));
1765
1766             visit::walk_view_item(cx, i, ());
1767         })
1768     }
1769
1770     fn visit_pat(&mut self, p: &ast::Pat, _: ()) {
1771         check_pat_non_uppercase_statics(self, p);
1772         check_pat_uppercase_variable(self, p);
1773
1774         visit::walk_pat(self, p, ());
1775     }
1776
1777     fn visit_expr(&mut self, e: &ast::Expr, _: ()) {
1778         match e.node {
1779             ast::ExprUnary(ast::UnNeg, expr) => {
1780                 // propagate negation, if the negation itself isn't negated
1781                 if self.negated_expr_id != e.id {
1782                     self.negated_expr_id = expr.id;
1783                 }
1784             },
1785             ast::ExprParen(expr) => if self.negated_expr_id == e.id {
1786                 self.negated_expr_id = expr.id
1787             },
1788             ast::ExprMatch(_, ref arms) => {
1789                 for a in arms.iter() {
1790                     check_unused_mut_pat(self, a.pats.as_slice());
1791                 }
1792             },
1793             _ => ()
1794         };
1795
1796         check_while_true_expr(self, e);
1797         check_stability(self, e);
1798         check_unnecessary_parens_expr(self, e);
1799         check_unused_unsafe(self, e);
1800         check_unsafe_block(self, e);
1801         check_unnecessary_allocation(self, e);
1802         check_heap_expr(self, e);
1803
1804         check_type_limits(self, e);
1805         check_unused_casts(self, e);
1806         check_deprecated_owned_vector(self, e);
1807
1808         visit::walk_expr(self, e, ());
1809     }
1810
1811     fn visit_stmt(&mut self, s: &ast::Stmt, _: ()) {
1812         check_path_statement(self, s);
1813         check_unused_result(self, s);
1814         check_unnecessary_parens_stmt(self, s);
1815
1816         match s.node {
1817             ast::StmtDecl(d, _) => {
1818                 match d.node {
1819                     ast::DeclLocal(l) => {
1820                         check_unused_mut_pat(self, &[l.pat]);
1821                     },
1822                     _ => {}
1823                 }
1824             },
1825             _ => {}
1826         }
1827
1828         visit::walk_stmt(self, s, ());
1829     }
1830
1831     fn visit_fn(&mut self, fk: &visit::FnKind, decl: &ast::FnDecl,
1832                 body: &ast::Block, span: Span, id: ast::NodeId, _: ()) {
1833         let recurse = |this: &mut Context| {
1834             visit::walk_fn(this, fk, decl, body, span, ());
1835         };
1836
1837         for a in decl.inputs.iter(){
1838             check_unused_mut_pat(self, &[a.pat]);
1839         }
1840
1841         match *fk {
1842             visit::FkMethod(_, _, m) => {
1843                 self.with_lint_attrs(m.attrs.as_slice(), |cx| {
1844                     check_missing_doc_method(cx, m);
1845                     check_attrs_usage(cx, m.attrs.as_slice());
1846
1847                     cx.visit_ids(|v| {
1848                         v.visit_fn(fk, decl, body, span, id, ());
1849                     });
1850                     recurse(cx);
1851                 })
1852             }
1853             _ => recurse(self),
1854         }
1855     }
1856
1857
1858     fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) {
1859         self.with_lint_attrs(t.attrs.as_slice(), |cx| {
1860             check_missing_doc_ty_method(cx, t);
1861             check_attrs_usage(cx, t.attrs.as_slice());
1862
1863             visit::walk_ty_method(cx, t, ());
1864         })
1865     }
1866
1867     fn visit_struct_def(&mut self,
1868                         s: &ast::StructDef,
1869                         _: ast::Ident,
1870                         _: &ast::Generics,
1871                         id: ast::NodeId,
1872                         _: ()) {
1873         check_struct_uppercase_variable(self, s);
1874
1875         let old_id = self.cur_struct_def_id;
1876         self.cur_struct_def_id = id;
1877         visit::walk_struct_def(self, s, ());
1878         self.cur_struct_def_id = old_id;
1879     }
1880
1881     fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) {
1882         self.with_lint_attrs(s.node.attrs.as_slice(), |cx| {
1883             check_missing_doc_struct_field(cx, s);
1884             check_attrs_usage(cx, s.node.attrs.as_slice());
1885
1886             visit::walk_struct_field(cx, s, ());
1887         })
1888     }
1889
1890     fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) {
1891         self.with_lint_attrs(v.node.attrs.as_slice(), |cx| {
1892             check_missing_doc_variant(cx, v);
1893             check_attrs_usage(cx, v.node.attrs.as_slice());
1894
1895             visit::walk_variant(cx, v, g, ());
1896         })
1897     }
1898
1899     // FIXME(#10894) should continue recursing
1900     fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {}
1901 }
1902
1903 impl<'a> IdVisitingOperation for Context<'a> {
1904     fn visit_id(&self, id: ast::NodeId) {
1905         match self.tcx.sess.lints.borrow_mut().pop(&id) {
1906             None => {}
1907             Some(l) => {
1908                 for (lint, span, msg) in l.move_iter() {
1909                     self.span_lint(lint, span, msg.as_slice())
1910                 }
1911             }
1912         }
1913     }
1914 }
1915
1916 pub fn check_crate(tcx: &ty::ctxt,
1917                    exported_items: &privacy::ExportedItems,
1918                    krate: &ast::Crate) {
1919     let mut cx = Context {
1920         dict: get_lint_dict(),
1921         cur: SmallIntMap::new(),
1922         tcx: tcx,
1923         exported_items: exported_items,
1924         cur_struct_def_id: -1,
1925         is_doc_hidden: false,
1926         lint_stack: Vec::new(),
1927         negated_expr_id: -1,
1928         checked_raw_pointers: NodeSet::new(),
1929     };
1930
1931     // Install default lint levels, followed by the command line levels, and
1932     // then actually visit the whole crate.
1933     for (_, spec) in cx.dict.iter() {
1934         if spec.default != Allow {
1935             cx.cur.insert(spec.lint as uint, (spec.default, Default));
1936         }
1937     }
1938     for &(lint, level) in tcx.sess.opts.lint_opts.iter() {
1939         cx.set_level(lint, level, CommandLine);
1940     }
1941     cx.with_lint_attrs(krate.attrs.as_slice(), |cx| {
1942         cx.visit_id(ast::CRATE_NODE_ID);
1943         cx.visit_ids(|v| {
1944             v.visited_outermost = true;
1945             visit::walk_crate(v, krate, ());
1946         });
1947
1948         check_crate_attrs_usage(cx, krate.attrs.as_slice());
1949         // since the root module isn't visited as an item (because it isn't an item), warn for it
1950         // here.
1951         check_unused_attribute(cx, krate.attrs.as_slice());
1952         check_missing_doc_attrs(cx,
1953                                 None,
1954                                 krate.attrs.as_slice(),
1955                                 krate.span,
1956                                 "crate");
1957
1958         visit::walk_crate(cx, krate, ());
1959     });
1960
1961     // If we missed any lints added to the session, then there's a bug somewhere
1962     // in the iteration code.
1963     for (id, v) in tcx.sess.lints.borrow().iter() {
1964         for &(lint, span, ref msg) in v.iter() {
1965             tcx.sess.span_bug(span,
1966                               format!("unprocessed lint {:?} at {}: {}",
1967                                       lint,
1968                                       tcx.map.node_to_str(*id),
1969                                       *msg).as_slice())
1970         }
1971     }
1972
1973     tcx.sess.abort_if_errors();
1974 }