]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/lint.rs
auto merge of #10165 : dcrewi/rust/missing-doc-on-private-trait, r=cmr
[rust.git] / src / librustc / middle / lint.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
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 use driver::session;
37 use middle::trans::adt; // for `adt::is_ffi_safe`
38 use middle::ty;
39 use middle::pat_util;
40 use metadata::csearch;
41 use util::ppaux::{ty_to_str};
42
43 use std::cmp;
44 use std::hashmap::HashMap;
45 use std::i16;
46 use std::i32;
47 use std::i64;
48 use std::i8;
49 use std::u16;
50 use std::u32;
51 use std::u64;
52 use std::u8;
53 use extra::smallintmap::SmallIntMap;
54 use syntax::ast_map;
55 use syntax::attr;
56 use syntax::attr::{AttrMetaMethods, AttributeMethods};
57 use syntax::codemap::Span;
58 use syntax::codemap;
59 use syntax::parse::token;
60 use syntax::{ast, ast_util, visit};
61 use syntax::visit::Visitor;
62
63 #[deriving(Clone, Eq)]
64 pub enum lint {
65     ctypes,
66     cstack,
67     unused_imports,
68     unnecessary_qualification,
69     while_true,
70     path_statement,
71     unrecognized_lint,
72     non_camel_case_types,
73     non_uppercase_statics,
74     non_uppercase_pattern_statics,
75     type_limits,
76     unused_unsafe,
77
78     managed_heap_memory,
79     owned_heap_memory,
80     heap_memory,
81
82     unused_variable,
83     dead_assignment,
84     unused_mut,
85     unnecessary_allocation,
86
87     missing_doc,
88     unreachable_code,
89
90     deprecated,
91     experimental,
92     unstable,
93
94     warnings,
95 }
96
97 pub fn level_to_str(lv: level) -> &'static str {
98     match lv {
99       allow => "allow",
100       warn => "warn",
101       deny => "deny",
102       forbid => "forbid"
103     }
104 }
105
106 #[deriving(Clone, Eq, Ord)]
107 pub enum level {
108     allow, warn, deny, forbid
109 }
110
111 #[deriving(Clone, Eq)]
112 pub struct LintSpec {
113     lint: lint,
114     desc: &'static str,
115     default: level
116 }
117
118 impl Ord for LintSpec {
119     fn lt(&self, other: &LintSpec) -> bool { self.default < other.default }
120 }
121
122 pub type LintDict = HashMap<&'static str, LintSpec>;
123
124 #[deriving(Eq)]
125 enum LintSource {
126     Node(Span),
127     Default,
128     CommandLine
129 }
130
131 static lint_table: &'static [(&'static str, LintSpec)] = &[
132     ("ctypes",
133      LintSpec {
134         lint: ctypes,
135         desc: "proper use of std::libc types in foreign modules",
136         default: warn
137      }),
138
139     ("cstack",
140      LintSpec {
141         lint: cstack,
142         desc: "only invoke foreign functions from fixedstacksegment fns",
143         default: deny
144      }),
145
146     ("unused_imports",
147      LintSpec {
148         lint: unused_imports,
149         desc: "imports that are never used",
150         default: warn
151      }),
152
153     ("unnecessary_qualification",
154      LintSpec {
155         lint: unnecessary_qualification,
156         desc: "detects unnecessarily qualified names",
157         default: allow
158      }),
159
160     ("while_true",
161      LintSpec {
162         lint: while_true,
163         desc: "suggest using loop { } instead of while(true) { }",
164         default: warn
165      }),
166
167     ("path_statement",
168      LintSpec {
169         lint: path_statement,
170         desc: "path statements with no effect",
171         default: warn
172      }),
173
174     ("unrecognized_lint",
175      LintSpec {
176         lint: unrecognized_lint,
177         desc: "unrecognized lint attribute",
178         default: warn
179      }),
180
181     ("non_camel_case_types",
182      LintSpec {
183         lint: non_camel_case_types,
184         desc: "types, variants and traits should have camel case names",
185         default: allow
186      }),
187
188     ("non_uppercase_statics",
189      LintSpec {
190          lint: non_uppercase_statics,
191          desc: "static constants should have uppercase identifiers",
192          default: allow
193      }),
194
195     ("non_uppercase_pattern_statics",
196      LintSpec {
197          lint: non_uppercase_pattern_statics,
198          desc: "static constants in match patterns should be all caps",
199          default: warn
200      }),
201
202     ("managed_heap_memory",
203      LintSpec {
204         lint: managed_heap_memory,
205         desc: "use of managed (@ type) heap memory",
206         default: allow
207      }),
208
209     ("owned_heap_memory",
210      LintSpec {
211         lint: owned_heap_memory,
212         desc: "use of owned (~ type) heap memory",
213         default: allow
214      }),
215
216     ("heap_memory",
217      LintSpec {
218         lint: heap_memory,
219         desc: "use of any (~ type or @ type) heap memory",
220         default: allow
221      }),
222
223     ("type_limits",
224      LintSpec {
225         lint: type_limits,
226         desc: "comparisons made useless by limits of the types involved",
227         default: warn
228      }),
229
230     ("unused_unsafe",
231      LintSpec {
232         lint: unused_unsafe,
233         desc: "unnecessary use of an `unsafe` block",
234         default: warn
235     }),
236
237     ("unused_variable",
238      LintSpec {
239         lint: unused_variable,
240         desc: "detect variables which are not used in any way",
241         default: warn
242     }),
243
244     ("dead_assignment",
245      LintSpec {
246         lint: dead_assignment,
247         desc: "detect assignments that will never be read",
248         default: warn
249     }),
250
251     ("unused_mut",
252      LintSpec {
253         lint: unused_mut,
254         desc: "detect mut variables which don't need to be mutable",
255         default: warn
256     }),
257
258     ("unnecessary_allocation",
259      LintSpec {
260         lint: unnecessary_allocation,
261         desc: "detects unnecessary allocations that can be eliminated",
262         default: warn
263     }),
264
265     ("missing_doc",
266      LintSpec {
267         lint: missing_doc,
268         desc: "detects missing documentation for public members",
269         default: allow
270     }),
271
272     ("unreachable_code",
273      LintSpec {
274         lint: unreachable_code,
275         desc: "detects unreachable code",
276         default: warn
277     }),
278
279     ("deprecated",
280      LintSpec {
281         lint: deprecated,
282         desc: "detects use of #[deprecated] items",
283         default: warn
284     }),
285
286     ("experimental",
287      LintSpec {
288         lint: experimental,
289         desc: "detects use of #[experimental] items",
290         default: warn
291     }),
292
293     ("unstable",
294      LintSpec {
295         lint: unstable,
296         desc: "detects use of #[unstable] items (incl. items with no stability attribute)",
297         default: allow
298     }),
299
300     ("warnings",
301      LintSpec {
302         lint: warnings,
303         desc: "mass-change the level for lints which produce warnings",
304         default: warn
305     }),
306 ];
307
308 /*
309   Pass names should not contain a '-', as the compiler normalizes
310   '-' to '_' in command-line flags
311  */
312 pub fn get_lint_dict() -> LintDict {
313     let mut map = HashMap::new();
314     for &(k, v) in lint_table.iter() {
315         map.insert(k, v);
316     }
317     return map;
318 }
319
320 struct Context {
321     // All known lint modes (string versions)
322     dict: @LintDict,
323     // Current levels of each lint warning
324     cur: SmallIntMap<(level, LintSource)>,
325     // context we're checking in (used to access fields like sess)
326     tcx: ty::ctxt,
327
328     // When recursing into an attributed node of the ast which modifies lint
329     // levels, this stack keeps track of the previous lint levels of whatever
330     // was modified.
331     lint_stack: ~[(lint, level, LintSource)],
332 }
333
334 impl Context {
335     fn get_level(&self, lint: lint) -> level {
336         match self.cur.find(&(lint as uint)) {
337           Some(&(lvl, _)) => lvl,
338           None => allow
339         }
340     }
341
342     fn get_source(&self, lint: lint) -> LintSource {
343         match self.cur.find(&(lint as uint)) {
344           Some(&(_, src)) => src,
345           None => Default
346         }
347     }
348
349     fn set_level(&mut self, lint: lint, level: level, src: LintSource) {
350         if level == allow {
351             self.cur.remove(&(lint as uint));
352         } else {
353             self.cur.insert(lint as uint, (level, src));
354         }
355     }
356
357     fn lint_to_str(&self, lint: lint) -> &'static str {
358         for (k, v) in self.dict.iter() {
359             if v.lint == lint {
360                 return *k;
361             }
362         }
363         fail!("unregistered lint {:?}", lint);
364     }
365
366     fn span_lint(&self, lint: lint, span: Span, msg: &str) {
367         let (level, src) = match self.cur.find(&(lint as uint)) {
368             None => { return }
369             Some(&(warn, src)) => (self.get_level(warnings), src),
370             Some(&pair) => pair,
371         };
372         if level == allow { return }
373
374         let mut note = None;
375         let msg = match src {
376             Default => {
377                 format!("{}, \\#[{}({})] on by default", msg,
378                     level_to_str(level), self.lint_to_str(lint))
379             },
380             CommandLine => {
381                 format!("{} [-{} {}]", msg,
382                     match level {
383                         warn => 'W', deny => 'D', forbid => 'F',
384                         allow => fail!()
385                     }, self.lint_to_str(lint).replace("_", "-"))
386             },
387             Node(src) => {
388                 note = Some(src);
389                 msg.to_str()
390             }
391         };
392         match level {
393             warn =>          { self.tcx.sess.span_warn(span, msg); }
394             deny | forbid => { self.tcx.sess.span_err(span, msg);  }
395             allow => fail!(),
396         }
397
398         for &span in note.iter() {
399             self.tcx.sess.span_note(span, "lint level defined here");
400         }
401     }
402
403     /**
404      * Merge the lints specified by any lint attributes into the
405      * current lint context, call the provided function, then reset the
406      * lints in effect to their previous state.
407      */
408     fn with_lint_attrs(&mut self, attrs: &[ast::Attribute],
409                        f: &fn(&mut Context)) {
410         // Parse all of the lint attributes, and then add them all to the
411         // current dictionary of lint information. Along the way, keep a history
412         // of what we changed so we can roll everything back after invoking the
413         // specified closure
414         let mut pushed = 0u;
415         do each_lint(self.tcx.sess, attrs) |meta, level, lintname| {
416             match self.dict.find_equiv(&lintname) {
417                 None => {
418                     self.span_lint(
419                         unrecognized_lint,
420                         meta.span,
421                         format!("unknown `{}` attribute: `{}`",
422                         level_to_str(level), lintname));
423                 }
424                 Some(lint) => {
425                     let lint = lint.lint;
426                     let now = self.get_level(lint);
427                     if now == forbid && level != forbid {
428                         self.tcx.sess.span_err(meta.span,
429                         format!("{}({}) overruled by outer forbid({})",
430                         level_to_str(level),
431                         lintname, lintname));
432                     } else if now != level {
433                         let src = self.get_source(lint);
434                         self.lint_stack.push((lint, now, src));
435                         pushed += 1;
436                         self.set_level(lint, level, Node(meta.span));
437                     }
438                 }
439             }
440             true
441         };
442
443         f(self);
444
445         // rollback
446         do pushed.times {
447             let (lint, lvl, src) = self.lint_stack.pop();
448             self.set_level(lint, lvl, src);
449         }
450     }
451
452     fn visit_ids(&self, f: &fn(&mut ast_util::IdVisitor<Context>)) {
453         let mut v = ast_util::IdVisitor {
454             operation: self,
455             pass_through_items: false,
456             visited_outermost: false,
457         };
458         f(&mut v);
459     }
460 }
461
462 pub fn each_lint(sess: session::Session,
463                  attrs: &[ast::Attribute],
464                  f: &fn(@ast::MetaItem, level, @str) -> bool) -> bool {
465     let xs = [allow, warn, deny, forbid];
466     for &level in xs.iter() {
467         let level_name = level_to_str(level);
468         for attr in attrs.iter().filter(|m| level_name == m.name()) {
469             let meta = attr.node.value;
470             let metas = match meta.node {
471                 ast::MetaList(_, ref metas) => metas,
472                 _ => {
473                     sess.span_err(meta.span, "malformed lint attribute");
474                     continue;
475                 }
476             };
477             for meta in metas.iter() {
478                 match meta.node {
479                     ast::MetaWord(lintname) => {
480                         if !f(*meta, level, lintname) {
481                             return false;
482                         }
483                     }
484                     _ => {
485                         sess.span_err(meta.span, "malformed lint attribute");
486                     }
487                 }
488             }
489         }
490     }
491     true
492 }
493
494 fn check_while_true_expr(cx: &Context, e: &ast::Expr) {
495     match e.node {
496         ast::ExprWhile(cond, _) => {
497             match cond.node {
498                 ast::ExprLit(@codemap::Spanned {
499                     node: ast::lit_bool(true), _}) =>
500                 {
501                     cx.span_lint(while_true, e.span,
502                                  "denote infinite loops with loop { ... }");
503                 }
504                 _ => ()
505             }
506         }
507         _ => ()
508     }
509 }
510
511 fn check_type_limits(cx: &Context, e: &ast::Expr) {
512     return match e.node {
513         ast::ExprBinary(_, binop, l, r) => {
514             if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) {
515                 cx.span_lint(type_limits, e.span,
516                              "comparison is useless due to type limits");
517             }
518         }
519         _ => ()
520     };
521
522     fn is_valid<T:cmp::Ord>(binop: ast::BinOp, v: T,
523                             min: T, max: T) -> bool {
524         match binop {
525             ast::BiLt => v <= max,
526             ast::BiLe => v < max,
527             ast::BiGt => v >= min,
528             ast::BiGe => v > min,
529             ast::BiEq | ast::BiNe => v >= min && v <= max,
530             _ => fail!()
531         }
532     }
533
534     fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
535         match binop {
536             ast::BiLt => ast::BiGt,
537             ast::BiLe => ast::BiGe,
538             ast::BiGt => ast::BiLt,
539             ast::BiGe => ast::BiLe,
540             _ => binop
541         }
542     }
543
544     // for int & uint, be conservative with the warnings, so that the
545     // warnings are consistent between 32- and 64-bit platforms
546     fn int_ty_range(int_ty: ast::int_ty) -> (i64, i64) {
547         match int_ty {
548             ast::ty_i =>    (i64::min_value,        i64::max_value),
549             ast::ty_i8 =>   (i8::min_value  as i64, i8::max_value  as i64),
550             ast::ty_i16 =>  (i16::min_value as i64, i16::max_value as i64),
551             ast::ty_i32 =>  (i32::min_value as i64, i32::max_value as i64),
552             ast::ty_i64 =>  (i64::min_value,        i64::max_value)
553         }
554     }
555
556     fn uint_ty_range(uint_ty: ast::uint_ty) -> (u64, u64) {
557         match uint_ty {
558             ast::ty_u =>   (u64::min_value,         u64::max_value),
559             ast::ty_u8 =>  (u8::min_value   as u64, u8::max_value   as u64),
560             ast::ty_u16 => (u16::min_value  as u64, u16::max_value  as u64),
561             ast::ty_u32 => (u32::min_value  as u64, u32::max_value  as u64),
562             ast::ty_u64 => (u64::min_value,         u64::max_value)
563         }
564     }
565
566     fn check_limits(tcx: ty::ctxt, binop: ast::BinOp,
567                     l: &ast::Expr, r: &ast::Expr) -> bool {
568         let (lit, expr, swap) = match (&l.node, &r.node) {
569             (&ast::ExprLit(_), _) => (l, r, true),
570             (_, &ast::ExprLit(_)) => (r, l, false),
571             _ => return true
572         };
573         // Normalize the binop so that the literal is always on the RHS in
574         // the comparison
575         let norm_binop = if swap { rev_binop(binop) } else { binop };
576         match ty::get(ty::expr_ty(tcx, expr)).sty {
577             ty::ty_int(int_ty) => {
578                 let (min, max) = int_ty_range(int_ty);
579                 let lit_val: i64 = match lit.node {
580                     ast::ExprLit(li) => match li.node {
581                         ast::lit_int(v, _) => v,
582                         ast::lit_uint(v, _) => v as i64,
583                         ast::lit_int_unsuffixed(v) => v,
584                         _ => return true
585                     },
586                     _ => fail!()
587                 };
588                 is_valid(norm_binop, lit_val, min, max)
589             }
590             ty::ty_uint(uint_ty) => {
591                 let (min, max): (u64, u64) = uint_ty_range(uint_ty);
592                 let lit_val: u64 = match lit.node {
593                     ast::ExprLit(li) => match li.node {
594                         ast::lit_int(v, _) => v as u64,
595                         ast::lit_uint(v, _) => v,
596                         ast::lit_int_unsuffixed(v) => v as u64,
597                         _ => return true
598                     },
599                     _ => fail!()
600                 };
601                 is_valid(norm_binop, lit_val, min, max)
602             }
603             _ => true
604         }
605     }
606
607     fn is_comparison(binop: ast::BinOp) -> bool {
608         match binop {
609             ast::BiEq | ast::BiLt | ast::BiLe |
610             ast::BiNe | ast::BiGe | ast::BiGt => true,
611             _ => false
612         }
613     }
614 }
615
616 fn check_item_ctypes(cx: &Context, it: &ast::item) {
617     fn check_ty(cx: &Context, ty: &ast::Ty) {
618         match ty.node {
619             ast::ty_path(_, _, id) => {
620                 match cx.tcx.def_map.get_copy(&id) {
621                     ast::DefPrimTy(ast::ty_int(ast::ty_i)) => {
622                         cx.span_lint(ctypes, ty.span,
623                                 "found rust type `int` in foreign module, while \
624                                 libc::c_int or libc::c_long should be used");
625                     }
626                     ast::DefPrimTy(ast::ty_uint(ast::ty_u)) => {
627                         cx.span_lint(ctypes, ty.span,
628                                 "found rust type `uint` in foreign module, while \
629                                 libc::c_uint or libc::c_ulong should be used");
630                     }
631                     ast::DefTy(def_id) => {
632                         if !adt::is_ffi_safe(cx.tcx, def_id) {
633                             cx.span_lint(ctypes, ty.span,
634                                          "found enum type without foreign-function-safe \
635                                           representation annotation in foreign module");
636                             // NOTE this message could be more helpful
637                         }
638                     }
639                     _ => ()
640                 }
641             }
642             ast::ty_ptr(ref mt) => { check_ty(cx, mt.ty) }
643             _ => ()
644         }
645     }
646
647     fn check_foreign_fn(cx: &Context, decl: &ast::fn_decl) {
648         for input in decl.inputs.iter() {
649             check_ty(cx, &input.ty);
650         }
651         check_ty(cx, &decl.output)
652     }
653
654     match it.node {
655       ast::item_foreign_mod(ref nmod) if !nmod.abis.is_intrinsic() => {
656         for ni in nmod.items.iter() {
657             match ni.node {
658                 ast::foreign_item_fn(ref decl, _) => {
659                     check_foreign_fn(cx, decl);
660                 }
661                 ast::foreign_item_static(ref t, _) => { check_ty(cx, t); }
662             }
663         }
664       }
665       _ => {/* nothing to do */ }
666     }
667 }
668
669 fn check_heap_type(cx: &Context, span: Span, ty: ty::t) {
670     let xs = [managed_heap_memory, owned_heap_memory, heap_memory];
671     for &lint in xs.iter() {
672         if cx.get_level(lint) == allow { continue }
673
674         let mut n_box = 0;
675         let mut n_uniq = 0;
676         ty::fold_ty(cx.tcx, ty, |t| {
677             match ty::get(t).sty {
678               ty::ty_box(_) => n_box += 1,
679               ty::ty_uniq(_) => n_uniq += 1,
680               _ => ()
681             };
682             t
683         });
684
685         if n_uniq > 0 && lint != managed_heap_memory {
686             let s = ty_to_str(cx.tcx, ty);
687             let m = format!("type uses owned (~ type) pointers: {}", s);
688             cx.span_lint(lint, span, m);
689         }
690
691         if n_box > 0 && lint != owned_heap_memory {
692             let s = ty_to_str(cx.tcx, ty);
693             let m = format!("type uses managed (@ type) pointers: {}", s);
694             cx.span_lint(lint, span, m);
695         }
696     }
697 }
698
699 fn check_heap_item(cx: &Context, it: &ast::item) {
700     match it.node {
701         ast::item_fn(*) |
702         ast::item_ty(*) |
703         ast::item_enum(*) |
704         ast::item_struct(*) => check_heap_type(cx, it.span,
705                                                ty::node_id_to_type(cx.tcx,
706                                                                    it.id)),
707         _ => ()
708     }
709
710     // If it's a struct, we also have to check the fields' types
711     match it.node {
712         ast::item_struct(struct_def, _) => {
713             for struct_field in struct_def.fields.iter() {
714                 check_heap_type(cx, struct_field.span,
715                                 ty::node_id_to_type(cx.tcx,
716                                                     struct_field.node.id));
717             }
718         }
719         _ => ()
720     }
721 }
722
723 fn check_heap_expr(cx: &Context, e: &ast::Expr) {
724     let ty = ty::expr_ty(cx.tcx, e);
725     check_heap_type(cx, e.span, ty);
726 }
727
728 fn check_path_statement(cx: &Context, s: &ast::Stmt) {
729     match s.node {
730         ast::StmtSemi(@ast::Expr { node: ast::ExprPath(_), _ }, _) => {
731             cx.span_lint(path_statement, s.span,
732                          "path statement with no effect");
733         }
734         _ => ()
735     }
736 }
737
738 fn check_item_non_camel_case_types(cx: &Context, it: &ast::item) {
739     fn is_camel_case(cx: ty::ctxt, ident: ast::Ident) -> bool {
740         let ident = cx.sess.str_of(ident);
741         assert!(!ident.is_empty());
742         let ident = ident.trim_chars(&'_');
743
744         // start with a non-lowercase letter rather than non-uppercase
745         // ones (some scripts don't have a concept of upper/lowercase)
746         !ident.char_at(0).is_lowercase() &&
747             !ident.contains_char('_')
748     }
749
750     fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
751         if !is_camel_case(cx.tcx, ident) {
752             cx.span_lint(
753                 non_camel_case_types, span,
754                 format!("{} `{}` should have a camel case identifier",
755                     sort, cx.tcx.sess.str_of(ident)));
756         }
757     }
758
759     match it.node {
760         ast::item_ty(*) | ast::item_struct(*) => {
761             check_case(cx, "type", it.ident, it.span)
762         }
763         ast::item_trait(*) => {
764             check_case(cx, "trait", it.ident, it.span)
765         }
766         ast::item_enum(ref enum_definition, _) => {
767             check_case(cx, "type", it.ident, it.span);
768             for variant in enum_definition.variants.iter() {
769                 check_case(cx, "variant", variant.node.name, variant.span);
770             }
771         }
772         _ => ()
773     }
774 }
775
776 fn check_item_non_uppercase_statics(cx: &Context, it: &ast::item) {
777     match it.node {
778         // only check static constants
779         ast::item_static(_, ast::MutImmutable, _) => {
780             let s = cx.tcx.sess.str_of(it.ident);
781             // check for lowercase letters rather than non-uppercase
782             // ones (some scripts don't have a concept of
783             // upper/lowercase)
784             if s.iter().any(|c| c.is_lowercase()) {
785                 cx.span_lint(non_uppercase_statics, it.span,
786                              "static constant should have an uppercase identifier");
787             }
788         }
789         _ => {}
790     }
791 }
792
793 fn check_pat_non_uppercase_statics(cx: &Context, p: &ast::Pat) {
794     // Lint for constants that look like binding identifiers (#7526)
795     match (&p.node, cx.tcx.def_map.find(&p.id)) {
796         (&ast::PatIdent(_, ref path, _), Some(&ast::DefStatic(_, false))) => {
797             // last identifier alone is right choice for this lint.
798             let ident = path.segments.last().identifier;
799             let s = cx.tcx.sess.str_of(ident);
800             if s.iter().any(|c| c.is_lowercase()) {
801                 cx.span_lint(non_uppercase_pattern_statics, path.span,
802                              "static constant in pattern should be all caps");
803             }
804         }
805         _ => {}
806     }
807 }
808
809 fn check_unused_unsafe(cx: &Context, e: &ast::Expr) {
810     match e.node {
811         // Don't warn about generated blocks, that'll just pollute the
812         // output.
813         ast::ExprBlock(ref blk) => {
814             if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
815                 !cx.tcx.used_unsafe.contains(&blk.id) {
816                 cx.span_lint(unused_unsafe, blk.span,
817                              "unnecessary `unsafe` block");
818             }
819         }
820         _ => ()
821     }
822 }
823
824 fn check_unused_mut_pat(cx: &Context, p: @ast::Pat) {
825     match p.node {
826         ast::PatIdent(ast::BindByValue(ast::MutMutable), _, _) => {
827             let mut used = false;
828             let mut bindings = 0;
829             do pat_util::pat_bindings(cx.tcx.def_map, p) |_, id, _, _| {
830                 used = used || cx.tcx.used_mut_nodes.contains(&id);
831                 bindings += 1;
832             }
833             if !used {
834                 let msg = if bindings == 1 {
835                     "variable does not need to be mutable"
836                 } else {
837                     "variables do not need to be mutable"
838                 };
839                 cx.span_lint(unused_mut, p.span, msg);
840             }
841         }
842         _ => ()
843     }
844 }
845
846 fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
847     // Warn if string and vector literals with sigils are immediately borrowed.
848     // Those can have the sigil removed.
849     match e.node {
850         ast::ExprVstore(e2, ast::ExprVstoreUniq) |
851         ast::ExprVstore(e2, ast::ExprVstoreBox) => {
852             match e2.node {
853                 ast::ExprLit(@codemap::Spanned{node: ast::lit_str(*), _}) |
854                 ast::ExprVec(*) => {}
855                 _ => return
856             }
857         }
858
859         _ => return
860     }
861
862     match cx.tcx.adjustments.find_copy(&e.id) {
863         Some(@ty::AutoDerefRef(ty::AutoDerefRef {
864             autoref: Some(ty::AutoBorrowVec(*)), _ })) => {
865             cx.span_lint(unnecessary_allocation, e.span,
866                          "unnecessary allocation, the sigil can be removed");
867         }
868
869         _ => ()
870     }
871 }
872
873 struct MissingDocLintVisitor(ty::ctxt);
874
875 impl MissingDocLintVisitor {
876     fn check_attrs(&self, attrs: &[ast::Attribute], id: ast::NodeId,
877                    sp: Span, msg: ~str) {
878         if !attrs.iter().any(|a| a.node.is_sugared_doc) {
879             self.sess.add_lint(missing_doc, id, sp, msg);
880         }
881     }
882
883     fn check_struct(&self, sdef: &ast::struct_def) {
884         for field in sdef.fields.iter() {
885             match field.node.kind {
886                 ast::named_field(_, vis) if vis != ast::private => {
887                     self.check_attrs(field.node.attrs, field.node.id, field.span,
888                                      ~"missing documentation for a field");
889                 }
890                 ast::unnamed_field | ast::named_field(*) => {}
891             }
892         }
893     }
894
895     fn doc_hidden(&self, attrs: &[ast::Attribute]) -> bool {
896         do attrs.iter().any |attr| {
897             "doc" == attr.name() &&
898                 match attr.meta_item_list() {
899                     Some(l) => attr::contains_name(l, "hidden"),
900                     None    => false // not of the form #[doc(...)]
901                 }
902         }
903     }
904 }
905
906 impl Visitor<()> for MissingDocLintVisitor {
907     fn visit_ty_method(&mut self, m:&ast::TypeMethod, _: ()) {
908         if self.doc_hidden(m.attrs) { return }
909
910         // All ty_method objects are linted about because they're part of a
911         // trait (no visibility)
912         self.check_attrs(m.attrs, m.id, m.span,
913                          ~"missing documentation for a method");
914         visit::walk_ty_method(self, m, ());
915     }
916
917     fn visit_fn(&mut self, fk: &visit::fn_kind, d: &ast::fn_decl,
918                 b: &ast::Block, sp: Span, id: ast::NodeId, _: ()) {
919         // Only warn about explicitly public methods.
920         match *fk {
921             visit::fk_method(_, _, m) => {
922                 if self.doc_hidden(m.attrs) {
923                     return;
924                 }
925                 // If we're in a trait implementation, no need to duplicate
926                 // documentation
927                 if m.vis == ast::public {
928                     self.check_attrs(m.attrs, id, sp,
929                                      ~"missing documentation for a method");
930                 }
931             }
932             _ => {}
933         }
934         visit::walk_fn(self, fk, d, b, sp, id, ());
935     }
936
937     fn visit_item(&mut self, it: @ast::item, _: ()) {
938         // If we're building a test harness, then warning about documentation is
939         // probably not really relevant right now
940         if self.sess.opts.test { return }
941         if self.doc_hidden(it.attrs) { return }
942
943         match it.node {
944             ast::item_struct(sdef, _) if it.vis == ast::public => {
945                 self.check_attrs(it.attrs, it.id, it.span,
946                                  ~"missing documentation for a struct");
947                 self.check_struct(sdef);
948             }
949
950             // Skip implementations because they inherit documentation from the
951             // trait (which was already linted)
952             ast::item_impl(_, Some(*), _, _) => return,
953
954             ast::item_trait(*) if it.vis != ast::public => return,
955             ast::item_trait(*) => self.check_attrs(it.attrs, it.id, it.span,
956                                                    ~"missing documentation for a trait"),
957
958             ast::item_fn(*) if it.vis == ast::public => {
959                 self.check_attrs(it.attrs, it.id, it.span,
960                                  ~"missing documentation for a function");
961             }
962
963             ast::item_mod(*) if it.vis == ast::public => {
964                 self.check_attrs(it.attrs, it.id, it.span,
965                                  ~"missing documentation for a module");
966             }
967
968             ast::item_enum(ref edef, _) if it.vis == ast::public => {
969                 self.check_attrs(it.attrs, it.id, it.span,
970                                  ~"missing documentation for an enum");
971                 for variant in edef.variants.iter() {
972                     if variant.node.vis == ast::private { continue; }
973
974                     self.check_attrs(variant.node.attrs, variant.node.id,
975                                      variant.span,
976                                      ~"missing documentation for a variant");
977                     match variant.node.kind {
978                         ast::struct_variant_kind(sdef) => {
979                             self.check_struct(sdef);
980                         }
981                         _ => ()
982                     }
983                 }
984             }
985
986             _ => {}
987         }
988         visit::walk_item(self, it, ());
989     }
990 }
991
992 /// Checks for use of items with #[deprecated], #[experimental] and
993 /// #[unstable] (or none of them) attributes.
994 fn check_stability(cx: &Context, e: &ast::Expr) {
995     let def = match e.node {
996         ast::ExprMethodCall(*) |
997         ast::ExprPath(*) |
998         ast::ExprStruct(*) => {
999             match cx.tcx.def_map.find(&e.id) {
1000                 Some(&def) => def,
1001                 None => return
1002             }
1003         }
1004         _ => return
1005     };
1006
1007     let id = ast_util::def_id_of_def(def);
1008
1009     let stability = if ast_util::is_local(id) {
1010         // this crate
1011         match cx.tcx.items.find(&id.node) {
1012             Some(ast_node) => {
1013                 let s = do ast_node.with_attrs |attrs| {
1014                     do attrs.map |a| {
1015                         attr::find_stability(a.iter().map(|a| a.meta()))
1016                     }
1017                 };
1018                 match s {
1019                     Some(s) => s,
1020
1021                     // no possibility of having attributes
1022                     // (e.g. it's a local variable), so just
1023                     // ignore it.
1024                     None => return
1025                 }
1026             }
1027             _ => cx.tcx.sess.bug(format!("handle_def: {:?} not found", id))
1028         }
1029     } else {
1030         // cross-crate
1031
1032         let mut s = None;
1033         // run through all the attributes and take the first
1034         // stability one.
1035         do csearch::get_item_attrs(cx.tcx.cstore, id) |meta_items| {
1036             if s.is_none() {
1037                 s = attr::find_stability(meta_items.move_iter())
1038             }
1039         }
1040         s
1041     };
1042
1043     let (lint, label) = match stability {
1044         // no stability attributes == Unstable
1045         None => (unstable, "unmarked"),
1046         Some(attr::Stability { level: attr::Unstable, _ }) =>
1047                 (unstable, "unstable"),
1048         Some(attr::Stability { level: attr::Experimental, _ }) =>
1049                 (experimental, "experimental"),
1050         Some(attr::Stability { level: attr::Deprecated, _ }) =>
1051                 (deprecated, "deprecated"),
1052         _ => return
1053     };
1054
1055     let msg = match stability {
1056         Some(attr::Stability { text: Some(ref s), _ }) => {
1057             format!("use of {} item: {}", label, *s)
1058         }
1059         _ => format!("use of {} item", label)
1060     };
1061
1062     cx.span_lint(lint, e.span, msg);
1063 }
1064
1065 impl Visitor<()> for Context {
1066     fn visit_item(&mut self, it: @ast::item, _: ()) {
1067         do self.with_lint_attrs(it.attrs) |cx| {
1068             check_item_ctypes(cx, it);
1069             check_item_non_camel_case_types(cx, it);
1070             check_item_non_uppercase_statics(cx, it);
1071             check_heap_item(cx, it);
1072
1073             do cx.visit_ids |v| {
1074                 v.visit_item(it, ());
1075             }
1076
1077             visit::walk_item(cx, it, ());
1078         }
1079     }
1080
1081     fn visit_pat(&mut self, p: @ast::Pat, _: ()) {
1082         check_pat_non_uppercase_statics(self, p);
1083         check_unused_mut_pat(self, p);
1084
1085         visit::walk_pat(self, p, ());
1086     }
1087
1088     fn visit_expr(&mut self, e: @ast::Expr, _: ()) {
1089         check_while_true_expr(self, e);
1090         check_stability(self, e);
1091         check_unused_unsafe(self, e);
1092         check_unnecessary_allocation(self, e);
1093         check_heap_expr(self, e);
1094         check_type_limits(self, e);
1095
1096         visit::walk_expr(self, e, ());
1097     }
1098
1099     fn visit_stmt(&mut self, s: @ast::Stmt, _: ()) {
1100         check_path_statement(self, s);
1101
1102         visit::walk_stmt(self, s, ());
1103     }
1104
1105     fn visit_fn(&mut self, fk: &visit::fn_kind, decl: &ast::fn_decl,
1106                 body: &ast::Block, span: Span, id: ast::NodeId, _: ()) {
1107         let recurse = |this: &mut Context| {
1108             visit::walk_fn(this, fk, decl, body, span, id, ());
1109         };
1110
1111         match *fk {
1112             visit::fk_method(_, _, m) => {
1113                 do self.with_lint_attrs(m.attrs) |cx| {
1114                     do cx.visit_ids |v| {
1115                         v.visit_fn(fk, decl, body, span, id, ());
1116                     }
1117                     recurse(cx);
1118                 }
1119             }
1120             _ => recurse(self),
1121         }
1122     }
1123 }
1124
1125 impl ast_util::IdVisitingOperation for Context {
1126     fn visit_id(&self, id: ast::NodeId) {
1127         match self.tcx.sess.lints.pop(&id) {
1128             None => {}
1129             Some(l) => {
1130                 for (lint, span, msg) in l.move_iter() {
1131                     self.span_lint(lint, span, msg)
1132                 }
1133             }
1134         }
1135     }
1136 }
1137
1138 pub fn check_crate(tcx: ty::ctxt, crate: &ast::Crate) {
1139     // This visitor contains more state than is currently maintained in Context,
1140     // and there's no reason for the Context to keep track of this information
1141     // really
1142     let mut dox = MissingDocLintVisitor(tcx);
1143     visit::walk_crate(&mut dox, crate, ());
1144
1145     let mut cx = Context {
1146         dict: @get_lint_dict(),
1147         cur: SmallIntMap::new(),
1148         tcx: tcx,
1149         lint_stack: ~[],
1150     };
1151
1152     // Install default lint levels, followed by the command line levels, and
1153     // then actually visit the whole crate.
1154     for (_, spec) in cx.dict.iter() {
1155         cx.set_level(spec.lint, spec.default, Default);
1156     }
1157     for &(lint, level) in tcx.sess.opts.lint_opts.iter() {
1158         cx.set_level(lint, level, CommandLine);
1159     }
1160     do cx.with_lint_attrs(crate.attrs) |cx| {
1161         do cx.visit_ids |v| {
1162             v.visited_outermost = true;
1163             visit::walk_crate(v, crate, ());
1164         }
1165         visit::walk_crate(cx, crate, ());
1166     }
1167
1168     // If we missed any lints added to the session, then there's a bug somewhere
1169     // in the iteration code.
1170     for (id, v) in tcx.sess.lints.iter() {
1171         for &(lint, span, ref msg) in v.iter() {
1172             tcx.sess.span_bug(span, format!("unprocessed lint {:?} at {}: {}",
1173                                             lint,
1174                                             ast_map::node_id_to_str(tcx.items,
1175                                                 *id,
1176                                                 token::get_ident_interner()),
1177                                             *msg))
1178         }
1179     }
1180
1181     tcx.sess.abort_if_errors();
1182 }