]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/lint.rs
libsyntax/librustc: Allow mut qualifier in patterns.
[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     fn check_unused_mut_pat_inner(cx: &Context, p: @ast::Pat) {
817         let mut used = false;
818         let mut bindings = 0;
819         do pat_util::pat_bindings(cx.tcx.def_map, p) |_, id, _, _| {
820             used = used || cx.tcx.used_mut_nodes.contains(&id);
821             bindings += 1;
822         }
823         if !used {
824             let msg = if bindings == 1 {
825                 "variable does not need to be mutable"
826             } else {
827                 "variables do not need to be mutable"
828             };
829             cx.span_lint(unused_mut, p.span, msg);
830         }
831     }
832     do ast_util::walk_pat(p) |pat| {
833         match pat.node {
834             ast::PatIdent(ast::BindByValue(ast::MutMutable), _, _) => {
835                 check_unused_mut_pat_inner(cx, pat);
836             }
837             _ => {}
838         }
839
840         true
841     };
842 }
843
844 fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
845     // Warn if string and vector literals with sigils are immediately borrowed.
846     // Those can have the sigil removed.
847     match e.node {
848         ast::ExprVstore(e2, ast::ExprVstoreUniq) |
849         ast::ExprVstore(e2, ast::ExprVstoreBox) => {
850             match e2.node {
851                 ast::ExprLit(@codemap::Spanned{node: ast::lit_str(*), _}) |
852                 ast::ExprVec(*) => {}
853                 _ => return
854             }
855         }
856
857         _ => return
858     }
859
860     match cx.tcx.adjustments.find_copy(&e.id) {
861         Some(@ty::AutoDerefRef(ty::AutoDerefRef {
862             autoref: Some(ty::AutoBorrowVec(*)), _ })) => {
863             cx.span_lint(unnecessary_allocation, e.span,
864                          "unnecessary allocation, the sigil can be removed");
865         }
866
867         _ => ()
868     }
869 }
870
871 struct MissingDocLintVisitor(ty::ctxt);
872
873 impl MissingDocLintVisitor {
874     fn check_attrs(&self, attrs: &[ast::Attribute], id: ast::NodeId,
875                    sp: Span, msg: ~str) {
876         if !attrs.iter().any(|a| a.node.is_sugared_doc) {
877             self.sess.add_lint(missing_doc, id, sp, msg);
878         }
879     }
880
881     fn check_struct(&self, sdef: &ast::struct_def) {
882         for field in sdef.fields.iter() {
883             match field.node.kind {
884                 ast::named_field(_, vis) if vis != ast::private => {
885                     self.check_attrs(field.node.attrs, field.node.id, field.span,
886                                      ~"missing documentation for a field");
887                 }
888                 ast::unnamed_field | ast::named_field(*) => {}
889             }
890         }
891     }
892
893     fn doc_hidden(&self, attrs: &[ast::Attribute]) -> bool {
894         do attrs.iter().any |attr| {
895             "doc" == attr.name() &&
896                 match attr.meta_item_list() {
897                     Some(l) => attr::contains_name(l, "hidden"),
898                     None    => false // not of the form #[doc(...)]
899                 }
900         }
901     }
902 }
903
904 impl Visitor<()> for MissingDocLintVisitor {
905     fn visit_ty_method(&mut self, m:&ast::TypeMethod, _: ()) {
906         if self.doc_hidden(m.attrs) { return }
907
908         // All ty_method objects are linted about because they're part of a
909         // trait (no visibility)
910         self.check_attrs(m.attrs, m.id, m.span,
911                          ~"missing documentation for a method");
912         visit::walk_ty_method(self, m, ());
913     }
914
915     fn visit_fn(&mut self, fk: &visit::fn_kind, d: &ast::fn_decl,
916                 b: &ast::Block, sp: Span, id: ast::NodeId, _: ()) {
917         // Only warn about explicitly public methods.
918         match *fk {
919             visit::fk_method(_, _, m) => {
920                 if self.doc_hidden(m.attrs) {
921                     return;
922                 }
923                 // If we're in a trait implementation, no need to duplicate
924                 // documentation
925                 if m.vis == ast::public {
926                     self.check_attrs(m.attrs, id, sp,
927                                      ~"missing documentation for a method");
928                 }
929             }
930             _ => {}
931         }
932         visit::walk_fn(self, fk, d, b, sp, id, ());
933     }
934
935     fn visit_item(&mut self, it: @ast::item, _: ()) {
936         // If we're building a test harness, then warning about documentation is
937         // probably not really relevant right now
938         if self.sess.opts.test { return }
939         if self.doc_hidden(it.attrs) { return }
940
941         match it.node {
942             ast::item_struct(sdef, _) if it.vis == ast::public => {
943                 self.check_attrs(it.attrs, it.id, it.span,
944                                  ~"missing documentation for a struct");
945                 self.check_struct(sdef);
946             }
947
948             // Skip implementations because they inherit documentation from the
949             // trait (which was already linted)
950             ast::item_impl(_, Some(*), _, _) => return,
951
952             ast::item_trait(*) if it.vis == ast::public => {
953                 self.check_attrs(it.attrs, it.id, it.span,
954                                  ~"missing documentation for a trait");
955             }
956
957             ast::item_fn(*) if it.vis == ast::public => {
958                 self.check_attrs(it.attrs, it.id, it.span,
959                                  ~"missing documentation for a function");
960             }
961
962             ast::item_mod(*) if it.vis == ast::public => {
963                 self.check_attrs(it.attrs, it.id, it.span,
964                                  ~"missing documentation for a module");
965             }
966
967             ast::item_enum(ref edef, _) if it.vis == ast::public => {
968                 self.check_attrs(it.attrs, it.id, it.span,
969                                  ~"missing documentation for an enum");
970                 for variant in edef.variants.iter() {
971                     if variant.node.vis == ast::private { continue; }
972
973                     self.check_attrs(variant.node.attrs, variant.node.id,
974                                      variant.span,
975                                      ~"missing documentation for a variant");
976                     match variant.node.kind {
977                         ast::struct_variant_kind(sdef) => {
978                             self.check_struct(sdef);
979                         }
980                         _ => ()
981                     }
982                 }
983             }
984
985             _ => {}
986         }
987         visit::walk_item(self, it, ());
988     }
989 }
990
991 /// Checks for use of items with #[deprecated], #[experimental] and
992 /// #[unstable] (or none of them) attributes.
993 fn check_stability(cx: &Context, e: &ast::Expr) {
994     let def = match e.node {
995         ast::ExprMethodCall(*) |
996         ast::ExprPath(*) |
997         ast::ExprStruct(*) => {
998             match cx.tcx.def_map.find(&e.id) {
999                 Some(&def) => def,
1000                 None => return
1001             }
1002         }
1003         _ => return
1004     };
1005
1006     let id = ast_util::def_id_of_def(def);
1007
1008     let stability = if ast_util::is_local(id) {
1009         // this crate
1010         match cx.tcx.items.find(&id.node) {
1011             Some(ast_node) => {
1012                 let s = do ast_node.with_attrs |attrs| {
1013                     do attrs.map |a| {
1014                         attr::find_stability(a.iter().map(|a| a.meta()))
1015                     }
1016                 };
1017                 match s {
1018                     Some(s) => s,
1019
1020                     // no possibility of having attributes
1021                     // (e.g. it's a local variable), so just
1022                     // ignore it.
1023                     None => return
1024                 }
1025             }
1026             _ => cx.tcx.sess.bug(format!("handle_def: {:?} not found", id))
1027         }
1028     } else {
1029         // cross-crate
1030
1031         let mut s = None;
1032         // run through all the attributes and take the first
1033         // stability one.
1034         do csearch::get_item_attrs(cx.tcx.cstore, id) |meta_items| {
1035             if s.is_none() {
1036                 s = attr::find_stability(meta_items.move_iter())
1037             }
1038         }
1039         s
1040     };
1041
1042     let (lint, label) = match stability {
1043         // no stability attributes == Unstable
1044         None => (unstable, "unmarked"),
1045         Some(attr::Stability { level: attr::Unstable, _ }) =>
1046                 (unstable, "unstable"),
1047         Some(attr::Stability { level: attr::Experimental, _ }) =>
1048                 (experimental, "experimental"),
1049         Some(attr::Stability { level: attr::Deprecated, _ }) =>
1050                 (deprecated, "deprecated"),
1051         _ => return
1052     };
1053
1054     let msg = match stability {
1055         Some(attr::Stability { text: Some(ref s), _ }) => {
1056             format!("use of {} item: {}", label, *s)
1057         }
1058         _ => format!("use of {} item", label)
1059     };
1060
1061     cx.span_lint(lint, e.span, msg);
1062 }
1063
1064 impl Visitor<()> for Context {
1065     fn visit_item(&mut self, it: @ast::item, _: ()) {
1066         do self.with_lint_attrs(it.attrs) |cx| {
1067             check_item_ctypes(cx, it);
1068             check_item_non_camel_case_types(cx, it);
1069             check_item_non_uppercase_statics(cx, it);
1070             check_heap_item(cx, it);
1071
1072             do cx.visit_ids |v| {
1073                 v.visit_item(it, ());
1074             }
1075
1076             visit::walk_item(cx, it, ());
1077         }
1078     }
1079
1080     fn visit_pat(&mut self, p: @ast::Pat, _: ()) {
1081         check_pat_non_uppercase_statics(self, p);
1082         check_unused_mut_pat(self, p);
1083
1084         visit::walk_pat(self, p, ());
1085     }
1086
1087     fn visit_expr(&mut self, e: @ast::Expr, _: ()) {
1088         check_while_true_expr(self, e);
1089         check_stability(self, e);
1090         check_unused_unsafe(self, e);
1091         check_unnecessary_allocation(self, e);
1092         check_heap_expr(self, e);
1093         check_type_limits(self, e);
1094
1095         visit::walk_expr(self, e, ());
1096     }
1097
1098     fn visit_stmt(&mut self, s: @ast::Stmt, _: ()) {
1099         check_path_statement(self, s);
1100
1101         visit::walk_stmt(self, s, ());
1102     }
1103
1104     fn visit_fn(&mut self, fk: &visit::fn_kind, decl: &ast::fn_decl,
1105                 body: &ast::Block, span: Span, id: ast::NodeId, _: ()) {
1106         let recurse = |this: &mut Context| {
1107             visit::walk_fn(this, fk, decl, body, span, id, ());
1108         };
1109
1110         match *fk {
1111             visit::fk_method(_, _, m) => {
1112                 do self.with_lint_attrs(m.attrs) |cx| {
1113                     do cx.visit_ids |v| {
1114                         v.visit_fn(fk, decl, body, span, id, ());
1115                     }
1116                     recurse(cx);
1117                 }
1118             }
1119             _ => recurse(self),
1120         }
1121     }
1122 }
1123
1124 impl ast_util::IdVisitingOperation for Context {
1125     fn visit_id(&self, id: ast::NodeId) {
1126         match self.tcx.sess.lints.pop(&id) {
1127             None => {}
1128             Some(l) => {
1129                 for (lint, span, msg) in l.move_iter() {
1130                     self.span_lint(lint, span, msg)
1131                 }
1132             }
1133         }
1134     }
1135 }
1136
1137 pub fn check_crate(tcx: ty::ctxt, crate: &ast::Crate) {
1138     // This visitor contains more state than is currently maintained in Context,
1139     // and there's no reason for the Context to keep track of this information
1140     // really
1141     let mut dox = MissingDocLintVisitor(tcx);
1142     visit::walk_crate(&mut dox, crate, ());
1143
1144     let mut cx = Context {
1145         dict: @get_lint_dict(),
1146         cur: SmallIntMap::new(),
1147         tcx: tcx,
1148         lint_stack: ~[],
1149     };
1150
1151     // Install default lint levels, followed by the command line levels, and
1152     // then actually visit the whole crate.
1153     for (_, spec) in cx.dict.iter() {
1154         cx.set_level(spec.lint, spec.default, Default);
1155     }
1156     for &(lint, level) in tcx.sess.opts.lint_opts.iter() {
1157         cx.set_level(lint, level, CommandLine);
1158     }
1159     do cx.with_lint_attrs(crate.attrs) |cx| {
1160         do cx.visit_ids |v| {
1161             v.visited_outermost = true;
1162             visit::walk_crate(v, crate, ());
1163         }
1164         visit::walk_crate(cx, crate, ());
1165     }
1166
1167     // If we missed any lints added to the session, then there's a bug somewhere
1168     // in the iteration code.
1169     for (id, v) in tcx.sess.lints.iter() {
1170         for &(lint, span, ref msg) in v.iter() {
1171             tcx.sess.span_bug(span, format!("unprocessed lint {:?} at {}: {}",
1172                                             lint,
1173                                             ast_map::node_id_to_str(tcx.items,
1174                                                 *id,
1175                                                 token::get_ident_interner()),
1176                                             *msg))
1177         }
1178     }
1179
1180     tcx.sess.abort_if_errors();
1181 }