]> git.lizzy.rs Git - rust.git/blob - src/librustc/lint/mod.rs
Move lint infrastructure and individual lints into separate files
[rust.git] / src / librustc / lint / mod.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 middle::dead::DEAD_CODE_LINT_STR;
40 use middle::privacy;
41 use middle::ty;
42 use middle::typeck::astconv::AstConv;
43 use middle::typeck::infer;
44 use util::nodemap::NodeSet;
45
46 use std::collections::HashMap;
47 use std::rc::Rc;
48 use std::gc::Gc;
49 use std::to_str::ToStr;
50 use std::collections::SmallIntMap;
51 use syntax::ast_util::IdVisitingOperation;
52 use syntax::attr::AttrMetaMethods;
53 use syntax::attr;
54 use syntax::codemap::Span;
55 use syntax::parse::token::InternedString;
56 use syntax::visit::Visitor;
57 use syntax::{ast, ast_util, visit};
58
59 mod builtin;
60
61 #[deriving(Clone, Show, PartialEq, PartialOrd, Eq, Ord, Hash)]
62 pub enum Lint {
63     CTypes,
64     UnusedImports,
65     UnnecessaryQualification,
66     WhileTrue,
67     PathStatement,
68     UnrecognizedLint,
69     NonCamelCaseTypes,
70     NonUppercaseStatics,
71     NonUppercasePatternStatics,
72     NonSnakeCaseFunctions,
73     UppercaseVariables,
74     UnnecessaryParens,
75     TypeLimits,
76     TypeOverflow,
77     UnusedUnsafe,
78     UnsafeBlock,
79     UnusedAttribute,
80     UnknownFeatures,
81     UnknownCrateType,
82     UnsignedNegate,
83     VariantSizeDifference,
84
85     ManagedHeapMemory,
86     OwnedHeapMemory,
87     HeapMemory,
88
89     UnusedVariable,
90     DeadAssignment,
91     UnusedMut,
92     UnnecessaryAllocation,
93     DeadCode,
94     VisiblePrivateTypes,
95     UnnecessaryTypecast,
96
97     MissingDoc,
98     UnreachableCode,
99
100     Deprecated,
101     Experimental,
102     Unstable,
103
104     UnusedMustUse,
105     UnusedResult,
106
107     Warnings,
108
109     RawPointerDeriving,
110 }
111
112 pub fn level_to_str(lv: Level) -> &'static str {
113     match lv {
114       Allow => "allow",
115       Warn => "warn",
116       Deny => "deny",
117       Forbid => "forbid"
118     }
119 }
120
121 #[deriving(Clone, PartialEq, PartialOrd, Eq, Ord)]
122 pub enum Level {
123     Allow, Warn, Deny, Forbid
124 }
125
126 #[deriving(Clone, PartialEq, PartialOrd, Eq, Ord)]
127 pub struct LintSpec {
128     pub default: Level,
129     pub lint: Lint,
130     pub desc: &'static str,
131 }
132
133 pub type LintDict = HashMap<&'static str, LintSpec>;
134
135 // this is public for the lints that run in trans
136 #[deriving(PartialEq)]
137 pub enum LintSource {
138     Node(Span),
139     Default,
140     CommandLine
141 }
142
143 static lint_table: &'static [(&'static str, LintSpec)] = &[
144     ("ctypes",
145      LintSpec {
146         lint: CTypes,
147         desc: "proper use of libc types in foreign modules",
148         default: Warn
149      }),
150
151     ("unused_imports",
152      LintSpec {
153         lint: UnusedImports,
154         desc: "imports that are never used",
155         default: Warn
156      }),
157
158     ("unnecessary_qualification",
159      LintSpec {
160         lint: UnnecessaryQualification,
161         desc: "detects unnecessarily qualified names",
162         default: Allow
163      }),
164
165     ("while_true",
166      LintSpec {
167         lint: WhileTrue,
168         desc: "suggest using `loop { }` instead of `while true { }`",
169         default: Warn
170      }),
171
172     ("path_statement",
173      LintSpec {
174         lint: PathStatement,
175         desc: "path statements with no effect",
176         default: Warn
177      }),
178
179     ("unrecognized_lint",
180      LintSpec {
181         lint: UnrecognizedLint,
182         desc: "unrecognized lint attribute",
183         default: Warn
184      }),
185
186     ("non_camel_case_types",
187      LintSpec {
188         lint: NonCamelCaseTypes,
189         desc: "types, variants and traits should have camel case names",
190         default: Warn
191      }),
192
193     ("non_uppercase_statics",
194      LintSpec {
195          lint: NonUppercaseStatics,
196          desc: "static constants should have uppercase identifiers",
197          default: Allow
198      }),
199
200     ("non_uppercase_pattern_statics",
201      LintSpec {
202          lint: NonUppercasePatternStatics,
203          desc: "static constants in match patterns should be all caps",
204          default: Warn
205      }),
206
207     ("non_snake_case_functions",
208      LintSpec {
209          lint: NonSnakeCaseFunctions,
210          desc: "methods and functions should have snake case names",
211          default: Warn
212      }),
213
214     ("uppercase_variables",
215      LintSpec {
216          lint: UppercaseVariables,
217          desc: "variable and structure field names should start with a lowercase character",
218          default: Warn
219      }),
220
221      ("unnecessary_parens",
222      LintSpec {
223         lint: UnnecessaryParens,
224         desc: "`if`, `match`, `while` and `return` do not need parentheses",
225         default: Warn
226      }),
227
228     ("managed_heap_memory",
229      LintSpec {
230         lint: ManagedHeapMemory,
231         desc: "use of managed (@ type) heap memory",
232         default: Allow
233      }),
234
235     ("owned_heap_memory",
236      LintSpec {
237         lint: OwnedHeapMemory,
238         desc: "use of owned (Box type) heap memory",
239         default: Allow
240      }),
241
242     ("heap_memory",
243      LintSpec {
244         lint: HeapMemory,
245         desc: "use of any (Box type or @ type) heap memory",
246         default: Allow
247      }),
248
249     ("type_limits",
250      LintSpec {
251         lint: TypeLimits,
252         desc: "comparisons made useless by limits of the types involved",
253         default: Warn
254      }),
255
256     ("type_overflow",
257      LintSpec {
258         lint: TypeOverflow,
259         desc: "literal out of range for its type",
260         default: Warn
261      }),
262
263
264     ("unused_unsafe",
265      LintSpec {
266         lint: UnusedUnsafe,
267         desc: "unnecessary use of an `unsafe` block",
268         default: Warn
269     }),
270
271     ("unsafe_block",
272      LintSpec {
273         lint: UnsafeBlock,
274         desc: "usage of an `unsafe` block",
275         default: Allow
276     }),
277
278     ("unused_attribute",
279      LintSpec {
280          lint: UnusedAttribute,
281          desc: "detects attributes that were not used by the compiler",
282          default: Warn
283     }),
284
285     ("unused_variable",
286      LintSpec {
287         lint: UnusedVariable,
288         desc: "detect variables which are not used in any way",
289         default: Warn
290     }),
291
292     ("dead_assignment",
293      LintSpec {
294         lint: DeadAssignment,
295         desc: "detect assignments that will never be read",
296         default: Warn
297     }),
298
299     ("unnecessary_typecast",
300      LintSpec {
301         lint: UnnecessaryTypecast,
302         desc: "detects unnecessary type casts, that can be removed",
303         default: Allow,
304     }),
305
306     ("unused_mut",
307      LintSpec {
308         lint: UnusedMut,
309         desc: "detect mut variables which don't need to be mutable",
310         default: Warn
311     }),
312
313     ("unnecessary_allocation",
314      LintSpec {
315         lint: UnnecessaryAllocation,
316         desc: "detects unnecessary allocations that can be eliminated",
317         default: Warn
318     }),
319
320     (DEAD_CODE_LINT_STR,
321      LintSpec {
322         lint: DeadCode,
323         desc: "detect piece of code that will never be used",
324         default: Warn
325     }),
326     ("visible_private_types",
327      LintSpec {
328         lint: VisiblePrivateTypes,
329         desc: "detect use of private types in exported type signatures",
330         default: Warn
331     }),
332
333     ("missing_doc",
334      LintSpec {
335         lint: MissingDoc,
336         desc: "detects missing documentation for public members",
337         default: Allow
338     }),
339
340     ("unreachable_code",
341      LintSpec {
342         lint: UnreachableCode,
343         desc: "detects unreachable code",
344         default: Warn
345     }),
346
347     ("deprecated",
348      LintSpec {
349         lint: Deprecated,
350         desc: "detects use of #[deprecated] items",
351         default: Warn
352     }),
353
354     ("experimental",
355      LintSpec {
356         lint: Experimental,
357         desc: "detects use of #[experimental] items",
358         // FIXME #6875: Change to Warn after std library stabilization is complete
359         default: Allow
360     }),
361
362     ("unstable",
363      LintSpec {
364         lint: Unstable,
365         desc: "detects use of #[unstable] items (incl. items with no stability attribute)",
366         default: Allow
367     }),
368
369     ("warnings",
370      LintSpec {
371         lint: Warnings,
372         desc: "mass-change the level for lints which produce warnings",
373         default: Warn
374     }),
375
376     ("unknown_features",
377      LintSpec {
378         lint: UnknownFeatures,
379         desc: "unknown features found in crate-level #[feature] directives",
380         default: Deny,
381     }),
382
383     ("unknown_crate_type",
384     LintSpec {
385         lint: UnknownCrateType,
386         desc: "unknown crate type found in #[crate_type] directive",
387         default: Deny,
388     }),
389
390     ("unsigned_negate",
391     LintSpec {
392         lint: UnsignedNegate,
393         desc: "using an unary minus operator on unsigned type",
394         default: Warn
395     }),
396
397     ("variant_size_difference",
398     LintSpec {
399         lint: VariantSizeDifference,
400         desc: "detects enums with widely varying variant sizes",
401         default: Allow,
402     }),
403
404     ("unused_must_use",
405     LintSpec {
406         lint: UnusedMustUse,
407         desc: "unused result of a type flagged as #[must_use]",
408         default: Warn,
409     }),
410
411     ("unused_result",
412     LintSpec {
413         lint: UnusedResult,
414         desc: "unused result of an expression in a statement",
415         default: Allow,
416     }),
417
418     ("raw_pointer_deriving",
419      LintSpec {
420         lint: RawPointerDeriving,
421         desc: "uses of #[deriving] with raw pointers are rarely correct",
422         default: Warn,
423     }),
424 ];
425
426 /*
427   Pass names should not contain a '-', as the compiler normalizes
428   '-' to '_' in command-line flags
429  */
430 pub fn get_lint_dict() -> LintDict {
431     lint_table.iter().map(|&(k, v)| (k, v)).collect()
432 }
433
434 struct Context<'a> {
435     /// All known lint modes (string versions)
436     dict: LintDict,
437     /// Current levels of each lint warning
438     cur: SmallIntMap<(Level, LintSource)>,
439     /// Context we're checking in (used to access fields like sess)
440     tcx: &'a ty::ctxt,
441     /// Items exported by the crate; used by the missing_doc lint.
442     exported_items: &'a privacy::ExportedItems,
443     /// The id of the current `ast::StructDef` being walked.
444     cur_struct_def_id: ast::NodeId,
445     /// Whether some ancestor of the current node was marked
446     /// #[doc(hidden)].
447     is_doc_hidden: bool,
448
449     /// When recursing into an attributed node of the ast which modifies lint
450     /// levels, this stack keeps track of the previous lint levels of whatever
451     /// was modified.
452     lint_stack: Vec<(Lint, Level, LintSource)>,
453
454     /// Id of the last visited negated expression
455     negated_expr_id: ast::NodeId,
456
457     /// Ids of structs/enums which have been checked for raw_pointer_deriving
458     checked_raw_pointers: NodeSet,
459
460     /// Level of lints for certain NodeIds, stored here because the body of
461     /// the lint needs to run in trans.
462     node_levels: HashMap<(ast::NodeId, Lint), (Level, LintSource)>,
463 }
464
465 pub fn emit_lint(level: Level, src: LintSource, msg: &str, span: Span,
466                  lint_str: &str, tcx: &ty::ctxt) {
467     if level == Allow { return }
468
469     let mut note = None;
470     let msg = match src {
471         Default => {
472             format!("{}, #[{}({})] on by default", msg,
473                 level_to_str(level), lint_str)
474         },
475         CommandLine => {
476             format!("{} [-{} {}]", msg,
477                 match level {
478                     Warn => 'W', Deny => 'D', Forbid => 'F',
479                     Allow => fail!()
480                 }, lint_str.replace("_", "-"))
481         },
482         Node(src) => {
483             note = Some(src);
484             msg.to_str()
485         }
486     };
487
488     match level {
489         Warn =>          { tcx.sess.span_warn(span, msg.as_slice()); }
490         Deny | Forbid => { tcx.sess.span_err(span, msg.as_slice());  }
491         Allow => fail!(),
492     }
493
494     for &span in note.iter() {
495         tcx.sess.span_note(span, "lint level defined here");
496     }
497 }
498
499 pub fn lint_to_str(lint: Lint) -> &'static str {
500     for &(name, lspec) in lint_table.iter() {
501         if lspec.lint == lint {
502             return name;
503         }
504     }
505
506     fail!("unrecognized lint: {}", lint);
507 }
508
509 impl<'a> Context<'a> {
510     fn get_level(&self, lint: Lint) -> Level {
511         match self.cur.find(&(lint as uint)) {
512           Some(&(lvl, _)) => lvl,
513           None => Allow
514         }
515     }
516
517     fn get_source(&self, lint: Lint) -> LintSource {
518         match self.cur.find(&(lint as uint)) {
519           Some(&(_, src)) => src,
520           None => Default
521         }
522     }
523
524     fn set_level(&mut self, lint: Lint, level: Level, src: LintSource) {
525         if level == Allow {
526             self.cur.remove(&(lint as uint));
527         } else {
528             self.cur.insert(lint as uint, (level, src));
529         }
530     }
531
532     fn lint_to_str(&self, lint: Lint) -> &'static str {
533         for (k, v) in self.dict.iter() {
534             if v.lint == lint {
535                 return *k;
536             }
537         }
538         fail!("unregistered lint {}", lint);
539     }
540
541     fn span_lint(&self, lint: Lint, span: Span, msg: &str) {
542         let (level, src) = match self.cur.find(&(lint as uint)) {
543             None => { return }
544             Some(&(Warn, src)) => (self.get_level(Warnings), src),
545             Some(&pair) => pair,
546         };
547
548         emit_lint(level, src, msg, span, self.lint_to_str(lint), self.tcx);
549     }
550
551     /**
552      * Merge the lints specified by any lint attributes into the
553      * current lint context, call the provided function, then reset the
554      * lints in effect to their previous state.
555      */
556     fn with_lint_attrs(&mut self,
557                        attrs: &[ast::Attribute],
558                        f: |&mut Context|) {
559         // Parse all of the lint attributes, and then add them all to the
560         // current dictionary of lint information. Along the way, keep a history
561         // of what we changed so we can roll everything back after invoking the
562         // specified closure
563         let mut pushed = 0u;
564         each_lint(&self.tcx.sess, attrs, |meta, level, lintname| {
565             match self.dict.find_equiv(&lintname) {
566                 None => {
567                     self.span_lint(
568                         UnrecognizedLint,
569                         meta.span,
570                         format!("unknown `{}` attribute: `{}`",
571                                 level_to_str(level), lintname).as_slice());
572                 }
573                 Some(lint) => {
574                     let lint = lint.lint;
575                     let now = self.get_level(lint);
576                     if now == Forbid && level != Forbid {
577                         self.tcx.sess.span_err(meta.span,
578                         format!("{}({}) overruled by outer forbid({})",
579                                 level_to_str(level),
580                                 lintname,
581                                 lintname).as_slice());
582                     } else if now != level {
583                         let src = self.get_source(lint);
584                         self.lint_stack.push((lint, now, src));
585                         pushed += 1;
586                         self.set_level(lint, level, Node(meta.span));
587                     }
588                 }
589             }
590             true
591         });
592
593         let old_is_doc_hidden = self.is_doc_hidden;
594         self.is_doc_hidden =
595             self.is_doc_hidden ||
596             attrs.iter()
597                  .any(|attr| {
598                      attr.name().equiv(&("doc")) &&
599                      match attr.meta_item_list() {
600                          None => false,
601                          Some(l) => {
602                              attr::contains_name(l.as_slice(), "hidden")
603                          }
604                      }
605                  });
606
607         f(self);
608
609         // rollback
610         self.is_doc_hidden = old_is_doc_hidden;
611         for _ in range(0, pushed) {
612             let (lint, lvl, src) = self.lint_stack.pop().unwrap();
613             self.set_level(lint, lvl, src);
614         }
615     }
616
617     fn visit_ids(&self, f: |&mut ast_util::IdVisitor<Context>|) {
618         let mut v = ast_util::IdVisitor {
619             operation: self,
620             pass_through_items: false,
621             visited_outermost: false,
622         };
623         f(&mut v);
624     }
625 }
626
627 /// Check that every lint from the list of attributes satisfies `f`.
628 /// Return true if that's the case. Otherwise return false.
629 pub fn each_lint(sess: &session::Session,
630                  attrs: &[ast::Attribute],
631                  f: |Gc<ast::MetaItem>, Level, InternedString| -> bool)
632                  -> bool {
633     let xs = [Allow, Warn, Deny, Forbid];
634     for &level in xs.iter() {
635         let level_name = level_to_str(level);
636         for attr in attrs.iter().filter(|m| m.check_name(level_name)) {
637             let meta = attr.node.value;
638             let metas = match meta.node {
639                 ast::MetaList(_, ref metas) => metas,
640                 _ => {
641                     sess.span_err(meta.span, "malformed lint attribute");
642                     continue;
643                 }
644             };
645             for meta in metas.iter() {
646                 match meta.node {
647                     ast::MetaWord(ref lintname) => {
648                         if !f(*meta, level, (*lintname).clone()) {
649                             return false;
650                         }
651                     }
652                     _ => {
653                         sess.span_err(meta.span, "malformed lint attribute");
654                     }
655                 }
656             }
657         }
658     }
659     true
660 }
661
662 /// Check from a list of attributes if it contains the appropriate
663 /// `#[level(lintname)]` attribute (e.g. `#[allow(dead_code)]).
664 pub fn contains_lint(attrs: &[ast::Attribute],
665                      level: Level,
666                      lintname: &'static str)
667                      -> bool {
668     let level_name = level_to_str(level);
669     for attr in attrs.iter().filter(|m| m.name().equiv(&level_name)) {
670         if attr.meta_item_list().is_none() {
671             continue
672         }
673         let list = attr.meta_item_list().unwrap();
674         for meta_item in list.iter() {
675             if meta_item.name().equiv(&lintname) {
676                 return true;
677             }
678         }
679     }
680     false
681 }
682
683 #[deriving(PartialEq)]
684 enum MethodContext {
685     TraitDefaultImpl,
686     TraitImpl,
687     PlainImpl
688 }
689
690 fn method_context(cx: &Context, m: &ast::Method) -> MethodContext {
691     let did = ast::DefId {
692         krate: ast::LOCAL_CRATE,
693         node: m.id
694     };
695
696     match cx.tcx.methods.borrow().find_copy(&did) {
697         None => cx.tcx.sess.span_bug(m.span, "missing method descriptor?!"),
698         Some(md) => {
699             match md.container {
700                 ty::TraitContainer(..) => TraitDefaultImpl,
701                 ty::ImplContainer(cid) => {
702                     match ty::impl_trait_ref(cx.tcx, cid) {
703                         Some(..) => TraitImpl,
704                         None => PlainImpl
705                     }
706                 }
707             }
708         }
709     }
710 }
711
712 impl<'a> AstConv for Context<'a>{
713     fn tcx<'a>(&'a self) -> &'a ty::ctxt { self.tcx }
714
715     fn get_item_ty(&self, id: ast::DefId) -> ty::ty_param_bounds_and_ty {
716         ty::lookup_item_type(self.tcx, id)
717     }
718
719     fn get_trait_def(&self, id: ast::DefId) -> Rc<ty::TraitDef> {
720         ty::lookup_trait_def(self.tcx, id)
721     }
722
723     fn ty_infer(&self, _span: Span) -> ty::t {
724         infer::new_infer_ctxt(self.tcx).next_ty_var()
725     }
726 }
727
728 impl<'a> Visitor<()> for Context<'a> {
729     fn visit_item(&mut self, it: &ast::Item, _: ()) {
730         self.with_lint_attrs(it.attrs.as_slice(), |cx| {
731             builtin::check_enum_variant_sizes(cx, it);
732             builtin::check_item_ctypes(cx, it);
733             builtin::check_item_non_camel_case_types(cx, it);
734             builtin::check_item_non_uppercase_statics(cx, it);
735             builtin::check_heap_item(cx, it);
736             builtin::check_missing_doc_item(cx, it);
737             builtin::check_raw_ptr_deriving(cx, it);
738
739             cx.visit_ids(|v| v.visit_item(it, ()));
740
741             visit::walk_item(cx, it, ());
742         })
743     }
744
745     fn visit_foreign_item(&mut self, it: &ast::ForeignItem, _: ()) {
746         self.with_lint_attrs(it.attrs.as_slice(), |cx| {
747             visit::walk_foreign_item(cx, it, ());
748         })
749     }
750
751     fn visit_view_item(&mut self, i: &ast::ViewItem, _: ()) {
752         self.with_lint_attrs(i.attrs.as_slice(), |cx| {
753             cx.visit_ids(|v| v.visit_view_item(i, ()));
754
755             visit::walk_view_item(cx, i, ());
756         })
757     }
758
759     fn visit_pat(&mut self, p: &ast::Pat, _: ()) {
760         builtin::check_pat_non_uppercase_statics(self, p);
761         builtin::check_pat_uppercase_variable(self, p);
762
763         visit::walk_pat(self, p, ());
764     }
765
766     fn visit_expr(&mut self, e: &ast::Expr, _: ()) {
767         match e.node {
768             ast::ExprUnary(ast::UnNeg, expr) => {
769                 // propagate negation, if the negation itself isn't negated
770                 if self.negated_expr_id != e.id {
771                     self.negated_expr_id = expr.id;
772                 }
773             },
774             ast::ExprParen(expr) => if self.negated_expr_id == e.id {
775                 self.negated_expr_id = expr.id
776             },
777             ast::ExprMatch(_, ref arms) => {
778                 for a in arms.iter() {
779                     builtin::check_unused_mut_pat(self, a.pats.as_slice());
780                 }
781             },
782             _ => ()
783         };
784
785         builtin::check_while_true_expr(self, e);
786         builtin::check_stability(self, e);
787         builtin::check_unnecessary_parens_expr(self, e);
788         builtin::check_unused_unsafe(self, e);
789         builtin::check_unsafe_block(self, e);
790         builtin::check_unnecessary_allocation(self, e);
791         builtin::check_heap_expr(self, e);
792
793         builtin::check_type_limits(self, e);
794         builtin::check_unused_casts(self, e);
795
796         visit::walk_expr(self, e, ());
797     }
798
799     fn visit_stmt(&mut self, s: &ast::Stmt, _: ()) {
800         builtin::check_path_statement(self, s);
801         builtin::check_unused_result(self, s);
802         builtin::check_unnecessary_parens_stmt(self, s);
803
804         match s.node {
805             ast::StmtDecl(d, _) => {
806                 match d.node {
807                     ast::DeclLocal(l) => {
808                         builtin::check_unused_mut_pat(self, &[l.pat]);
809                     },
810                     _ => {}
811                 }
812             },
813             _ => {}
814         }
815
816         visit::walk_stmt(self, s, ());
817     }
818
819     fn visit_fn(&mut self, fk: &visit::FnKind, decl: &ast::FnDecl,
820                 body: &ast::Block, span: Span, id: ast::NodeId, _: ()) {
821         let recurse = |this: &mut Context| {
822             visit::walk_fn(this, fk, decl, body, span, ());
823         };
824
825         for a in decl.inputs.iter(){
826             builtin::check_unused_mut_pat(self, &[a.pat]);
827         }
828
829         match *fk {
830             visit::FkMethod(ident, _, m) => {
831                 self.with_lint_attrs(m.attrs.as_slice(), |cx| {
832                     builtin::check_missing_doc_method(cx, m);
833
834                     match method_context(cx, m) {
835                         PlainImpl
836                             => builtin::check_snake_case(cx, "method", ident, span),
837                         TraitDefaultImpl
838                             => builtin::check_snake_case(cx, "trait method", ident, span),
839                         _ => (),
840                     }
841
842                     cx.visit_ids(|v| {
843                         v.visit_fn(fk, decl, body, span, id, ());
844                     });
845                     recurse(cx);
846                 })
847             },
848             visit::FkItemFn(ident, _, _, _) => {
849                 builtin::check_snake_case(self, "function", ident, span);
850                 recurse(self);
851             }
852             _ => recurse(self),
853         }
854     }
855
856     fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) {
857         self.with_lint_attrs(t.attrs.as_slice(), |cx| {
858             builtin::check_missing_doc_ty_method(cx, t);
859             builtin::check_snake_case(cx, "trait method", t.ident, t.span);
860
861             visit::walk_ty_method(cx, t, ());
862         })
863     }
864
865     fn visit_struct_def(&mut self,
866                         s: &ast::StructDef,
867                         _: ast::Ident,
868                         _: &ast::Generics,
869                         id: ast::NodeId,
870                         _: ()) {
871         builtin::check_struct_uppercase_variable(self, s);
872
873         let old_id = self.cur_struct_def_id;
874         self.cur_struct_def_id = id;
875         visit::walk_struct_def(self, s, ());
876         self.cur_struct_def_id = old_id;
877     }
878
879     fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) {
880         self.with_lint_attrs(s.node.attrs.as_slice(), |cx| {
881             builtin::check_missing_doc_struct_field(cx, s);
882
883             visit::walk_struct_field(cx, s, ());
884         })
885     }
886
887     fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) {
888         self.with_lint_attrs(v.node.attrs.as_slice(), |cx| {
889             builtin::check_missing_doc_variant(cx, v);
890
891             visit::walk_variant(cx, v, g, ());
892         })
893     }
894
895     // FIXME(#10894) should continue recursing
896     fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {}
897
898     fn visit_attribute(&mut self, attr: &ast::Attribute, _: ()) {
899         builtin::check_unused_attribute(self, attr);
900     }
901 }
902
903 impl<'a> IdVisitingOperation for Context<'a> {
904     fn visit_id(&self, id: ast::NodeId) {
905         match self.tcx.sess.lints.borrow_mut().pop(&id) {
906             None => {}
907             Some(l) => {
908                 for (lint, span, msg) in l.move_iter() {
909                     self.span_lint(lint, span, msg.as_slice())
910                 }
911             }
912         }
913     }
914 }
915
916 pub fn check_crate(tcx: &ty::ctxt,
917                    exported_items: &privacy::ExportedItems,
918                    krate: &ast::Crate) {
919     let mut cx = Context {
920         dict: get_lint_dict(),
921         cur: SmallIntMap::new(),
922         tcx: tcx,
923         exported_items: exported_items,
924         cur_struct_def_id: -1,
925         is_doc_hidden: false,
926         lint_stack: Vec::new(),
927         negated_expr_id: -1,
928         checked_raw_pointers: NodeSet::new(),
929         node_levels: HashMap::new(),
930     };
931
932     // Install default lint levels, followed by the command line levels, and
933     // then actually visit the whole crate.
934     for (_, spec) in cx.dict.iter() {
935         if spec.default != Allow {
936             cx.cur.insert(spec.lint as uint, (spec.default, Default));
937         }
938     }
939     for &(lint, level) in tcx.sess.opts.lint_opts.iter() {
940         cx.set_level(lint, level, CommandLine);
941     }
942     cx.with_lint_attrs(krate.attrs.as_slice(), |cx| {
943         cx.visit_id(ast::CRATE_NODE_ID);
944         cx.visit_ids(|v| {
945             v.visited_outermost = true;
946             visit::walk_crate(v, krate, ());
947         });
948
949         // since the root module isn't visited as an item (because it isn't an item), warn for it
950         // here.
951         builtin::check_missing_doc_attrs(cx,
952                                          None,
953                                          krate.attrs.as_slice(),
954                                          krate.span,
955                                          "crate");
956
957         visit::walk_crate(cx, krate, ());
958     });
959
960     // If we missed any lints added to the session, then there's a bug somewhere
961     // in the iteration code.
962     for (id, v) in tcx.sess.lints.borrow().iter() {
963         for &(lint, span, ref msg) in v.iter() {
964             tcx.sess.span_bug(span, format!("unprocessed lint {} at {}: {}",
965                                             lint, tcx.map.node_to_str(*id), *msg).as_slice())
966         }
967     }
968
969     tcx.sess.abort_if_errors();
970     *tcx.node_lint_levels.borrow_mut() = cx.node_levels;
971 }