]> git.lizzy.rs Git - rust.git/blob - src/librustc/lint/builtin.rs
721e5f296e2e7b7f218d9de42d96ea1b189c6550
[rust.git] / src / librustc / lint / builtin.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Lints built in to rustc.
12 //!
13 //! This is a sibling of `lint::context` in order to ensure that
14 //! lints implemented here use the same public API as lint plugins.
15 //!
16 //! To add a new lint to rustc, declare it here using `declare_lint!()`.
17 //! Then add code to emit the new lint in the appropriate circumstances.
18 //! You can do that in an existing `LintPass` if it makes sense, or in
19 //! a new `LintPass`, or using `Session::add_lint` elsewhere in the
20 //! compiler. Only do the latter if the check can't be written cleanly
21 //! as a `LintPass`.
22 //!
23 //! If you define a new `LintPass`, you will also need to add it to the
24 //! `add_builtin!` or `add_builtin_with_new!` invocation in `context.rs`.
25 //! Use the former for unit-like structs and the latter for structs with
26 //! a `pub fn new()`.
27
28 use metadata::csearch;
29 use middle::def::*;
30 use middle::typeck::astconv::ast_ty_to_ty;
31 use middle::typeck::infer;
32 use middle::{typeck, ty, def, pat_util, stability};
33 use util::ppaux::{ty_to_string};
34 use util::nodemap::NodeSet;
35 use lint::{Context, LintPass, LintArray};
36
37 use std::cmp;
38 use std::collections::HashMap;
39 use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
40 use std::gc::Gc;
41 use syntax::abi;
42 use syntax::ast_map;
43 use syntax::attr::AttrMetaMethods;
44 use syntax::attr;
45 use syntax::codemap::Span;
46 use syntax::parse::token;
47 use syntax::{ast, ast_util, visit};
48 use syntax::visit::Visitor;
49
50 declare_lint!(WHILE_TRUE, Warn,
51               "suggest using `loop { }` instead of `while true { }`")
52
53 pub struct WhileTrue;
54
55 impl LintPass for WhileTrue {
56     fn get_lints(&self) -> LintArray {
57         lint_array!(WHILE_TRUE)
58     }
59
60     fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
61         match e.node {
62             ast::ExprWhile(cond, _, _) => {
63                 match cond.node {
64                     ast::ExprLit(lit) => {
65                         match lit.node {
66                             ast::LitBool(true) => {
67                                 cx.span_lint(WHILE_TRUE, e.span,
68                                              "denote infinite loops with loop \
69                                               { ... }");
70                             }
71                             _ => {}
72                         }
73                     }
74                     _ => ()
75                 }
76             }
77             _ => ()
78         }
79     }
80 }
81
82 declare_lint!(UNNECESSARY_TYPECAST, Allow,
83               "detects unnecessary type casts, that can be removed")
84
85 pub struct UnusedCasts;
86
87 impl LintPass for UnusedCasts {
88     fn get_lints(&self) -> LintArray {
89         lint_array!(UNNECESSARY_TYPECAST)
90     }
91
92     fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
93         match e.node {
94             ast::ExprCast(expr, ty) => {
95                 let t_t = ast_ty_to_ty(cx, &infer::new_infer_ctxt(cx.tcx), &*ty);
96                 if ty::get(ty::expr_ty(cx.tcx, &*expr)).sty == ty::get(t_t).sty {
97                     cx.span_lint(UNNECESSARY_TYPECAST, ty.span, "unnecessary type cast");
98                 }
99             }
100             _ => ()
101         }
102     }
103 }
104
105 declare_lint!(UNSIGNED_NEGATE, Warn,
106               "using an unary minus operator on unsigned type")
107
108 declare_lint!(TYPE_LIMITS, Warn,
109               "comparisons made useless by limits of the types involved")
110
111 declare_lint!(TYPE_OVERFLOW, Warn,
112               "literal out of range for its type")
113
114 pub struct TypeLimits {
115     /// Id of the last visited negated expression
116     negated_expr_id: ast::NodeId,
117 }
118
119 impl TypeLimits {
120     pub fn new() -> TypeLimits {
121         TypeLimits {
122             negated_expr_id: -1,
123         }
124     }
125 }
126
127 impl LintPass for TypeLimits {
128     fn get_lints(&self) -> LintArray {
129         lint_array!(UNSIGNED_NEGATE, TYPE_LIMITS, TYPE_OVERFLOW)
130     }
131
132     fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
133         match e.node {
134             ast::ExprUnary(ast::UnNeg, expr) => {
135                 match expr.node  {
136                     ast::ExprLit(lit) => {
137                         match lit.node {
138                             ast::LitInt(_, ast::UnsignedIntLit(_)) => {
139                                 cx.span_lint(UNSIGNED_NEGATE, e.span,
140                                              "negation of unsigned int literal may \
141                                              be unintentional");
142                             },
143                             _ => ()
144                         }
145                     },
146                     _ => {
147                         let t = ty::expr_ty(cx.tcx, &*expr);
148                         match ty::get(t).sty {
149                             ty::ty_uint(_) => {
150                                 cx.span_lint(UNSIGNED_NEGATE, e.span,
151                                              "negation of unsigned int variable may \
152                                              be unintentional");
153                             },
154                             _ => ()
155                         }
156                     }
157                 };
158                 // propagate negation, if the negation itself isn't negated
159                 if self.negated_expr_id != e.id {
160                     self.negated_expr_id = expr.id;
161                 }
162             },
163             ast::ExprParen(expr) if self.negated_expr_id == e.id => {
164                 self.negated_expr_id = expr.id;
165             },
166             ast::ExprBinary(binop, l, r) => {
167                 if is_comparison(binop) && !check_limits(cx.tcx, binop, &*l, &*r) {
168                     cx.span_lint(TYPE_LIMITS, e.span,
169                                  "comparison is useless due to type limits");
170                 }
171             },
172             ast::ExprLit(lit) => {
173                 match ty::get(ty::expr_ty(cx.tcx, e)).sty {
174                     ty::ty_int(t) => {
175                         match lit.node {
176                             ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) |
177                             ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => {
178                                 let int_type = if t == ast::TyI {
179                                     cx.sess().targ_cfg.int_type
180                                 } else { t };
181                                 let (min, max) = int_ty_range(int_type);
182                                 let negative = self.negated_expr_id == e.id;
183
184                                 if (negative && v > (min.abs() as u64)) ||
185                                    (!negative && v > (max.abs() as u64)) {
186                                     cx.span_lint(TYPE_OVERFLOW, e.span,
187                                                  "literal out of range for its type");
188                                     return;
189                                 }
190                             }
191                             _ => fail!()
192                         };
193                     },
194                     ty::ty_uint(t) => {
195                         let uint_type = if t == ast::TyU {
196                             cx.sess().targ_cfg.uint_type
197                         } else { t };
198                         let (min, max) = uint_ty_range(uint_type);
199                         let lit_val: u64 = match lit.node {
200                             ast::LitByte(_v) => return,  // _v is u8, within range by definition
201                             ast::LitInt(v, _) => v,
202                             _ => fail!()
203                         };
204                         if  lit_val < min || lit_val > max {
205                             cx.span_lint(TYPE_OVERFLOW, e.span,
206                                          "literal out of range for its type");
207                         }
208                     },
209                     ty::ty_float(t) => {
210                         let (min, max) = float_ty_range(t);
211                         let lit_val: f64 = match lit.node {
212                             ast::LitFloat(ref v, _) |
213                             ast::LitFloatUnsuffixed(ref v) => match from_str(v.get()) {
214                                 Some(f) => f,
215                                 None => return
216                             },
217                             _ => fail!()
218                         };
219                         if lit_val < min || lit_val > max {
220                             cx.span_lint(TYPE_OVERFLOW, e.span,
221                                          "literal out of range for its type");
222                         }
223                     },
224                     _ => ()
225                 };
226             },
227             _ => ()
228         };
229
230         fn is_valid<T:cmp::PartialOrd>(binop: ast::BinOp, v: T,
231                                 min: T, max: T) -> bool {
232             match binop {
233                 ast::BiLt => v >  min && v <= max,
234                 ast::BiLe => v >= min && v <  max,
235                 ast::BiGt => v >= min && v <  max,
236                 ast::BiGe => v >  min && v <= max,
237                 ast::BiEq | ast::BiNe => v >= min && v <= max,
238                 _ => fail!()
239             }
240         }
241
242         fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
243             match binop {
244                 ast::BiLt => ast::BiGt,
245                 ast::BiLe => ast::BiGe,
246                 ast::BiGt => ast::BiLt,
247                 ast::BiGe => ast::BiLe,
248                 _ => binop
249             }
250         }
251
252         // for int & uint, be conservative with the warnings, so that the
253         // warnings are consistent between 32- and 64-bit platforms
254         fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) {
255             match int_ty {
256                 ast::TyI =>    (i64::MIN,        i64::MAX),
257                 ast::TyI8 =>   (i8::MIN  as i64, i8::MAX  as i64),
258                 ast::TyI16 =>  (i16::MIN as i64, i16::MAX as i64),
259                 ast::TyI32 =>  (i32::MIN as i64, i32::MAX as i64),
260                 ast::TyI64 =>  (i64::MIN,        i64::MAX)
261             }
262         }
263
264         fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
265             match uint_ty {
266                 ast::TyU =>   (u64::MIN,         u64::MAX),
267                 ast::TyU8 =>  (u8::MIN   as u64, u8::MAX   as u64),
268                 ast::TyU16 => (u16::MIN  as u64, u16::MAX  as u64),
269                 ast::TyU32 => (u32::MIN  as u64, u32::MAX  as u64),
270                 ast::TyU64 => (u64::MIN,         u64::MAX)
271             }
272         }
273
274         fn float_ty_range(float_ty: ast::FloatTy) -> (f64, f64) {
275             match float_ty {
276                 ast::TyF32  => (f32::MIN_VALUE as f64, f32::MAX_VALUE as f64),
277                 ast::TyF64  => (f64::MIN_VALUE,        f64::MAX_VALUE)
278             }
279         }
280
281         fn check_limits(tcx: &ty::ctxt, binop: ast::BinOp,
282                         l: &ast::Expr, r: &ast::Expr) -> bool {
283             let (lit, expr, swap) = match (&l.node, &r.node) {
284                 (&ast::ExprLit(_), _) => (l, r, true),
285                 (_, &ast::ExprLit(_)) => (r, l, false),
286                 _ => return true
287             };
288             // Normalize the binop so that the literal is always on the RHS in
289             // the comparison
290             let norm_binop = if swap { rev_binop(binop) } else { binop };
291             match ty::get(ty::expr_ty(tcx, expr)).sty {
292                 ty::ty_int(int_ty) => {
293                     let (min, max) = int_ty_range(int_ty);
294                     let lit_val: i64 = match lit.node {
295                         ast::ExprLit(li) => match li.node {
296                             ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) |
297                             ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => v as i64,
298                             ast::LitInt(v, ast::SignedIntLit(_, ast::Minus)) |
299                             ast::LitInt(v, ast::UnsuffixedIntLit(ast::Minus)) => -(v as i64),
300                             _ => return true
301                         },
302                         _ => fail!()
303                     };
304                     is_valid(norm_binop, lit_val, min, max)
305                 }
306                 ty::ty_uint(uint_ty) => {
307                     let (min, max): (u64, u64) = uint_ty_range(uint_ty);
308                     let lit_val: u64 = match lit.node {
309                         ast::ExprLit(li) => match li.node {
310                             ast::LitInt(v, _) => v,
311                             _ => return true
312                         },
313                         _ => fail!()
314                     };
315                     is_valid(norm_binop, lit_val, min, max)
316                 }
317                 _ => true
318             }
319         }
320
321         fn is_comparison(binop: ast::BinOp) -> bool {
322             match binop {
323                 ast::BiEq | ast::BiLt | ast::BiLe |
324                 ast::BiNe | ast::BiGe | ast::BiGt => true,
325                 _ => false
326             }
327         }
328     }
329 }
330
331 declare_lint!(CTYPES, Warn,
332               "proper use of libc types in foreign modules")
333
334 struct CTypesVisitor<'a, 'tcx: 'a> {
335     cx: &'a Context<'a, 'tcx>
336 }
337
338 impl<'a, 'tcx> CTypesVisitor<'a, 'tcx> {
339     fn check_def(&mut self, sp: Span, ty_id: ast::NodeId, path_id: ast::NodeId) {
340         match self.cx.tcx.def_map.borrow().get_copy(&path_id) {
341             def::DefPrimTy(ast::TyInt(ast::TyI)) => {
342                 self.cx.span_lint(CTYPES, sp,
343                                   "found rust type `int` in foreign module, while \
344                                    libc::c_int or libc::c_long should be used");
345             }
346             def::DefPrimTy(ast::TyUint(ast::TyU)) => {
347                 self.cx.span_lint(CTYPES, sp,
348                                   "found rust type `uint` in foreign module, while \
349                                    libc::c_uint or libc::c_ulong should be used");
350             }
351             def::DefTy(..) => {
352                 let tty = match self.cx.tcx.ast_ty_to_ty_cache.borrow().find(&ty_id) {
353                     Some(&ty::atttce_resolved(t)) => t,
354                     _ => fail!("ast_ty_to_ty_cache was incomplete after typeck!")
355                 };
356
357                 if !ty::is_ffi_safe(self.cx.tcx, tty) {
358                     self.cx.span_lint(CTYPES, sp,
359                                       "found type without foreign-function-safe
360                                       representation annotation in foreign module, consider \
361                                       adding a #[repr(...)] attribute to the type");
362                 }
363             }
364             _ => ()
365         }
366     }
367 }
368
369 impl<'a, 'tcx> Visitor<()> for CTypesVisitor<'a, 'tcx> {
370     fn visit_ty(&mut self, ty: &ast::Ty, _: ()) {
371         match ty.node {
372             ast::TyPath(_, _, id) => self.check_def(ty.span, ty.id, id),
373             _ => (),
374         }
375         visit::walk_ty(self, ty, ());
376     }
377 }
378
379 pub struct CTypes;
380
381 impl LintPass for CTypes {
382     fn get_lints(&self) -> LintArray {
383         lint_array!(CTYPES)
384     }
385
386     fn check_item(&mut self, cx: &Context, it: &ast::Item) {
387         fn check_ty(cx: &Context, ty: &ast::Ty) {
388             let mut vis = CTypesVisitor { cx: cx };
389             vis.visit_ty(ty, ());
390         }
391
392         fn check_foreign_fn(cx: &Context, decl: &ast::FnDecl) {
393             for input in decl.inputs.iter() {
394                 check_ty(cx, &*input.ty);
395             }
396             check_ty(cx, &*decl.output)
397         }
398
399         match it.node {
400             ast::ItemForeignMod(ref nmod) if nmod.abi != abi::RustIntrinsic => {
401                 for ni in nmod.items.iter() {
402                     match ni.node {
403                         ast::ForeignItemFn(decl, _) => check_foreign_fn(cx, &*decl),
404                         ast::ForeignItemStatic(t, _) => check_ty(cx, &*t)
405                     }
406                 }
407             }
408             _ => (),
409         }
410     }
411 }
412
413 declare_lint!(MANAGED_HEAP_MEMORY, Allow,
414               "use of managed (@ type) heap memory")
415
416 declare_lint!(OWNED_HEAP_MEMORY, Allow,
417               "use of owned (Box type) heap memory")
418
419 declare_lint!(HEAP_MEMORY, Allow,
420               "use of any (Box type or @ type) heap memory")
421
422 pub struct HeapMemory;
423
424 impl HeapMemory {
425     fn check_heap_type(&self, cx: &Context, span: Span, ty: ty::t) {
426         let mut n_box = 0i;
427         let mut n_uniq = 0i;
428         ty::fold_ty(cx.tcx, ty, |t| {
429             match ty::get(t).sty {
430                 ty::ty_box(_) => {
431                     n_box += 1;
432                 }
433                 ty::ty_uniq(_) |
434                 ty::ty_closure(box ty::ClosureTy {
435                     store: ty::UniqTraitStore,
436                     ..
437                 }) => {
438                     n_uniq += 1;
439                 }
440
441                 _ => ()
442             };
443             t
444         });
445
446         if n_uniq > 0 {
447             let s = ty_to_string(cx.tcx, ty);
448             let m = format!("type uses owned (Box type) pointers: {}", s);
449             cx.span_lint(OWNED_HEAP_MEMORY, span, m.as_slice());
450             cx.span_lint(HEAP_MEMORY, span, m.as_slice());
451         }
452
453         if n_box > 0 {
454             let s = ty_to_string(cx.tcx, ty);
455             let m = format!("type uses managed (@ type) pointers: {}", s);
456             cx.span_lint(MANAGED_HEAP_MEMORY, span, m.as_slice());
457             cx.span_lint(HEAP_MEMORY, span, m.as_slice());
458         }
459     }
460 }
461
462 impl LintPass for HeapMemory {
463     fn get_lints(&self) -> LintArray {
464         lint_array!(MANAGED_HEAP_MEMORY, OWNED_HEAP_MEMORY, HEAP_MEMORY)
465     }
466
467     fn check_item(&mut self, cx: &Context, it: &ast::Item) {
468         match it.node {
469             ast::ItemFn(..) |
470             ast::ItemTy(..) |
471             ast::ItemEnum(..) |
472             ast::ItemStruct(..) =>
473                 self.check_heap_type(cx, it.span,
474                                      ty::node_id_to_type(cx.tcx, it.id)),
475             _ => ()
476         }
477
478         // If it's a struct, we also have to check the fields' types
479         match it.node {
480             ast::ItemStruct(struct_def, _) => {
481                 for struct_field in struct_def.fields.iter() {
482                     self.check_heap_type(cx, struct_field.span,
483                                          ty::node_id_to_type(cx.tcx, struct_field.node.id));
484                 }
485             }
486             _ => ()
487         }
488     }
489
490     fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
491         let ty = ty::expr_ty(cx.tcx, e);
492         self.check_heap_type(cx, e.span, ty);
493     }
494 }
495
496 declare_lint!(RAW_POINTER_DERIVING, Warn,
497               "uses of #[deriving] with raw pointers are rarely correct")
498
499 struct RawPtrDerivingVisitor<'a, 'tcx: 'a> {
500     cx: &'a Context<'a, 'tcx>
501 }
502
503 impl<'a, 'tcx> Visitor<()> for RawPtrDerivingVisitor<'a, 'tcx> {
504     fn visit_ty(&mut self, ty: &ast::Ty, _: ()) {
505         static MSG: &'static str = "use of `#[deriving]` with a raw pointer";
506         match ty.node {
507             ast::TyPtr(..) => self.cx.span_lint(RAW_POINTER_DERIVING, ty.span, MSG),
508             _ => {}
509         }
510         visit::walk_ty(self, ty, ());
511     }
512     // explicit override to a no-op to reduce code bloat
513     fn visit_expr(&mut self, _: &ast::Expr, _: ()) {}
514     fn visit_block(&mut self, _: &ast::Block, _: ()) {}
515 }
516
517 pub struct RawPointerDeriving {
518     checked_raw_pointers: NodeSet,
519 }
520
521 impl RawPointerDeriving {
522     pub fn new() -> RawPointerDeriving {
523         RawPointerDeriving {
524             checked_raw_pointers: NodeSet::new(),
525         }
526     }
527 }
528
529 impl LintPass for RawPointerDeriving {
530     fn get_lints(&self) -> LintArray {
531         lint_array!(RAW_POINTER_DERIVING)
532     }
533
534     fn check_item(&mut self, cx: &Context, item: &ast::Item) {
535         if !attr::contains_name(item.attrs.as_slice(), "automatically_derived") {
536             return
537         }
538         let did = match item.node {
539             ast::ItemImpl(..) => {
540                 match ty::get(ty::node_id_to_type(cx.tcx, item.id)).sty {
541                     ty::ty_enum(did, _) => did,
542                     ty::ty_struct(did, _) => did,
543                     _ => return,
544                 }
545             }
546             _ => return,
547         };
548         if !ast_util::is_local(did) { return }
549         let item = match cx.tcx.map.find(did.node) {
550             Some(ast_map::NodeItem(item)) => item,
551             _ => return,
552         };
553         if !self.checked_raw_pointers.insert(item.id) { return }
554         match item.node {
555             ast::ItemStruct(..) | ast::ItemEnum(..) => {
556                 let mut visitor = RawPtrDerivingVisitor { cx: cx };
557                 visit::walk_item(&mut visitor, &*item, ());
558             }
559             _ => {}
560         }
561     }
562 }
563
564 declare_lint!(UNUSED_ATTRIBUTE, Warn,
565               "detects attributes that were not used by the compiler")
566
567 pub struct UnusedAttribute;
568
569 impl LintPass for UnusedAttribute {
570     fn get_lints(&self) -> LintArray {
571         lint_array!(UNUSED_ATTRIBUTE)
572     }
573
574     fn check_attribute(&mut self, cx: &Context, attr: &ast::Attribute) {
575         static ATTRIBUTE_WHITELIST: &'static [&'static str] = &[
576             // FIXME: #14408 whitelist docs since rustdoc looks at them
577             "doc",
578
579             // FIXME: #14406 these are processed in trans, which happens after the
580             // lint pass
581             "cold",
582             "export_name",
583             "inline",
584             "link",
585             "link_name",
586             "link_section",
587             "no_builtins",
588             "no_mangle",
589             "no_split_stack",
590             "packed",
591             "static_assert",
592             "thread_local",
593             "no_debug",
594
595             // used in resolve
596             "prelude_import",
597
598             // not used anywhere (!?) but apparently we want to keep them around
599             "comment",
600             "desc",
601             "license",
602
603             // FIXME: #14407 these are only looked at on-demand so we can't
604             // guarantee they'll have already been checked
605             "deprecated",
606             "experimental",
607             "frozen",
608             "locked",
609             "must_use",
610             "stable",
611             "unstable",
612         ];
613
614         static CRATE_ATTRS: &'static [&'static str] = &[
615             "crate_name",
616             "crate_type",
617             "feature",
618             "no_start",
619             "no_main",
620             "no_std",
621             "desc",
622             "comment",
623             "license",
624             "copyright",
625             "no_builtins",
626         ];
627
628         for &name in ATTRIBUTE_WHITELIST.iter() {
629             if attr.check_name(name) {
630                 break;
631             }
632         }
633
634         if !attr::is_used(attr) {
635             cx.span_lint(UNUSED_ATTRIBUTE, attr.span, "unused attribute");
636             if CRATE_ATTRS.contains(&attr.name().get()) {
637                 let msg = match attr.node.style {
638                     ast::AttrOuter => "crate-level attribute should be an inner \
639                                        attribute: add an exclamation mark: #![foo]",
640                     ast::AttrInner => "crate-level attribute should be in the \
641                                        root module",
642                 };
643                 cx.span_lint(UNUSED_ATTRIBUTE, attr.span, msg);
644             }
645         }
646     }
647 }
648
649 declare_lint!(PATH_STATEMENT, Warn,
650               "path statements with no effect")
651
652 pub struct PathStatement;
653
654 impl LintPass for PathStatement {
655     fn get_lints(&self) -> LintArray {
656         lint_array!(PATH_STATEMENT)
657     }
658
659     fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
660         match s.node {
661             ast::StmtSemi(expr, _) => {
662                 match expr.node {
663                     ast::ExprPath(_) => cx.span_lint(PATH_STATEMENT, s.span,
664                                                      "path statement with no effect"),
665                     _ => ()
666                 }
667             }
668             _ => ()
669         }
670     }
671 }
672
673 declare_lint!(UNUSED_MUST_USE, Warn,
674               "unused result of a type flagged as #[must_use]")
675
676 declare_lint!(UNUSED_RESULT, Allow,
677               "unused result of an expression in a statement")
678
679 pub struct UnusedResult;
680
681 impl LintPass for UnusedResult {
682     fn get_lints(&self) -> LintArray {
683         lint_array!(UNUSED_MUST_USE, UNUSED_RESULT)
684     }
685
686     fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
687         let expr = match s.node {
688             ast::StmtSemi(expr, _) => expr,
689             _ => return
690         };
691         let t = ty::expr_ty(cx.tcx, &*expr);
692         match ty::get(t).sty {
693             ty::ty_nil | ty::ty_bot | ty::ty_bool => return,
694             _ => {}
695         }
696         match expr.node {
697             ast::ExprRet(..) => return,
698             _ => {}
699         }
700
701         let t = ty::expr_ty(cx.tcx, &*expr);
702         let mut warned = false;
703         match ty::get(t).sty {
704             ty::ty_struct(did, _) |
705             ty::ty_enum(did, _) => {
706                 if ast_util::is_local(did) {
707                     match cx.tcx.map.get(did.node) {
708                         ast_map::NodeItem(it) => {
709                             warned |= check_must_use(cx, it.attrs.as_slice(), s.span);
710                         }
711                         _ => {}
712                     }
713                 } else {
714                     csearch::get_item_attrs(&cx.sess().cstore, did, |attrs| {
715                         warned |= check_must_use(cx, attrs.as_slice(), s.span);
716                     });
717                 }
718             }
719             _ => {}
720         }
721         if !warned {
722             cx.span_lint(UNUSED_RESULT, s.span, "unused result");
723         }
724
725         fn check_must_use(cx: &Context, attrs: &[ast::Attribute], sp: Span) -> bool {
726             for attr in attrs.iter() {
727                 if attr.check_name("must_use") {
728                     let mut msg = "unused result which must be used".to_string();
729                     // check for #[must_use="..."]
730                     match attr.value_str() {
731                         None => {}
732                         Some(s) => {
733                             msg.push_str(": ");
734                             msg.push_str(s.get());
735                         }
736                     }
737                     cx.span_lint(UNUSED_MUST_USE, sp, msg.as_slice());
738                     return true;
739                 }
740             }
741             false
742         }
743     }
744 }
745
746 declare_lint!(pub NON_CAMEL_CASE_TYPES, Warn,
747               "types, variants, traits and type parameters should have camel case names")
748
749 pub struct NonCamelCaseTypes;
750
751 impl NonCamelCaseTypes {
752     fn check_case(&self, cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
753         fn is_camel_case(ident: ast::Ident) -> bool {
754             let ident = token::get_ident(ident);
755             if ident.get().is_empty() { return true; }
756             let ident = ident.get().trim_chars('_');
757
758             // start with a non-lowercase letter rather than non-uppercase
759             // ones (some scripts don't have a concept of upper/lowercase)
760             ident.len() > 0 && !ident.char_at(0).is_lowercase() && !ident.contains_char('_')
761         }
762
763         fn to_camel_case(s: &str) -> String {
764             s.split('_').flat_map(|word| word.chars().enumerate().map(|(i, c)|
765                 if i == 0 { c.to_uppercase() }
766                 else { c }
767             )).collect()
768         }
769
770         let s = token::get_ident(ident);
771
772         if !is_camel_case(ident) {
773             let c = to_camel_case(s.get());
774             let m = if c.is_empty() {
775                 format!("{} `{}` should have a camel case name such as `CamelCase`", sort, s)
776             } else {
777                 format!("{} `{}` should have a camel case name such as `{}`", sort, s, c)
778             };
779             cx.span_lint(NON_CAMEL_CASE_TYPES, span, m.as_slice());
780         }
781     }
782 }
783
784 impl LintPass for NonCamelCaseTypes {
785     fn get_lints(&self) -> LintArray {
786         lint_array!(NON_CAMEL_CASE_TYPES)
787     }
788
789     fn check_item(&mut self, cx: &Context, it: &ast::Item) {
790         let has_extern_repr = it.attrs.iter().map(|attr| {
791             attr::find_repr_attrs(cx.tcx.sess.diagnostic(), attr).iter()
792                 .any(|r| r == &attr::ReprExtern)
793         }).any(|x| x);
794         if has_extern_repr { return }
795
796         match it.node {
797             ast::ItemTy(..) | ast::ItemStruct(..) => {
798                 self.check_case(cx, "type", it.ident, it.span)
799             }
800             ast::ItemTrait(..) => {
801                 self.check_case(cx, "trait", it.ident, it.span)
802             }
803             ast::ItemEnum(ref enum_definition, _) => {
804                 if has_extern_repr { return }
805                 self.check_case(cx, "type", it.ident, it.span);
806                 for variant in enum_definition.variants.iter() {
807                     self.check_case(cx, "variant", variant.node.name, variant.span);
808                 }
809             }
810             _ => ()
811         }
812     }
813
814     fn check_generics(&mut self, cx: &Context, it: &ast::Generics) {
815         for gen in it.ty_params.iter() {
816             self.check_case(cx, "type parameter", gen.ident, gen.span);
817         }
818     }
819 }
820
821 #[deriving(PartialEq)]
822 enum MethodContext {
823     TraitDefaultImpl,
824     TraitImpl,
825     PlainImpl
826 }
827
828 fn method_context(cx: &Context, m: &ast::Method) -> MethodContext {
829     let did = ast::DefId {
830         krate: ast::LOCAL_CRATE,
831         node: m.id
832     };
833
834     match cx.tcx.impl_or_trait_items.borrow().find_copy(&did) {
835         None => cx.sess().span_bug(m.span, "missing method descriptor?!"),
836         Some(md) => {
837             match md {
838                 ty::MethodTraitItem(md) => {
839                     match md.container {
840                         ty::TraitContainer(..) => TraitDefaultImpl,
841                         ty::ImplContainer(cid) => {
842                             match ty::impl_trait_ref(cx.tcx, cid) {
843                                 Some(..) => TraitImpl,
844                                 None => PlainImpl
845                             }
846                         }
847                     }
848                 }
849             }
850         }
851     }
852 }
853
854 declare_lint!(pub NON_SNAKE_CASE, Warn,
855               "methods, functions, lifetime parameters and modules should have snake case names")
856
857 pub struct NonSnakeCase;
858
859 impl NonSnakeCase {
860     fn check_snake_case(&self, cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
861         fn is_snake_case(ident: ast::Ident) -> bool {
862             let ident = token::get_ident(ident);
863             if ident.get().is_empty() { return true; }
864             let ident = ident.get().trim_left_chars('\'');
865             let ident = ident.trim_chars('_');
866
867             let mut allow_underscore = true;
868             ident.chars().all(|c| {
869                 allow_underscore = match c {
870                     c if c.is_lowercase() || c.is_digit() => true,
871                     '_' if allow_underscore => false,
872                     _ => return false,
873                 };
874                 true
875             })
876         }
877
878         fn to_snake_case(str: &str) -> String {
879             let mut words = vec![];
880             for s in str.split('_') {
881                 let mut buf = String::new();
882                 if s.is_empty() { continue; }
883                 for ch in s.chars() {
884                     if !buf.is_empty() && buf.as_slice() != "'" && ch.is_uppercase() {
885                         words.push(buf);
886                         buf = String::new();
887                     }
888                     buf.push_char(ch.to_lowercase());
889                 }
890                 words.push(buf);
891             }
892             words.connect("_")
893         }
894
895         let s = token::get_ident(ident);
896
897         if !is_snake_case(ident) {
898             cx.span_lint(NON_SNAKE_CASE, span,
899                 format!("{} `{}` should have a snake case name such as `{}`",
900                         sort, s, to_snake_case(s.get())).as_slice());
901         }
902     }
903 }
904
905 impl LintPass for NonSnakeCase {
906     fn get_lints(&self) -> LintArray {
907         lint_array!(NON_SNAKE_CASE)
908     }
909
910     fn check_fn(&mut self, cx: &Context,
911                 fk: &visit::FnKind, _: &ast::FnDecl,
912                 _: &ast::Block, span: Span, _: ast::NodeId) {
913         match *fk {
914             visit::FkMethod(ident, _, m) => match method_context(cx, m) {
915                 PlainImpl
916                     => self.check_snake_case(cx, "method", ident, span),
917                 TraitDefaultImpl
918                     => self.check_snake_case(cx, "trait method", ident, span),
919                 _ => (),
920             },
921             visit::FkItemFn(ident, _, _, _)
922                 => self.check_snake_case(cx, "function", ident, span),
923             _ => (),
924         }
925     }
926
927     fn check_item(&mut self, cx: &Context, it: &ast::Item) {
928         match it.node {
929             ast::ItemMod(_) => {
930                 self.check_snake_case(cx, "module", it.ident, it.span);
931             }
932             _ => {}
933         }
934     }
935
936     fn check_ty_method(&mut self, cx: &Context, t: &ast::TypeMethod) {
937         self.check_snake_case(cx, "trait method", t.ident, t.span);
938     }
939
940     fn check_lifetime_decl(&mut self, cx: &Context, t: &ast::LifetimeDef) {
941         self.check_snake_case(cx, "lifetime", t.lifetime.name.ident(), t.lifetime.span);
942     }
943
944     fn check_pat(&mut self, cx: &Context, p: &ast::Pat) {
945         match &p.node {
946             &ast::PatIdent(_, ref path1, _) => {
947                 match cx.tcx.def_map.borrow().find(&p.id) {
948                     Some(&def::DefLocal(_, _)) | Some(&def::DefBinding(_, _)) |
949                             Some(&def::DefArg(_, _)) => {
950                         self.check_snake_case(cx, "variable", path1.node, p.span);
951                     }
952                     _ => {}
953                 }
954             }
955             _ => {}
956         }
957     }
958
959     fn check_struct_def(&mut self, cx: &Context, s: &ast::StructDef,
960             _: ast::Ident, _: &ast::Generics, _: ast::NodeId) {
961         for sf in s.fields.iter() {
962             match sf.node {
963                 ast::StructField_ { kind: ast::NamedField(ident, _), .. } => {
964                     self.check_snake_case(cx, "structure field", ident, sf.span);
965                 }
966                 _ => {}
967             }
968         }
969     }
970 }
971
972 declare_lint!(pub NON_UPPERCASE_STATICS, Allow,
973               "static constants should have uppercase identifiers")
974
975 pub struct NonUppercaseStatics;
976
977 impl LintPass for NonUppercaseStatics {
978     fn get_lints(&self) -> LintArray {
979         lint_array!(NON_UPPERCASE_STATICS)
980     }
981
982     fn check_item(&mut self, cx: &Context, it: &ast::Item) {
983         match it.node {
984             // only check static constants
985             ast::ItemStatic(_, ast::MutImmutable, _) => {
986                 let s = token::get_ident(it.ident);
987                 // check for lowercase letters rather than non-uppercase
988                 // ones (some scripts don't have a concept of
989                 // upper/lowercase)
990                 if s.get().chars().any(|c| c.is_lowercase()) {
991                     cx.span_lint(NON_UPPERCASE_STATICS, it.span,
992                         format!("static constant `{}` should have an uppercase name \
993                                  such as `{}`",
994                                 s.get(), s.get().chars().map(|c| c.to_uppercase())
995                                 .collect::<String>().as_slice()).as_slice());
996                 }
997             }
998             _ => {}
999         }
1000     }
1001
1002     fn check_pat(&mut self, cx: &Context, p: &ast::Pat) {
1003         // Lint for constants that look like binding identifiers (#7526)
1004         match (&p.node, cx.tcx.def_map.borrow().find(&p.id)) {
1005             (&ast::PatIdent(_, ref path1, _), Some(&def::DefStatic(_, false))) => {
1006                 let s = token::get_ident(path1.node);
1007                 if s.get().chars().any(|c| c.is_lowercase()) {
1008                     cx.span_lint(NON_UPPERCASE_STATICS, path1.span,
1009                         format!("static constant in pattern `{}` should have an uppercase \
1010                                  name such as `{}`",
1011                                 s.get(), s.get().chars().map(|c| c.to_uppercase())
1012                                     .collect::<String>().as_slice()).as_slice());
1013                 }
1014             }
1015             _ => {}
1016         }
1017     }
1018 }
1019
1020 declare_lint!(UNNECESSARY_PARENS, Warn,
1021               "`if`, `match`, `while` and `return` do not need parentheses")
1022
1023 pub struct UnnecessaryParens;
1024
1025 impl UnnecessaryParens {
1026     fn check_unnecessary_parens_core(&self, cx: &Context, value: &ast::Expr, msg: &str,
1027                                      struct_lit_needs_parens: bool) {
1028         match value.node {
1029             ast::ExprParen(ref inner) => {
1030                 let necessary = struct_lit_needs_parens && contains_exterior_struct_lit(&**inner);
1031                 if !necessary {
1032                     cx.span_lint(UNNECESSARY_PARENS, value.span,
1033                                  format!("unnecessary parentheses around {}",
1034                                          msg).as_slice())
1035                 }
1036             }
1037             _ => {}
1038         }
1039
1040         /// Expressions that syntactically contain an "exterior" struct
1041         /// literal i.e. not surrounded by any parens or other
1042         /// delimiters, e.g. `X { y: 1 }`, `X { y: 1 }.method()`, `foo
1043         /// == X { y: 1 }` and `X { y: 1 } == foo` all do, but `(X {
1044         /// y: 1 }) == foo` does not.
1045         fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
1046             match value.node {
1047                 ast::ExprStruct(..) => true,
1048
1049                 ast::ExprAssign(ref lhs, ref rhs) |
1050                 ast::ExprAssignOp(_, ref lhs, ref rhs) |
1051                 ast::ExprBinary(_, ref lhs, ref rhs) => {
1052                     // X { y: 1 } + X { y: 2 }
1053                     contains_exterior_struct_lit(&**lhs) ||
1054                         contains_exterior_struct_lit(&**rhs)
1055                 }
1056                 ast::ExprUnary(_, ref x) |
1057                 ast::ExprCast(ref x, _) |
1058                 ast::ExprField(ref x, _, _) |
1059                 ast::ExprIndex(ref x, _) => {
1060                     // &X { y: 1 }, X { y: 1 }.y
1061                     contains_exterior_struct_lit(&**x)
1062                 }
1063
1064                 ast::ExprMethodCall(_, _, ref exprs) => {
1065                     // X { y: 1 }.bar(...)
1066                     contains_exterior_struct_lit(&**exprs.get(0))
1067                 }
1068
1069                 _ => false
1070             }
1071         }
1072     }
1073 }
1074
1075 impl LintPass for UnnecessaryParens {
1076     fn get_lints(&self) -> LintArray {
1077         lint_array!(UNNECESSARY_PARENS)
1078     }
1079
1080     fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1081         let (value, msg, struct_lit_needs_parens) = match e.node {
1082             ast::ExprIf(cond, _, _) => (cond, "`if` condition", true),
1083             ast::ExprWhile(cond, _, _) => (cond, "`while` condition", true),
1084             ast::ExprMatch(head, _) => (head, "`match` head expression", true),
1085             ast::ExprRet(Some(value)) => (value, "`return` value", false),
1086             ast::ExprAssign(_, value) => (value, "assigned value", false),
1087             ast::ExprAssignOp(_, _, value) => (value, "assigned value", false),
1088             _ => return
1089         };
1090         self.check_unnecessary_parens_core(cx, &*value, msg, struct_lit_needs_parens);
1091     }
1092
1093     fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
1094         let (value, msg) = match s.node {
1095             ast::StmtDecl(decl, _) => match decl.node {
1096                 ast::DeclLocal(local) => match local.init {
1097                     Some(value) => (value, "assigned value"),
1098                     None => return
1099                 },
1100                 _ => return
1101             },
1102             _ => return
1103         };
1104         self.check_unnecessary_parens_core(cx, &*value, msg, false);
1105     }
1106 }
1107
1108 declare_lint!(UNUSED_UNSAFE, Warn,
1109               "unnecessary use of an `unsafe` block")
1110
1111 pub struct UnusedUnsafe;
1112
1113 impl LintPass for UnusedUnsafe {
1114     fn get_lints(&self) -> LintArray {
1115         lint_array!(UNUSED_UNSAFE)
1116     }
1117
1118     fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1119         match e.node {
1120             // Don't warn about generated blocks, that'll just pollute the output.
1121             ast::ExprBlock(ref blk) => {
1122                 if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
1123                     !cx.tcx.used_unsafe.borrow().contains(&blk.id) {
1124                     cx.span_lint(UNUSED_UNSAFE, blk.span, "unnecessary `unsafe` block");
1125                 }
1126             }
1127             _ => ()
1128         }
1129     }
1130 }
1131
1132 declare_lint!(UNSAFE_BLOCK, Allow,
1133               "usage of an `unsafe` block")
1134
1135 pub struct UnsafeBlock;
1136
1137 impl LintPass for UnsafeBlock {
1138     fn get_lints(&self) -> LintArray {
1139         lint_array!(UNSAFE_BLOCK)
1140     }
1141
1142     fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1143         match e.node {
1144             // Don't warn about generated blocks, that'll just pollute the output.
1145             ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => {
1146                 cx.span_lint(UNSAFE_BLOCK, blk.span, "usage of an `unsafe` block");
1147             }
1148             _ => ()
1149         }
1150     }
1151 }
1152
1153 declare_lint!(pub UNUSED_MUT, Warn,
1154               "detect mut variables which don't need to be mutable")
1155
1156 pub struct UnusedMut;
1157
1158 impl UnusedMut {
1159     fn check_unused_mut_pat(&self, cx: &Context, pats: &[Gc<ast::Pat>]) {
1160         // collect all mutable pattern and group their NodeIDs by their Identifier to
1161         // avoid false warnings in match arms with multiple patterns
1162         let mut mutables = HashMap::new();
1163         for &p in pats.iter() {
1164             pat_util::pat_bindings(&cx.tcx.def_map, &*p, |mode, id, _, path1| {
1165                 let ident = path1.node;
1166                 match mode {
1167                     ast::BindByValue(ast::MutMutable) => {
1168                         if !token::get_ident(ident).get().starts_with("_") {
1169                             mutables.insert_or_update_with(ident.name.uint(),
1170                                 vec!(id), |_, old| { old.push(id); });
1171                         }
1172                     }
1173                     _ => {
1174                     }
1175                 }
1176             });
1177         }
1178
1179         let used_mutables = cx.tcx.used_mut_nodes.borrow();
1180         for (_, v) in mutables.iter() {
1181             if !v.iter().any(|e| used_mutables.contains(e)) {
1182                 cx.span_lint(UNUSED_MUT, cx.tcx.map.span(*v.get(0)),
1183                              "variable does not need to be mutable");
1184             }
1185         }
1186     }
1187 }
1188
1189 impl LintPass for UnusedMut {
1190     fn get_lints(&self) -> LintArray {
1191         lint_array!(UNUSED_MUT)
1192     }
1193
1194     fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1195         match e.node {
1196             ast::ExprMatch(_, ref arms) => {
1197                 for a in arms.iter() {
1198                     self.check_unused_mut_pat(cx, a.pats.as_slice())
1199                 }
1200             }
1201             _ => {}
1202         }
1203     }
1204
1205     fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
1206         match s.node {
1207             ast::StmtDecl(d, _) => {
1208                 match d.node {
1209                     ast::DeclLocal(l) => {
1210                         self.check_unused_mut_pat(cx, &[l.pat]);
1211                     },
1212                     _ => {}
1213                 }
1214             },
1215             _ => {}
1216         }
1217     }
1218
1219     fn check_fn(&mut self, cx: &Context,
1220                 _: &visit::FnKind, decl: &ast::FnDecl,
1221                 _: &ast::Block, _: Span, _: ast::NodeId) {
1222         for a in decl.inputs.iter() {
1223             self.check_unused_mut_pat(cx, &[a.pat]);
1224         }
1225     }
1226 }
1227
1228 enum Allocation {
1229     VectorAllocation,
1230     BoxAllocation
1231 }
1232
1233 declare_lint!(UNNECESSARY_ALLOCATION, Warn,
1234               "detects unnecessary allocations that can be eliminated")
1235
1236 pub struct UnnecessaryAllocation;
1237
1238 impl LintPass for UnnecessaryAllocation {
1239     fn get_lints(&self) -> LintArray {
1240         lint_array!(UNNECESSARY_ALLOCATION)
1241     }
1242
1243     fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1244         // Warn if boxing expressions are immediately borrowed.
1245         let allocation = match e.node {
1246             ast::ExprUnary(ast::UnUniq, _) |
1247             ast::ExprUnary(ast::UnBox, _) => BoxAllocation,
1248
1249             _ => return
1250         };
1251
1252         match cx.tcx.adjustments.borrow().find(&e.id) {
1253             Some(adjustment) => {
1254                 match *adjustment {
1255                     ty::AutoDerefRef(ty::AutoDerefRef { ref autoref, .. }) => {
1256                         match (allocation, autoref) {
1257                             (VectorAllocation, &Some(ty::AutoPtr(_, _, None))) => {
1258                                 cx.span_lint(UNNECESSARY_ALLOCATION, e.span,
1259                                              "unnecessary allocation, the sigil can be removed");
1260                             }
1261                             (BoxAllocation,
1262                              &Some(ty::AutoPtr(_, ast::MutImmutable, None))) => {
1263                                 cx.span_lint(UNNECESSARY_ALLOCATION, e.span,
1264                                              "unnecessary allocation, use & instead");
1265                             }
1266                             (BoxAllocation,
1267                              &Some(ty::AutoPtr(_, ast::MutMutable, None))) => {
1268                                 cx.span_lint(UNNECESSARY_ALLOCATION, e.span,
1269                                              "unnecessary allocation, use &mut instead");
1270                             }
1271                             _ => ()
1272                         }
1273                     }
1274                     _ => {}
1275                 }
1276             }
1277             _ => ()
1278         }
1279     }
1280 }
1281
1282 declare_lint!(MISSING_DOC, Allow,
1283               "detects missing documentation for public members")
1284
1285 pub struct MissingDoc {
1286     /// Stack of IDs of struct definitions.
1287     struct_def_stack: Vec<ast::NodeId>,
1288
1289     /// Stack of whether #[doc(hidden)] is set
1290     /// at each level which has lint attributes.
1291     doc_hidden_stack: Vec<bool>,
1292 }
1293
1294 impl MissingDoc {
1295     pub fn new() -> MissingDoc {
1296         MissingDoc {
1297             struct_def_stack: vec!(),
1298             doc_hidden_stack: vec!(false),
1299         }
1300     }
1301
1302     fn doc_hidden(&self) -> bool {
1303         *self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
1304     }
1305
1306     fn check_missing_doc_attrs(&self,
1307                                cx: &Context,
1308                                id: Option<ast::NodeId>,
1309                                attrs: &[ast::Attribute],
1310                                sp: Span,
1311                                desc: &'static str) {
1312         // If we're building a test harness, then warning about
1313         // documentation is probably not really relevant right now.
1314         if cx.sess().opts.test { return }
1315
1316         // `#[doc(hidden)]` disables missing_doc check.
1317         if self.doc_hidden() { return }
1318
1319         // Only check publicly-visible items, using the result from the privacy pass.
1320         // It's an option so the crate root can also use this function (it doesn't
1321         // have a NodeId).
1322         match id {
1323             Some(ref id) if !cx.exported_items.contains(id) => return,
1324             _ => ()
1325         }
1326
1327         let has_doc = attrs.iter().any(|a| {
1328             match a.node.value.node {
1329                 ast::MetaNameValue(ref name, _) if name.equiv(&("doc")) => true,
1330                 _ => false
1331             }
1332         });
1333         if !has_doc {
1334             cx.span_lint(MISSING_DOC, sp,
1335                 format!("missing documentation for {}", desc).as_slice());
1336         }
1337     }
1338 }
1339
1340 impl LintPass for MissingDoc {
1341     fn get_lints(&self) -> LintArray {
1342         lint_array!(MISSING_DOC)
1343     }
1344
1345     fn enter_lint_attrs(&mut self, _: &Context, attrs: &[ast::Attribute]) {
1346         let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| {
1347             attr.check_name("doc") && match attr.meta_item_list() {
1348                 None => false,
1349                 Some(l) => attr::contains_name(l.as_slice(), "hidden"),
1350             }
1351         });
1352         self.doc_hidden_stack.push(doc_hidden);
1353     }
1354
1355     fn exit_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) {
1356         self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
1357     }
1358
1359     fn check_struct_def(&mut self, _: &Context,
1360         _: &ast::StructDef, _: ast::Ident, _: &ast::Generics, id: ast::NodeId) {
1361         self.struct_def_stack.push(id);
1362     }
1363
1364     fn check_struct_def_post(&mut self, _: &Context,
1365         _: &ast::StructDef, _: ast::Ident, _: &ast::Generics, id: ast::NodeId) {
1366         let popped = self.struct_def_stack.pop().expect("empty struct_def_stack");
1367         assert!(popped == id);
1368     }
1369
1370     fn check_crate(&mut self, cx: &Context, krate: &ast::Crate) {
1371         self.check_missing_doc_attrs(cx, None, krate.attrs.as_slice(),
1372                                      krate.span, "crate");
1373     }
1374
1375     fn check_item(&mut self, cx: &Context, it: &ast::Item) {
1376         let desc = match it.node {
1377             ast::ItemFn(..) => "a function",
1378             ast::ItemMod(..) => "a module",
1379             ast::ItemEnum(..) => "an enum",
1380             ast::ItemStruct(..) => "a struct",
1381             ast::ItemTrait(..) => "a trait",
1382             _ => return
1383         };
1384         self.check_missing_doc_attrs(cx, Some(it.id), it.attrs.as_slice(),
1385                                      it.span, desc);
1386     }
1387
1388     fn check_fn(&mut self, cx: &Context,
1389             fk: &visit::FnKind, _: &ast::FnDecl,
1390             _: &ast::Block, _: Span, _: ast::NodeId) {
1391         match *fk {
1392             visit::FkMethod(_, _, m) => {
1393                 // If the method is an impl for a trait, don't doc.
1394                 if method_context(cx, m) == TraitImpl { return; }
1395
1396                 // Otherwise, doc according to privacy. This will also check
1397                 // doc for default methods defined on traits.
1398                 self.check_missing_doc_attrs(cx, Some(m.id), m.attrs.as_slice(),
1399                                              m.span, "a method");
1400             }
1401             _ => {}
1402         }
1403     }
1404
1405     fn check_ty_method(&mut self, cx: &Context, tm: &ast::TypeMethod) {
1406         self.check_missing_doc_attrs(cx, Some(tm.id), tm.attrs.as_slice(),
1407                                      tm.span, "a type method");
1408     }
1409
1410     fn check_struct_field(&mut self, cx: &Context, sf: &ast::StructField) {
1411         match sf.node.kind {
1412             ast::NamedField(_, vis) if vis == ast::Public => {
1413                 let cur_struct_def = *self.struct_def_stack.last()
1414                     .expect("empty struct_def_stack");
1415                 self.check_missing_doc_attrs(cx, Some(cur_struct_def),
1416                                              sf.node.attrs.as_slice(), sf.span,
1417                                              "a struct field")
1418             }
1419             _ => {}
1420         }
1421     }
1422
1423     fn check_variant(&mut self, cx: &Context, v: &ast::Variant, _: &ast::Generics) {
1424         self.check_missing_doc_attrs(cx, Some(v.node.id), v.node.attrs.as_slice(),
1425                                      v.span, "a variant");
1426     }
1427 }
1428
1429 declare_lint!(DEPRECATED, Warn,
1430               "detects use of #[deprecated] items")
1431
1432 // FIXME #6875: Change to Warn after std library stabilization is complete
1433 declare_lint!(EXPERIMENTAL, Allow,
1434               "detects use of #[experimental] items")
1435
1436 declare_lint!(UNSTABLE, Allow,
1437               "detects use of #[unstable] items (incl. items with no stability attribute)")
1438
1439 /// Checks for use of items with `#[deprecated]`, `#[experimental]` and
1440 /// `#[unstable]` attributes, or no stability attribute.
1441 pub struct Stability;
1442
1443 impl LintPass for Stability {
1444     fn get_lints(&self) -> LintArray {
1445         lint_array!(DEPRECATED, EXPERIMENTAL, UNSTABLE)
1446     }
1447
1448     fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
1449         // if the expression was produced by a macro expansion,
1450         if e.span.expn_info.is_some() { return }
1451
1452         let id = match e.node {
1453             ast::ExprPath(..) | ast::ExprStruct(..) => {
1454                 match cx.tcx.def_map.borrow().find(&e.id) {
1455                     Some(&def) => def.def_id(),
1456                     None => return
1457                 }
1458             }
1459             ast::ExprMethodCall(..) => {
1460                 let method_call = typeck::MethodCall::expr(e.id);
1461                 match cx.tcx.method_map.borrow().find(&method_call) {
1462                     Some(method) => {
1463                         match method.origin {
1464                             typeck::MethodStatic(def_id) => {
1465                                 def_id
1466                             }
1467                             typeck::MethodStaticUnboxedClosure(def_id) => {
1468                                 def_id
1469                             }
1470                             typeck::MethodParam(typeck::MethodParam {
1471                                 trait_id: trait_id,
1472                                 method_num: index,
1473                                 ..
1474                             })
1475                             | typeck::MethodObject(typeck::MethodObject {
1476                                 trait_id: trait_id,
1477                                 method_num: index,
1478                                 ..
1479                             }) => {
1480                                 match ty::trait_item(cx.tcx,
1481                                                      trait_id,
1482                                                      index) {
1483                                     ty::MethodTraitItem(method) => {
1484                                         method.def_id
1485                                     }
1486                                 }
1487                             }
1488                         }
1489                     }
1490                     None => return
1491                 }
1492             }
1493             _ => return
1494         };
1495
1496         let stability = stability::lookup(cx.tcx, id);
1497         let cross_crate = !ast_util::is_local(id);
1498
1499         // stability attributes are promises made across crates; only
1500         // check DEPRECATED for crate-local usage.
1501         let (lint, label) = match stability {
1502             // no stability attributes == Unstable
1503             None if cross_crate => (UNSTABLE, "unmarked"),
1504             Some(attr::Stability { level: attr::Unstable, .. }) if cross_crate =>
1505                 (UNSTABLE, "unstable"),
1506             Some(attr::Stability { level: attr::Experimental, .. }) if cross_crate =>
1507                 (EXPERIMENTAL, "experimental"),
1508             Some(attr::Stability { level: attr::Deprecated, .. }) =>
1509                 (DEPRECATED, "deprecated"),
1510             _ => return
1511         };
1512
1513         let msg = match stability {
1514             Some(attr::Stability { text: Some(ref s), .. }) => {
1515                 format!("use of {} item: {}", label, *s)
1516             }
1517             _ => format!("use of {} item", label)
1518         };
1519
1520         cx.span_lint(lint, e.span, msg.as_slice());
1521     }
1522 }
1523
1524 declare_lint!(pub UNUSED_IMPORTS, Warn,
1525               "imports that are never used")
1526
1527 declare_lint!(pub UNNECESSARY_QUALIFICATION, Allow,
1528               "detects unnecessarily qualified names")
1529
1530 declare_lint!(pub UNRECOGNIZED_LINT, Warn,
1531               "unrecognized lint attribute")
1532
1533 declare_lint!(pub UNUSED_VARIABLE, Warn,
1534               "detect variables which are not used in any way")
1535
1536 declare_lint!(pub DEAD_ASSIGNMENT, Warn,
1537               "detect assignments that will never be read")
1538
1539 declare_lint!(pub DEAD_CODE, Warn,
1540               "detect piece of code that will never be used")
1541
1542 declare_lint!(pub VISIBLE_PRIVATE_TYPES, Warn,
1543               "detect use of private types in exported type signatures")
1544
1545 declare_lint!(pub UNREACHABLE_CODE, Warn,
1546               "detects unreachable code")
1547
1548 declare_lint!(pub WARNINGS, Warn,
1549               "mass-change the level for lints which produce warnings")
1550
1551 declare_lint!(pub UNKNOWN_FEATURES, Deny,
1552               "unknown features found in crate-level #[feature] directives")
1553
1554 declare_lint!(pub UNKNOWN_CRATE_TYPE, Deny,
1555               "unknown crate type found in #[crate_type] directive")
1556
1557 declare_lint!(pub VARIANT_SIZE_DIFFERENCE, Allow,
1558               "detects enums with widely varying variant sizes")
1559
1560 declare_lint!(pub TRANSMUTE_FAT_PTR, Allow,
1561               "detects transmutes of fat pointers")
1562
1563 /// Does nothing as a lint pass, but registers some `Lint`s
1564 /// which are used by other parts of the compiler.
1565 pub struct HardwiredLints;
1566
1567 impl LintPass for HardwiredLints {
1568     fn get_lints(&self) -> LintArray {
1569         lint_array!(
1570             UNUSED_IMPORTS,
1571             UNNECESSARY_QUALIFICATION,
1572             UNRECOGNIZED_LINT,
1573             UNUSED_VARIABLE,
1574             DEAD_ASSIGNMENT,
1575             DEAD_CODE,
1576             VISIBLE_PRIVATE_TYPES,
1577             UNREACHABLE_CODE,
1578             WARNINGS,
1579             UNKNOWN_FEATURES,
1580             UNKNOWN_CRATE_TYPE,
1581             VARIANT_SIZE_DIFFERENCE
1582         )
1583     }
1584 }