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