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