]> git.lizzy.rs Git - rust.git/blob - src/librustc_lint/unused.rs
2384b3987f2534852261594a3e1073ceb3fa3eb9
[rust.git] / src / librustc_lint / unused.rs
1 // Copyright 2015 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 use metadata::csearch;
12 use middle::pat_util;
13 use middle::ty;
14 use middle::ty::adjustment;
15 use rustc::front::map as hir_map;
16 use util::nodemap::FnvHashMap;
17 use lint::{LateContext, EarlyContext, LintContext, LintArray};
18 use lint::{LintPass, EarlyLintPass, LateLintPass};
19
20 use std::collections::hash_map::Entry::{Occupied, Vacant};
21
22 use syntax::ast;
23 use syntax::attr::{self, AttrMetaMethods};
24 use syntax::codemap::Span;
25 use syntax::feature_gate::{KNOWN_ATTRIBUTES, AttributeType};
26 use syntax::ptr::P;
27
28 use rustc_back::slice;
29 use rustc_front::hir;
30 use rustc_front::visit::FnKind;
31
32 declare_lint! {
33     pub UNUSED_MUT,
34     Warn,
35     "detect mut variables which don't need to be mutable"
36 }
37
38 #[derive(Copy, Clone)]
39 pub struct UnusedMut;
40
41 impl UnusedMut {
42     fn check_unused_mut_pat(&self, cx: &LateContext, pats: &[P<hir::Pat>]) {
43         // collect all mutable pattern and group their NodeIDs by their Identifier to
44         // avoid false warnings in match arms with multiple patterns
45
46         let mut mutables = FnvHashMap();
47         for p in pats {
48             pat_util::pat_bindings(&cx.tcx.def_map, p, |mode, id, _, path1| {
49                 let name = path1.node;
50                 if let hir::BindByValue(hir::MutMutable) = mode {
51                     if !name.as_str().starts_with("_") {
52                         match mutables.entry(name.0 as usize) {
53                             Vacant(entry) => { entry.insert(vec![id]); },
54                             Occupied(mut entry) => { entry.get_mut().push(id); },
55                         }
56                     }
57                 }
58             });
59         }
60
61         let used_mutables = cx.tcx.used_mut_nodes.borrow();
62         for (_, v) in &mutables {
63             if !v.iter().any(|e| used_mutables.contains(e)) {
64                 cx.span_lint(UNUSED_MUT, cx.tcx.map.span(v[0]),
65                              "variable does not need to be mutable");
66             }
67         }
68     }
69 }
70
71 impl LintPass for UnusedMut {
72     fn get_lints(&self) -> LintArray {
73         lint_array!(UNUSED_MUT)
74     }
75 }
76
77 impl LateLintPass for UnusedMut {
78     fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
79         if let hir::ExprMatch(_, ref arms, _) = e.node {
80             for a in arms {
81                 self.check_unused_mut_pat(cx, &a.pats)
82             }
83         }
84     }
85
86     fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
87         if let hir::StmtDecl(ref d, _) = s.node {
88             if let hir::DeclLocal(ref l) = d.node {
89                 self.check_unused_mut_pat(cx, slice::ref_slice(&l.pat));
90             }
91         }
92     }
93
94     fn check_fn(&mut self, cx: &LateContext,
95                 _: FnKind, decl: &hir::FnDecl,
96                 _: &hir::Block, _: Span, _: ast::NodeId) {
97         for a in &decl.inputs {
98             self.check_unused_mut_pat(cx, slice::ref_slice(&a.pat));
99         }
100     }
101 }
102
103 declare_lint! {
104     pub UNUSED_MUST_USE,
105     Warn,
106     "unused result of a type flagged as #[must_use]"
107 }
108
109 declare_lint! {
110     pub UNUSED_RESULTS,
111     Allow,
112     "unused result of an expression in a statement"
113 }
114
115 #[derive(Copy, Clone)]
116 pub struct UnusedResults;
117
118 impl LintPass for UnusedResults {
119     fn get_lints(&self) -> LintArray {
120         lint_array!(UNUSED_MUST_USE, UNUSED_RESULTS)
121     }
122 }
123
124 impl LateLintPass for UnusedResults {
125     fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
126         let expr = match s.node {
127             hir::StmtSemi(ref expr, _) => &**expr,
128             _ => return
129         };
130
131         if let hir::ExprRet(..) = expr.node {
132             return;
133         }
134
135         let t = cx.tcx.expr_ty(&expr);
136         let warned = match t.sty {
137             ty::TyTuple(ref tys) if tys.is_empty() => return,
138             ty::TyBool => return,
139             ty::TyStruct(def, _) |
140             ty::TyEnum(def, _) => {
141                 if let Some(def_node_id) = cx.tcx.map.as_local_node_id(def.did) {
142                     if let hir_map::NodeItem(it) = cx.tcx.map.get(def_node_id) {
143                         check_must_use(cx, &it.attrs, s.span)
144                     } else {
145                         false
146                     }
147                 } else {
148                     let attrs = csearch::get_item_attrs(&cx.sess().cstore, def.did);
149                     check_must_use(cx, &attrs[..], s.span)
150                 }
151             }
152             _ => false,
153         };
154         if !warned {
155             cx.span_lint(UNUSED_RESULTS, s.span, "unused result");
156         }
157
158         fn check_must_use(cx: &LateContext, attrs: &[ast::Attribute], sp: Span) -> bool {
159             for attr in attrs {
160                 if attr.check_name("must_use") {
161                     let mut msg = "unused result which must be used".to_string();
162                     // check for #[must_use="..."]
163                     match attr.value_str() {
164                         None => {}
165                         Some(s) => {
166                             msg.push_str(": ");
167                             msg.push_str(&s);
168                         }
169                     }
170                     cx.span_lint(UNUSED_MUST_USE, sp, &msg);
171                     return true;
172                 }
173             }
174             false
175         }
176     }
177 }
178
179 declare_lint! {
180     pub UNUSED_UNSAFE,
181     Warn,
182     "unnecessary use of an `unsafe` block"
183 }
184
185 #[derive(Copy, Clone)]
186 pub struct UnusedUnsafe;
187
188 impl LintPass for UnusedUnsafe {
189     fn get_lints(&self) -> LintArray {
190         lint_array!(UNUSED_UNSAFE)
191     }
192 }
193
194 impl LateLintPass for UnusedUnsafe {
195     fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
196         if let hir::ExprBlock(ref blk) = e.node {
197             // Don't warn about generated blocks, that'll just pollute the output.
198             if blk.rules == hir::UnsafeBlock(hir::UserProvided) &&
199                 !cx.tcx.used_unsafe.borrow().contains(&blk.id) {
200                     cx.span_lint(UNUSED_UNSAFE, blk.span, "unnecessary `unsafe` block");
201             }
202         }
203     }
204 }
205
206 declare_lint! {
207     pub PATH_STATEMENTS,
208     Warn,
209     "path statements with no effect"
210 }
211
212 #[derive(Copy, Clone)]
213 pub struct PathStatements;
214
215 impl LintPass for PathStatements {
216     fn get_lints(&self) -> LintArray {
217         lint_array!(PATH_STATEMENTS)
218     }
219 }
220
221 impl LateLintPass for PathStatements {
222     fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
223         if let hir::StmtSemi(ref expr, _) = s.node {
224             if let hir::ExprPath(..) = expr.node {
225                 cx.span_lint(PATH_STATEMENTS, s.span,
226                              "path statement with no effect");
227             }
228         }
229     }
230 }
231
232 declare_lint! {
233     pub UNUSED_ATTRIBUTES,
234     Warn,
235     "detects attributes that were not used by the compiler"
236 }
237
238 #[derive(Copy, Clone)]
239 pub struct UnusedAttributes;
240
241 impl LintPass for UnusedAttributes {
242     fn get_lints(&self) -> LintArray {
243         lint_array!(UNUSED_ATTRIBUTES)
244     }
245 }
246
247 impl LateLintPass for UnusedAttributes {
248     fn check_attribute(&mut self, cx: &LateContext, attr: &ast::Attribute) {
249         // Note that check_name() marks the attribute as used if it matches.
250         for &(ref name, ty, _) in KNOWN_ATTRIBUTES {
251             match ty {
252                 AttributeType::Whitelisted if attr.check_name(name) => {
253                     break;
254                 },
255                 _ => ()
256             }
257         }
258
259         let plugin_attributes = cx.sess().plugin_attributes.borrow_mut();
260         for &(ref name, ty) in plugin_attributes.iter() {
261             if ty == AttributeType::Whitelisted && attr.check_name(&*name) {
262                 break;
263             }
264         }
265
266         if !attr::is_used(attr) {
267             cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
268             // Is it a builtin attribute that must be used at the crate level?
269             let known_crate = KNOWN_ATTRIBUTES.iter().find(|&&(name, ty, _)| {
270                 attr.name() == name &&
271                 ty == AttributeType::CrateLevel
272             }).is_some();
273
274             // Has a plugin registered this attribute as one which must be used at
275             // the crate level?
276             let plugin_crate = plugin_attributes.iter()
277                                                 .find(|&&(ref x, t)| {
278                                                         &*attr.name() == &*x &&
279                                                         AttributeType::CrateLevel == t
280                                                     }).is_some();
281             if  known_crate || plugin_crate {
282                 let msg = match attr.node.style {
283                     ast::AttrStyle::Outer => "crate-level attribute should be an inner \
284                                               attribute: add an exclamation mark: #![foo]",
285                     ast::AttrStyle::Inner => "crate-level attribute should be in the \
286                                               root module",
287                 };
288                 cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg);
289             }
290         }
291     }
292 }
293
294 declare_lint! {
295     UNUSED_PARENS,
296     Warn,
297     "`if`, `match`, `while` and `return` do not need parentheses"
298 }
299
300 #[derive(Copy, Clone)]
301 pub struct UnusedParens;
302
303 impl UnusedParens {
304     fn check_unused_parens_core(&self, cx: &EarlyContext, value: &ast::Expr, msg: &str,
305                                 struct_lit_needs_parens: bool) {
306         if let ast::ExprParen(ref inner) = value.node {
307             let necessary = struct_lit_needs_parens && contains_exterior_struct_lit(&**inner);
308             if !necessary {
309                 cx.span_lint(UNUSED_PARENS, value.span,
310                              &format!("unnecessary parentheses around {}", msg))
311             }
312         }
313
314         /// Expressions that syntactically contain an "exterior" struct
315         /// literal i.e. not surrounded by any parens or other
316         /// delimiters, e.g. `X { y: 1 }`, `X { y: 1 }.method()`, `foo
317         /// == X { y: 1 }` and `X { y: 1 } == foo` all do, but `(X {
318         /// y: 1 }) == foo` does not.
319         fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
320             match value.node {
321                 ast::ExprStruct(..) => true,
322
323                 ast::ExprAssign(ref lhs, ref rhs) |
324                 ast::ExprAssignOp(_, ref lhs, ref rhs) |
325                 ast::ExprBinary(_, ref lhs, ref rhs) => {
326                     // X { y: 1 } + X { y: 2 }
327                     contains_exterior_struct_lit(&**lhs) ||
328                         contains_exterior_struct_lit(&**rhs)
329                 }
330                 ast::ExprUnary(_, ref x) |
331                 ast::ExprCast(ref x, _) |
332                 ast::ExprField(ref x, _) |
333                 ast::ExprTupField(ref x, _) |
334                 ast::ExprIndex(ref x, _) => {
335                     // &X { y: 1 }, X { y: 1 }.y
336                     contains_exterior_struct_lit(&**x)
337                 }
338
339                 ast::ExprMethodCall(_, _, ref exprs) => {
340                     // X { y: 1 }.bar(...)
341                     contains_exterior_struct_lit(&*exprs[0])
342                 }
343
344                 _ => false
345             }
346         }
347     }
348 }
349
350 impl LintPass for UnusedParens {
351     fn get_lints(&self) -> LintArray {
352         lint_array!(UNUSED_PARENS)
353     }
354 }
355
356 impl EarlyLintPass for UnusedParens {
357     fn check_expr(&mut self, cx: &EarlyContext, e: &ast::Expr) {
358         let (value, msg, struct_lit_needs_parens) = match e.node {
359             ast::ExprIf(ref cond, _, _) => (cond, "`if` condition", true),
360             ast::ExprWhile(ref cond, _, _) => (cond, "`while` condition", true),
361             ast::ExprIfLet(_, ref cond, _, _) => (cond, "`if let` head expression", true),
362             ast::ExprWhileLet(_, ref cond, _, _) => (cond, "`while let` head expression", true),
363             ast::ExprForLoop(_, ref cond, _, _) => (cond, "`for` head expression", true),
364             ast::ExprMatch(ref head, _) => (head, "`match` head expression", true),
365             ast::ExprRet(Some(ref value)) => (value, "`return` value", false),
366             ast::ExprAssign(_, ref value) => (value, "assigned value", false),
367             ast::ExprAssignOp(_, _, ref value) => (value, "assigned value", false),
368             ast::ExprInPlace(_, ref value) => (value, "emplacement value", false),
369             _ => return
370         };
371         self.check_unused_parens_core(cx, &**value, msg, struct_lit_needs_parens);
372     }
373
374     fn check_stmt(&mut self, cx: &EarlyContext, s: &ast::Stmt) {
375         let (value, msg) = match s.node {
376             ast::StmtDecl(ref decl, _) => match decl.node {
377                 ast::DeclLocal(ref local) => match local.init {
378                     Some(ref value) => (value, "assigned value"),
379                     None => return
380                 },
381                 _ => return
382             },
383             _ => return
384         };
385         self.check_unused_parens_core(cx, &**value, msg, false);
386     }
387 }
388
389 declare_lint! {
390     UNUSED_IMPORT_BRACES,
391     Allow,
392     "unnecessary braces around an imported item"
393 }
394
395 #[derive(Copy, Clone)]
396 pub struct UnusedImportBraces;
397
398 impl LintPass for UnusedImportBraces {
399     fn get_lints(&self) -> LintArray {
400         lint_array!(UNUSED_IMPORT_BRACES)
401     }
402 }
403
404 impl LateLintPass for UnusedImportBraces {
405     fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
406         if let hir::ItemUse(ref view_path) = item.node {
407             if let hir::ViewPathList(_, ref items) = view_path.node {
408                 if items.len() == 1 {
409                     if let hir::PathListIdent {ref name, ..} = items[0].node {
410                         let m = format!("braces around {} is unnecessary",
411                                         name);
412                         cx.span_lint(UNUSED_IMPORT_BRACES, item.span,
413                                      &m[..]);
414                     }
415                 }
416             }
417         }
418     }
419 }
420
421 declare_lint! {
422     UNUSED_ALLOCATION,
423     Warn,
424     "detects unnecessary allocations that can be eliminated"
425 }
426
427 #[derive(Copy, Clone)]
428 pub struct UnusedAllocation;
429
430 impl LintPass for UnusedAllocation {
431     fn get_lints(&self) -> LintArray {
432         lint_array!(UNUSED_ALLOCATION)
433     }
434 }
435
436 impl LateLintPass for UnusedAllocation {
437     fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
438         match e.node {
439             hir::ExprBox(_) => {}
440             _ => return
441         }
442
443         if let Some(adjustment) = cx.tcx.tables.borrow().adjustments.get(&e.id) {
444             if let adjustment::AdjustDerefRef(adjustment::AutoDerefRef {
445                 ref autoref, ..
446             }) = *adjustment {
447                 match autoref {
448                     &Some(adjustment::AutoPtr(_, hir::MutImmutable)) => {
449                         cx.span_lint(UNUSED_ALLOCATION, e.span,
450                                      "unnecessary allocation, use & instead");
451                     }
452                     &Some(adjustment::AutoPtr(_, hir::MutMutable)) => {
453                         cx.span_lint(UNUSED_ALLOCATION, e.span,
454                                      "unnecessary allocation, use &mut instead");
455                     }
456                     _ => ()
457                 }
458             }
459         }
460     }
461 }
462