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