]> git.lizzy.rs Git - rust.git/blob - src/librustc/lint/builtin.rs
Move lint infrastructure and individual lints into separate files
[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 use metadata::csearch;
14 use middle::def;
15 use middle::def::*;
16 use middle::pat_util;
17 use middle::trans::adt; // for `adt::is_ffi_safe`
18 use middle::ty;
19 use middle::typeck::astconv::{ast_ty_to_ty, AstConv};
20 use middle::typeck::infer;
21 use middle::typeck;
22 use util::ppaux::{ty_to_str};
23 use lint::Context;
24 use lint;
25
26 use std::cmp;
27 use std::collections::HashMap;
28 use std::i16;
29 use std::i32;
30 use std::i64;
31 use std::i8;
32 use std::u16;
33 use std::u32;
34 use std::u64;
35 use std::u8;
36 use syntax::abi;
37 use syntax::ast_map;
38 use syntax::attr::AttrMetaMethods;
39 use syntax::attr;
40 use syntax::codemap::Span;
41 use syntax::parse::token;
42 use syntax::visit::Visitor;
43 use syntax::{ast, ast_util, visit};
44
45 pub fn check_while_true_expr(cx: &Context, e: &ast::Expr) {
46     match e.node {
47         ast::ExprWhile(cond, _) => {
48             match cond.node {
49                 ast::ExprLit(lit) => {
50                     match lit.node {
51                         ast::LitBool(true) => {
52                             cx.span_lint(lint::WhileTrue,
53                                          e.span,
54                                          "denote infinite loops with loop \
55                                           { ... }");
56                         }
57                         _ => {}
58                     }
59                 }
60                 _ => ()
61             }
62         }
63         _ => ()
64     }
65 }
66
67 pub fn check_unused_casts(cx: &Context, e: &ast::Expr) {
68     return match e.node {
69         ast::ExprCast(expr, ty) => {
70             let t_t = ast_ty_to_ty(cx, &infer::new_infer_ctxt(cx.tcx), ty);
71             if  ty::get(ty::expr_ty(cx.tcx, expr)).sty == ty::get(t_t).sty {
72                 cx.span_lint(lint::UnnecessaryTypecast, ty.span,
73                              "unnecessary type cast");
74             }
75         }
76         _ => ()
77     };
78 }
79
80 pub fn check_type_limits(cx: &Context, e: &ast::Expr) {
81     return match e.node {
82         ast::ExprUnary(ast::UnNeg, ex) => {
83             match ex.node  {
84                 ast::ExprLit(lit) => {
85                     match lit.node {
86                         ast::LitUint(..) => {
87                             cx.span_lint(lint::UnsignedNegate, e.span,
88                                          "negation of unsigned int literal may be unintentional");
89                         },
90                         _ => ()
91                     }
92                 },
93                 _ => {
94                     let t = ty::expr_ty(cx.tcx, ex);
95                     match ty::get(t).sty {
96                         ty::ty_uint(_) => {
97                             cx.span_lint(lint::UnsignedNegate, e.span,
98                                          "negation of unsigned int variable may be unintentional");
99                         },
100                         _ => ()
101                     }
102                 }
103             }
104         },
105         ast::ExprBinary(binop, l, r) => {
106             if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) {
107                 cx.span_lint(lint::TypeLimits, e.span,
108                              "comparison is useless due to type limits");
109             }
110         },
111         ast::ExprLit(lit) => {
112             match ty::get(ty::expr_ty(cx.tcx, e)).sty {
113                 ty::ty_int(t) => {
114                     let int_type = if t == ast::TyI {
115                         cx.tcx.sess.targ_cfg.int_type
116                     } else { t };
117                     let (min, max) = int_ty_range(int_type);
118                     let mut lit_val: i64 = match lit.node {
119                         ast::LitInt(v, _) => v,
120                         ast::LitUint(v, _) => v as i64,
121                         ast::LitIntUnsuffixed(v) => v,
122                         _ => fail!()
123                     };
124                     if cx.negated_expr_id == e.id {
125                         lit_val *= -1;
126                     }
127                     if  lit_val < min || lit_val > max {
128                         cx.span_lint(lint::TypeOverflow, e.span,
129                                      "literal out of range for its type");
130                     }
131                 },
132                 ty::ty_uint(t) => {
133                     let uint_type = if t == ast::TyU {
134                         cx.tcx.sess.targ_cfg.uint_type
135                     } else { t };
136                     let (min, max) = uint_ty_range(uint_type);
137                     let lit_val: u64 = match lit.node {
138                         ast::LitInt(v, _) => v as u64,
139                         ast::LitUint(v, _) => v,
140                         ast::LitIntUnsuffixed(v) => v as u64,
141                         _ => fail!()
142                     };
143                     if  lit_val < min || lit_val > max {
144                         cx.span_lint(lint::TypeOverflow, e.span,
145                                      "literal out of range for its type");
146                     }
147                 },
148
149                 _ => ()
150             };
151         },
152         _ => ()
153     };
154
155     fn is_valid<T:cmp::PartialOrd>(binop: ast::BinOp, v: T,
156                             min: T, max: T) -> bool {
157         match binop {
158             ast::BiLt => v >  min && v <= max,
159             ast::BiLe => v >= min && v <  max,
160             ast::BiGt => v >= min && v <  max,
161             ast::BiGe => v >  min && v <= max,
162             ast::BiEq | ast::BiNe => v >= min && v <= max,
163             _ => fail!()
164         }
165     }
166
167     fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
168         match binop {
169             ast::BiLt => ast::BiGt,
170             ast::BiLe => ast::BiGe,
171             ast::BiGt => ast::BiLt,
172             ast::BiGe => ast::BiLe,
173             _ => binop
174         }
175     }
176
177     // for int & uint, be conservative with the warnings, so that the
178     // warnings are consistent between 32- and 64-bit platforms
179     fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) {
180         match int_ty {
181             ast::TyI =>    (i64::MIN,        i64::MAX),
182             ast::TyI8 =>   (i8::MIN  as i64, i8::MAX  as i64),
183             ast::TyI16 =>  (i16::MIN as i64, i16::MAX as i64),
184             ast::TyI32 =>  (i32::MIN as i64, i32::MAX as i64),
185             ast::TyI64 =>  (i64::MIN,        i64::MAX)
186         }
187     }
188
189     fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
190         match uint_ty {
191             ast::TyU =>   (u64::MIN,         u64::MAX),
192             ast::TyU8 =>  (u8::MIN   as u64, u8::MAX   as u64),
193             ast::TyU16 => (u16::MIN  as u64, u16::MAX  as u64),
194             ast::TyU32 => (u32::MIN  as u64, u32::MAX  as u64),
195             ast::TyU64 => (u64::MIN,         u64::MAX)
196         }
197     }
198
199     fn check_limits(tcx: &ty::ctxt, binop: ast::BinOp,
200                     l: &ast::Expr, r: &ast::Expr) -> bool {
201         let (lit, expr, swap) = match (&l.node, &r.node) {
202             (&ast::ExprLit(_), _) => (l, r, true),
203             (_, &ast::ExprLit(_)) => (r, l, false),
204             _ => return true
205         };
206         // Normalize the binop so that the literal is always on the RHS in
207         // the comparison
208         let norm_binop = if swap { rev_binop(binop) } else { binop };
209         match ty::get(ty::expr_ty(tcx, expr)).sty {
210             ty::ty_int(int_ty) => {
211                 let (min, max) = int_ty_range(int_ty);
212                 let lit_val: i64 = match lit.node {
213                     ast::ExprLit(li) => match li.node {
214                         ast::LitInt(v, _) => v,
215                         ast::LitUint(v, _) => v as i64,
216                         ast::LitIntUnsuffixed(v) => v,
217                         _ => return true
218                     },
219                     _ => fail!()
220                 };
221                 is_valid(norm_binop, lit_val, min, max)
222             }
223             ty::ty_uint(uint_ty) => {
224                 let (min, max): (u64, u64) = uint_ty_range(uint_ty);
225                 let lit_val: u64 = match lit.node {
226                     ast::ExprLit(li) => match li.node {
227                         ast::LitInt(v, _) => v as u64,
228                         ast::LitUint(v, _) => v,
229                         ast::LitIntUnsuffixed(v) => v as u64,
230                         _ => return true
231                     },
232                     _ => fail!()
233                 };
234                 is_valid(norm_binop, lit_val, min, max)
235             }
236             _ => true
237         }
238     }
239
240     fn is_comparison(binop: ast::BinOp) -> bool {
241         match binop {
242             ast::BiEq | ast::BiLt | ast::BiLe |
243             ast::BiNe | ast::BiGe | ast::BiGt => true,
244             _ => false
245         }
246     }
247 }
248
249 pub fn check_item_ctypes(cx: &Context, it: &ast::Item) {
250     fn check_ty(cx: &Context, ty: &ast::Ty) {
251         match ty.node {
252             ast::TyPath(_, _, id) => {
253                 match cx.tcx.def_map.borrow().get_copy(&id) {
254                     def::DefPrimTy(ast::TyInt(ast::TyI)) => {
255                         cx.span_lint(lint::CTypes, ty.span,
256                                 "found rust type `int` in foreign module, while \
257                                 libc::c_int or libc::c_long should be used");
258                     }
259                     def::DefPrimTy(ast::TyUint(ast::TyU)) => {
260                         cx.span_lint(lint::CTypes, ty.span,
261                                 "found rust type `uint` in foreign module, while \
262                                 libc::c_uint or libc::c_ulong should be used");
263                     }
264                     def::DefTy(def_id) => {
265                         if !adt::is_ffi_safe(cx.tcx, def_id) {
266                             cx.span_lint(lint::CTypes, ty.span,
267                                          "found enum type without foreign-function-safe \
268                                           representation annotation in foreign module");
269                             // hmm... this message could be more helpful
270                         }
271                     }
272                     _ => ()
273                 }
274             }
275             ast::TyPtr(ref mt) => { check_ty(cx, mt.ty) }
276             _ => {}
277         }
278     }
279
280     fn check_foreign_fn(cx: &Context, decl: &ast::FnDecl) {
281         for input in decl.inputs.iter() {
282             check_ty(cx, input.ty);
283         }
284         check_ty(cx, decl.output)
285     }
286
287     match it.node {
288       ast::ItemForeignMod(ref nmod) if nmod.abi != abi::RustIntrinsic => {
289         for ni in nmod.items.iter() {
290             match ni.node {
291                 ast::ForeignItemFn(decl, _) => check_foreign_fn(cx, decl),
292                 ast::ForeignItemStatic(t, _) => check_ty(cx, t)
293             }
294         }
295       }
296       _ => {/* nothing to do */ }
297     }
298 }
299
300 pub fn check_heap_type(cx: &Context, span: Span, ty: ty::t) {
301     let xs = [lint::ManagedHeapMemory, lint::OwnedHeapMemory, lint::HeapMemory];
302     for &lint in xs.iter() {
303         if cx.get_level(lint) == lint::Allow { continue }
304
305         let mut n_box = 0;
306         let mut n_uniq = 0;
307         ty::fold_ty(cx.tcx, ty, |t| {
308             match ty::get(t).sty {
309                 ty::ty_box(_) => {
310                     n_box += 1;
311                 }
312                 ty::ty_uniq(_) |
313                 ty::ty_trait(box ty::TyTrait {
314                     store: ty::UniqTraitStore, ..
315                 }) |
316                 ty::ty_closure(box ty::ClosureTy {
317                     store: ty::UniqTraitStore,
318                     ..
319                 }) => {
320                     n_uniq += 1;
321                 }
322
323                 _ => ()
324             };
325             t
326         });
327
328         if n_uniq > 0 && lint != lint::ManagedHeapMemory {
329             let s = ty_to_str(cx.tcx, ty);
330             let m = format!("type uses owned (Box type) pointers: {}", s);
331             cx.span_lint(lint, span, m.as_slice());
332         }
333
334         if n_box > 0 && lint != lint::OwnedHeapMemory {
335             let s = ty_to_str(cx.tcx, ty);
336             let m = format!("type uses managed (@ type) pointers: {}", s);
337             cx.span_lint(lint, span, m.as_slice());
338         }
339     }
340 }
341
342 pub fn check_heap_item(cx: &Context, it: &ast::Item) {
343     match it.node {
344         ast::ItemFn(..) |
345         ast::ItemTy(..) |
346         ast::ItemEnum(..) |
347         ast::ItemStruct(..) => check_heap_type(cx, it.span,
348                                                ty::node_id_to_type(cx.tcx,
349                                                                    it.id)),
350         _ => ()
351     }
352
353     // If it's a struct, we also have to check the fields' types
354     match it.node {
355         ast::ItemStruct(struct_def, _) => {
356             for struct_field in struct_def.fields.iter() {
357                 check_heap_type(cx, struct_field.span,
358                                 ty::node_id_to_type(cx.tcx,
359                                                     struct_field.node.id));
360             }
361         }
362         _ => ()
363     }
364 }
365
366 struct RawPtrDerivingVisitor<'a> {
367     cx: &'a Context<'a>
368 }
369
370 impl<'a> Visitor<()> for RawPtrDerivingVisitor<'a> {
371     fn visit_ty(&mut self, ty: &ast::Ty, _: ()) {
372         static MSG: &'static str = "use of `#[deriving]` with a raw pointer";
373         match ty.node {
374             ast::TyPtr(..) => self.cx.span_lint(lint::RawPointerDeriving, ty.span, MSG),
375             _ => {}
376         }
377         visit::walk_ty(self, ty, ());
378     }
379     // explicit override to a no-op to reduce code bloat
380     fn visit_expr(&mut self, _: &ast::Expr, _: ()) {}
381     fn visit_block(&mut self, _: &ast::Block, _: ()) {}
382 }
383
384 pub fn check_raw_ptr_deriving(cx: &mut Context, item: &ast::Item) {
385     if !attr::contains_name(item.attrs.as_slice(), "automatically_derived") {
386         return
387     }
388     let did = match item.node {
389         ast::ItemImpl(..) => {
390             match ty::get(ty::node_id_to_type(cx.tcx, item.id)).sty {
391                 ty::ty_enum(did, _) => did,
392                 ty::ty_struct(did, _) => did,
393                 _ => return,
394             }
395         }
396         _ => return,
397     };
398     if !ast_util::is_local(did) { return }
399     let item = match cx.tcx.map.find(did.node) {
400         Some(ast_map::NodeItem(item)) => item,
401         _ => return,
402     };
403     if !cx.checked_raw_pointers.insert(item.id) { return }
404     match item.node {
405         ast::ItemStruct(..) | ast::ItemEnum(..) => {
406             let mut visitor = RawPtrDerivingVisitor { cx: cx };
407             visit::walk_item(&mut visitor, item, ());
408         }
409         _ => {}
410     }
411 }
412
413 pub fn check_unused_attribute(cx: &Context, attr: &ast::Attribute) {
414     static ATTRIBUTE_WHITELIST: &'static [&'static str] = &'static [
415         // FIXME: #14408 whitelist docs since rustdoc looks at them
416         "doc",
417
418         // FIXME: #14406 these are processed in trans, which happens after the
419         // lint pass
420         "address_insignificant",
421         "cold",
422         "inline",
423         "link",
424         "link_name",
425         "link_section",
426         "no_builtins",
427         "no_mangle",
428         "no_split_stack",
429         "packed",
430         "static_assert",
431         "thread_local",
432
433         // not used anywhere (!?) but apparently we want to keep them around
434         "comment",
435         "desc",
436         "license",
437
438         // FIXME: #14407 these are only looked at on-demand so we can't
439         // guarantee they'll have already been checked
440         "deprecated",
441         "experimental",
442         "frozen",
443         "locked",
444         "must_use",
445         "stable",
446         "unstable",
447     ];
448
449     static CRATE_ATTRS: &'static [&'static str] = &'static [
450         "crate_type",
451         "feature",
452         "no_start",
453         "no_main",
454         "no_std",
455         "crate_id",
456         "desc",
457         "comment",
458         "license",
459         "copyright",
460         "no_builtins",
461     ];
462
463     for &name in ATTRIBUTE_WHITELIST.iter() {
464         if attr.check_name(name) {
465             break;
466         }
467     }
468
469     if !attr::is_used(attr) {
470         cx.span_lint(lint::UnusedAttribute, attr.span, "unused attribute");
471         if CRATE_ATTRS.contains(&attr.name().get()) {
472             let msg = match attr.node.style {
473                ast::AttrOuter => "crate-level attribute should be an inner \
474                                   attribute: add an exclamation mark: #![foo]",
475                 ast::AttrInner => "crate-level attribute should be in the \
476                                    root module",
477             };
478             cx.span_lint(lint::UnusedAttribute, attr.span, msg);
479         }
480     }
481 }
482
483 pub fn check_heap_expr(cx: &Context, e: &ast::Expr) {
484     let ty = ty::expr_ty(cx.tcx, e);
485     check_heap_type(cx, e.span, ty);
486 }
487
488 pub fn check_path_statement(cx: &Context, s: &ast::Stmt) {
489     match s.node {
490         ast::StmtSemi(expr, _) => {
491             match expr.node {
492                 ast::ExprPath(_) => {
493                     cx.span_lint(lint::PathStatement,
494                                  s.span,
495                                  "path statement with no effect");
496                 }
497                 _ => {}
498             }
499         }
500         _ => ()
501     }
502 }
503
504 pub fn check_unused_result(cx: &Context, s: &ast::Stmt) {
505     let expr = match s.node {
506         ast::StmtSemi(expr, _) => expr,
507         _ => return
508     };
509     let t = ty::expr_ty(cx.tcx, expr);
510     match ty::get(t).sty {
511         ty::ty_nil | ty::ty_bot | ty::ty_bool => return,
512         _ => {}
513     }
514     match expr.node {
515         ast::ExprRet(..) => return,
516         _ => {}
517     }
518
519     let t = ty::expr_ty(cx.tcx, expr);
520     let mut warned = false;
521     match ty::get(t).sty {
522         ty::ty_struct(did, _) |
523         ty::ty_enum(did, _) => {
524             if ast_util::is_local(did) {
525                 match cx.tcx.map.get(did.node) {
526                     ast_map::NodeItem(it) => {
527                         if attr::contains_name(it.attrs.as_slice(),
528                                                "must_use") {
529                             cx.span_lint(lint::UnusedMustUse, s.span,
530                                          "unused result which must be used");
531                             warned = true;
532                         }
533                     }
534                     _ => {}
535                 }
536             } else {
537                 csearch::get_item_attrs(&cx.tcx.sess.cstore, did, |attrs| {
538                     if attr::contains_name(attrs.as_slice(), "must_use") {
539                         cx.span_lint(lint::UnusedMustUse, s.span,
540                                      "unused result which must be used");
541                         warned = true;
542                     }
543                 });
544             }
545         }
546         _ => {}
547     }
548     if !warned {
549         cx.span_lint(lint::UnusedResult, s.span, "unused result");
550     }
551 }
552
553 pub fn check_deprecated_owned_vector(cx: &Context, e: &ast::Expr) {
554     let t = ty::expr_ty(cx.tcx, e);
555     match ty::get(t).sty {
556         ty::ty_uniq(t) => match ty::get(t).sty {
557             ty::ty_vec(_, None) => {
558                 cx.span_lint(lint::DeprecatedOwnedVector, e.span,
559                              "use of deprecated `~[]` vector; replaced by `std::vec::Vec`")
560             }
561             _ => {}
562         },
563         _ => {}
564     }
565 }
566
567 pub fn check_item_non_camel_case_types(cx: &Context, it: &ast::Item) {
568     fn is_camel_case(ident: ast::Ident) -> bool {
569         let ident = token::get_ident(ident);
570         assert!(!ident.get().is_empty());
571         let ident = ident.get().trim_chars('_');
572
573         // start with a non-lowercase letter rather than non-uppercase
574         // ones (some scripts don't have a concept of upper/lowercase)
575         !ident.char_at(0).is_lowercase() && !ident.contains_char('_')
576     }
577
578     fn to_camel_case(s: &str) -> String {
579         s.split('_').flat_map(|word| word.chars().enumerate().map(|(i, c)|
580             if i == 0 { c.to_uppercase() }
581             else { c }
582         )).collect()
583     }
584
585     fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
586         let s = token::get_ident(ident);
587
588         if !is_camel_case(ident) {
589             cx.span_lint(lint::
590                 NonCamelCaseTypes, span,
591                 format!("{} `{}` should have a camel case name such as `{}`",
592                     sort, s, to_camel_case(s.get())).as_slice());
593         }
594     }
595
596     match it.node {
597         ast::ItemTy(..) | ast::ItemStruct(..) => {
598             check_case(cx, "type", it.ident, it.span)
599         }
600         ast::ItemTrait(..) => {
601             check_case(cx, "trait", it.ident, it.span)
602         }
603         ast::ItemEnum(ref enum_definition, _) => {
604             check_case(cx, "type", it.ident, it.span);
605             for variant in enum_definition.variants.iter() {
606                 check_case(cx, "variant", variant.node.name, variant.span);
607             }
608         }
609         _ => ()
610     }
611 }
612
613 pub fn check_snake_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
614     fn is_snake_case(ident: ast::Ident) -> bool {
615         let ident = token::get_ident(ident);
616         assert!(!ident.get().is_empty());
617         let ident = ident.get().trim_chars('_');
618
619         let mut allow_underscore = true;
620         ident.chars().all(|c| {
621             allow_underscore = match c {
622                 c if c.is_lowercase() || c.is_digit() => true,
623                 '_' if allow_underscore => false,
624                 _ => return false,
625             };
626             true
627         })
628     }
629
630     fn to_snake_case(str: &str) -> String {
631         let mut words = vec![];
632         for s in str.split('_') {
633             let mut buf = String::new();
634             if s.is_empty() { continue; }
635             for ch in s.chars() {
636                 if !buf.is_empty() && ch.is_uppercase() {
637                     words.push(buf);
638                     buf = String::new();
639                 }
640                 buf.push_char(ch.to_lowercase());
641             }
642             words.push(buf);
643         }
644         words.connect("_")
645     }
646
647     let s = token::get_ident(ident);
648
649     if !is_snake_case(ident) {
650         cx.span_lint(lint::NonSnakeCaseFunctions, span,
651             format!("{} `{}` should have a snake case name such as `{}`",
652                 sort, s, to_snake_case(s.get())).as_slice());
653     }
654 }
655
656 pub fn check_item_non_uppercase_statics(cx: &Context, it: &ast::Item) {
657     match it.node {
658         // only check static constants
659         ast::ItemStatic(_, ast::MutImmutable, _) => {
660             let s = token::get_ident(it.ident);
661             // check for lowercase letters rather than non-uppercase
662             // ones (some scripts don't have a concept of
663             // upper/lowercase)
664             if s.get().chars().any(|c| c.is_lowercase()) {
665                 cx.span_lint(lint::NonUppercaseStatics, it.span,
666                     format!("static constant `{}` should have an uppercase name \
667                         such as `{}`", s.get(),
668                         s.get().chars().map(|c| c.to_uppercase())
669                             .collect::<String>().as_slice()).as_slice());
670             }
671         }
672         _ => {}
673     }
674 }
675
676 pub fn check_pat_non_uppercase_statics(cx: &Context, p: &ast::Pat) {
677     // Lint for constants that look like binding identifiers (#7526)
678     match (&p.node, cx.tcx.def_map.borrow().find(&p.id)) {
679         (&ast::PatIdent(_, ref path, _), Some(&def::DefStatic(_, false))) => {
680             // last identifier alone is right choice for this lint.
681             let ident = path.segments.last().unwrap().identifier;
682             let s = token::get_ident(ident);
683             if s.get().chars().any(|c| c.is_lowercase()) {
684                 cx.span_lint(lint::NonUppercasePatternStatics, path.span,
685                     format!("static constant in pattern `{}` should have an uppercase \
686                         name such as `{}`", s.get(),
687                         s.get().chars().map(|c| c.to_uppercase())
688                             .collect::<String>().as_slice()).as_slice());
689             }
690         }
691         _ => {}
692     }
693 }
694
695 pub fn check_pat_uppercase_variable(cx: &Context, p: &ast::Pat) {
696     match &p.node {
697         &ast::PatIdent(_, ref path, _) => {
698             match cx.tcx.def_map.borrow().find(&p.id) {
699                 Some(&def::DefLocal(_, _)) | Some(&def::DefBinding(_, _)) |
700                         Some(&def::DefArg(_, _)) => {
701                     // last identifier alone is right choice for this lint.
702                     let ident = path.segments.last().unwrap().identifier;
703                     let s = token::get_ident(ident);
704                     if s.get().len() > 0 && s.get().char_at(0).is_uppercase() {
705                         cx.span_lint(lint::
706                             UppercaseVariables,
707                             path.span,
708                             "variable names should start with a lowercase character");
709                     }
710                 }
711                 _ => {}
712             }
713         }
714         _ => {}
715     }
716 }
717
718 pub fn check_struct_uppercase_variable(cx: &Context, s: &ast::StructDef) {
719     for sf in s.fields.iter() {
720         match sf.node {
721             ast::StructField_ { kind: ast::NamedField(ident, _), .. } => {
722                 let s = token::get_ident(ident);
723                 if s.get().char_at(0).is_uppercase() {
724                     cx.span_lint(lint::
725                         UppercaseVariables,
726                         sf.span,
727                         "structure field names should start with a lowercase character");
728                 }
729             }
730             _ => {}
731         }
732     }
733 }
734
735 pub fn check_unnecessary_parens_core(cx: &Context, value: &ast::Expr, msg: &str) {
736     match value.node {
737         ast::ExprParen(_) => {
738             cx.span_lint(lint::UnnecessaryParens, value.span,
739                          format!("unnecessary parentheses around {}",
740                                  msg).as_slice())
741         }
742         _ => {}
743     }
744 }
745
746 pub fn check_unnecessary_parens_expr(cx: &Context, e: &ast::Expr) {
747     let (value, msg) = match e.node {
748         ast::ExprIf(cond, _, _) => (cond, "`if` condition"),
749         ast::ExprWhile(cond, _) => (cond, "`while` condition"),
750         ast::ExprMatch(head, _) => (head, "`match` head expression"),
751         ast::ExprRet(Some(value)) => (value, "`return` value"),
752         ast::ExprAssign(_, value) => (value, "assigned value"),
753         ast::ExprAssignOp(_, _, value) => (value, "assigned value"),
754         _ => return
755     };
756     check_unnecessary_parens_core(cx, value, msg);
757 }
758
759 pub fn check_unnecessary_parens_stmt(cx: &Context, s: &ast::Stmt) {
760     let (value, msg) = match s.node {
761         ast::StmtDecl(decl, _) => match decl.node {
762             ast::DeclLocal(local) => match local.init {
763                 Some(value) => (value, "assigned value"),
764                 None => return
765             },
766             _ => return
767         },
768         _ => return
769     };
770     check_unnecessary_parens_core(cx, value, msg);
771 }
772
773 pub fn check_unused_unsafe(cx: &Context, e: &ast::Expr) {
774     match e.node {
775         // Don't warn about generated blocks, that'll just pollute the output.
776         ast::ExprBlock(ref blk) => {
777             if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
778                 !cx.tcx.used_unsafe.borrow().contains(&blk.id) {
779                 cx.span_lint(lint::UnusedUnsafe, blk.span,
780                              "unnecessary `unsafe` block");
781             }
782         }
783         _ => ()
784     }
785 }
786
787 pub fn check_unsafe_block(cx: &Context, e: &ast::Expr) {
788     match e.node {
789         // Don't warn about generated blocks, that'll just pollute the output.
790         ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => {
791             cx.span_lint(lint::UnsafeBlock, blk.span, "usage of an `unsafe` block");
792         }
793         _ => ()
794     }
795 }
796
797 pub fn check_unused_mut_pat(cx: &Context, pats: &[@ast::Pat]) {
798     // collect all mutable pattern and group their NodeIDs by their Identifier to
799     // avoid false warnings in match arms with multiple patterns
800     let mut mutables = HashMap::new();
801     for &p in pats.iter() {
802         pat_util::pat_bindings(&cx.tcx.def_map, p, |mode, id, _, path| {
803             match mode {
804                 ast::BindByValue(ast::MutMutable) => {
805                     if path.segments.len() != 1 {
806                         cx.tcx.sess.span_bug(p.span,
807                                              "mutable binding that doesn't consist \
808                                               of exactly one segment");
809                     }
810                     let ident = path.segments.get(0).identifier;
811                     if !token::get_ident(ident).get().starts_with("_") {
812                         mutables.insert_or_update_with(ident.name as uint, vec!(id), |_, old| {
813                             old.push(id);
814                         });
815                     }
816                 }
817                 _ => {
818                 }
819             }
820         });
821     }
822
823     let used_mutables = cx.tcx.used_mut_nodes.borrow();
824     for (_, v) in mutables.iter() {
825         if !v.iter().any(|e| used_mutables.contains(e)) {
826             cx.span_lint(lint::UnusedMut, cx.tcx.map.span(*v.get(0)),
827                          "variable does not need to be mutable");
828         }
829     }
830 }
831
832 enum Allocation {
833     VectorAllocation,
834     BoxAllocation
835 }
836
837 pub fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
838     // Warn if string and vector literals with sigils, or boxing expressions,
839     // are immediately borrowed.
840     let allocation = match e.node {
841         ast::ExprVstore(e2, ast::ExprVstoreUniq) => {
842             match e2.node {
843                 ast::ExprLit(lit) if ast_util::lit_is_str(lit) => {
844                     VectorAllocation
845                 }
846                 ast::ExprVec(..) => VectorAllocation,
847                 _ => return
848             }
849         }
850         ast::ExprUnary(ast::UnUniq, _) |
851         ast::ExprUnary(ast::UnBox, _) => BoxAllocation,
852
853         _ => return
854     };
855
856     let report = |msg| {
857         cx.span_lint(lint::UnnecessaryAllocation, e.span, msg);
858     };
859
860     match cx.tcx.adjustments.borrow().find(&e.id) {
861         Some(adjustment) => {
862             match *adjustment {
863                 ty::AutoDerefRef(ty::AutoDerefRef { autoref, .. }) => {
864                     match (allocation, autoref) {
865                         (VectorAllocation, Some(ty::AutoBorrowVec(..))) => {
866                             report("unnecessary allocation, the sigil can be \
867                                     removed");
868                         }
869                         (BoxAllocation,
870                          Some(ty::AutoPtr(_, ast::MutImmutable))) => {
871                             report("unnecessary allocation, use & instead");
872                         }
873                         (BoxAllocation,
874                          Some(ty::AutoPtr(_, ast::MutMutable))) => {
875                             report("unnecessary allocation, use &mut \
876                                     instead");
877                         }
878                         _ => ()
879                     }
880                 }
881                 _ => {}
882             }
883         }
884
885         _ => ()
886     }
887 }
888
889 pub fn check_missing_doc_attrs(cx: &Context,
890                            id: Option<ast::NodeId>,
891                            attrs: &[ast::Attribute],
892                            sp: Span,
893                            desc: &'static str) {
894     // If we're building a test harness, then warning about
895     // documentation is probably not really relevant right now.
896     if cx.tcx.sess.opts.test { return }
897
898     // `#[doc(hidden)]` disables missing_doc check.
899     if cx.is_doc_hidden { return }
900
901     // Only check publicly-visible items, using the result from the privacy pass. It's an option so
902     // the crate root can also use this function (it doesn't have a NodeId).
903     match id {
904         Some(ref id) if !cx.exported_items.contains(id) => return,
905         _ => ()
906     }
907
908     let has_doc = attrs.iter().any(|a| {
909         match a.node.value.node {
910             ast::MetaNameValue(ref name, _) if name.equiv(&("doc")) => true,
911             _ => false
912         }
913     });
914     if !has_doc {
915         cx.span_lint(lint::MissingDoc,
916                      sp,
917                      format!("missing documentation for {}",
918                              desc).as_slice());
919     }
920 }
921
922 pub fn check_missing_doc_item(cx: &Context, it: &ast::Item) {
923     let desc = match it.node {
924         ast::ItemFn(..) => "a function",
925         ast::ItemMod(..) => "a module",
926         ast::ItemEnum(..) => "an enum",
927         ast::ItemStruct(..) => "a struct",
928         ast::ItemTrait(..) => "a trait",
929         _ => return
930     };
931     check_missing_doc_attrs(cx,
932                             Some(it.id),
933                             it.attrs.as_slice(),
934                             it.span,
935                             desc);
936 }
937
938 pub fn check_missing_doc_method(cx: &Context, m: &ast::Method) {
939     // If the method is an impl for a trait, don't doc.
940     if lint::method_context(cx, m) == lint::TraitImpl { return; }
941
942     // Otherwise, doc according to privacy. This will also check
943     // doc for default methods defined on traits.
944     check_missing_doc_attrs(cx,
945                             Some(m.id),
946                             m.attrs.as_slice(),
947                             m.span,
948                             "a method");
949 }
950
951 pub fn check_missing_doc_ty_method(cx: &Context, tm: &ast::TypeMethod) {
952     check_missing_doc_attrs(cx,
953                             Some(tm.id),
954                             tm.attrs.as_slice(),
955                             tm.span,
956                             "a type method");
957 }
958
959 pub fn check_missing_doc_struct_field(cx: &Context, sf: &ast::StructField) {
960     match sf.node.kind {
961         ast::NamedField(_, vis) if vis == ast::Public =>
962             check_missing_doc_attrs(cx,
963                                     Some(cx.cur_struct_def_id),
964                                     sf.node.attrs.as_slice(),
965                                     sf.span,
966                                     "a struct field"),
967         _ => {}
968     }
969 }
970
971 pub fn check_missing_doc_variant(cx: &Context, v: &ast::Variant) {
972     check_missing_doc_attrs(cx,
973                             Some(v.node.id),
974                             v.node.attrs.as_slice(),
975                             v.span,
976                             "a variant");
977 }
978
979 /// Checks for use of items with #[deprecated], #[experimental] and
980 /// #[unstable] (or none of them) attributes.
981 pub fn check_stability(cx: &Context, e: &ast::Expr) {
982     let id = match e.node {
983         ast::ExprPath(..) | ast::ExprStruct(..) => {
984             match cx.tcx.def_map.borrow().find(&e.id) {
985                 Some(&def) => def.def_id(),
986                 None => return
987             }
988         }
989         ast::ExprMethodCall(..) => {
990             let method_call = typeck::MethodCall::expr(e.id);
991             match cx.tcx.method_map.borrow().find(&method_call) {
992                 Some(method) => {
993                     match method.origin {
994                         typeck::MethodStatic(def_id) => {
995                             // If this implements a trait method, get def_id
996                             // of the method inside trait definition.
997                             // Otherwise, use the current def_id (which refers
998                             // to the method inside impl).
999                             ty::trait_method_of_method(
1000                                 cx.tcx, def_id).unwrap_or(def_id)
1001                         }
1002                         typeck::MethodParam(typeck::MethodParam {
1003                             trait_id: trait_id,
1004                             method_num: index,
1005                             ..
1006                         })
1007                         | typeck::MethodObject(typeck::MethodObject {
1008                             trait_id: trait_id,
1009                             method_num: index,
1010                             ..
1011                         }) => ty::trait_method(cx.tcx, trait_id, index).def_id
1012                     }
1013                 }
1014                 None => return
1015             }
1016         }
1017         _ => return
1018     };
1019
1020     let stability = if ast_util::is_local(id) {
1021         // this crate
1022         let s = cx.tcx.map.with_attrs(id.node, |attrs| {
1023             attrs.map(|a| attr::find_stability(a.as_slice()))
1024         });
1025         match s {
1026             Some(s) => s,
1027
1028             // no possibility of having attributes
1029             // (e.g. it's a local variable), so just
1030             // ignore it.
1031             None => return
1032         }
1033     } else {
1034         // cross-crate
1035
1036         let mut s = None;
1037         // run through all the attributes and take the first
1038         // stability one.
1039         csearch::get_item_attrs(&cx.tcx.sess.cstore, id, |attrs| {
1040             if s.is_none() {
1041                 s = attr::find_stability(attrs.as_slice())
1042             }
1043         });
1044         s
1045     };
1046
1047     let (lint, label) = match stability {
1048         // no stability attributes == Unstable
1049         None => (lint::Unstable, "unmarked"),
1050         Some(attr::Stability { level: attr::Unstable, .. }) =>
1051                 (lint::Unstable, "unstable"),
1052         Some(attr::Stability { level: attr::Experimental, .. }) =>
1053                 (lint::Experimental, "experimental"),
1054         Some(attr::Stability { level: attr::Deprecated, .. }) =>
1055                 (lint::Deprecated, "deprecated"),
1056         _ => return
1057     };
1058
1059     let msg = match stability {
1060         Some(attr::Stability { text: Some(ref s), .. }) => {
1061             format!("use of {} item: {}", label, *s)
1062         }
1063         _ => format!("use of {} item", label)
1064     };
1065
1066     cx.span_lint(lint, e.span, msg.as_slice());
1067 }
1068
1069 pub fn check_enum_variant_sizes(cx: &mut Context, it: &ast::Item) {
1070     match it.node {
1071         ast::ItemEnum(..) => {
1072             match cx.cur.find(&(lint::VariantSizeDifference as uint)) {
1073                 Some(&(lvl, src)) if lvl != lint::Allow => {
1074                     cx.node_levels.insert((it.id, lint::VariantSizeDifference), (lvl, src));
1075                 },
1076                 _ => { }
1077             }
1078         },
1079         _ => { }
1080     }
1081 }