]> git.lizzy.rs Git - rust.git/blob - src/librustc_lint/unused.rs
preserve order if blocks are between items
[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 rustc::hir::def::Def;
12 use rustc::hir::def_id::DefId;
13 use rustc::ty;
14 use rustc::ty::adjustment;
15 use lint::{LateContext, EarlyContext, LintContext, LintArray};
16 use lint::{LintPass, EarlyLintPass, LateLintPass};
17
18 use syntax::ast;
19 use syntax::attr;
20 use syntax::feature_gate::{BUILTIN_ATTRIBUTES, AttributeType};
21 use syntax::print::pprust;
22 use syntax::symbol::keywords;
23 use syntax::util::parser;
24 use syntax_pos::Span;
25
26 use rustc::hir;
27
28 declare_lint! {
29     pub UNUSED_MUST_USE,
30     Warn,
31     "unused result of a type flagged as #[must_use]"
32 }
33
34 declare_lint! {
35     pub UNUSED_RESULTS,
36     Allow,
37     "unused result of an expression in a statement"
38 }
39
40 #[derive(Copy, Clone)]
41 pub struct UnusedResults;
42
43 impl LintPass for UnusedResults {
44     fn get_lints(&self) -> LintArray {
45         lint_array!(UNUSED_MUST_USE, UNUSED_RESULTS)
46     }
47 }
48
49 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
50     fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
51         let expr = match s.node {
52             hir::StmtKind::Semi(ref expr, _) => &**expr,
53             _ => return,
54         };
55
56         if let hir::ExprKind::Ret(..) = expr.node {
57             return;
58         }
59
60         let t = cx.tables.expr_ty(&expr);
61         let ty_warned = match t.sty {
62             ty::TyTuple(ref tys) if tys.is_empty() => return,
63             ty::TyNever => return,
64             ty::TyAdt(def, _) => {
65                 if def.variants.is_empty() {
66                     return;
67                 } else {
68                     check_must_use(cx, def.did, s.span, "")
69                 }
70             },
71             _ => false,
72         };
73
74         let mut fn_warned = false;
75         let mut op_warned = false;
76         let maybe_def = match expr.node {
77             hir::ExprKind::Call(ref callee, _) => {
78                 match callee.node {
79                     hir::ExprKind::Path(ref qpath) => {
80                         let def = cx.tables.qpath_def(qpath, callee.hir_id);
81                         if let Def::Fn(_) = def {
82                             Some(def)
83                         } else {  // `Def::Local` if it was a closure, for which we
84                             None  // do not currently support must-use linting
85                         }
86                     },
87                     _ => None
88                 }
89             },
90             hir::ExprKind::MethodCall(..) => {
91                 cx.tables.type_dependent_defs().get(expr.hir_id).cloned()
92             },
93             _ => None
94         };
95         if let Some(def) = maybe_def {
96             let def_id = def.def_id();
97             fn_warned = check_must_use(cx, def_id, s.span, "return value of ");
98         }
99         let must_use_op = match expr.node {
100             // Hardcoding operators here seemed more expedient than the
101             // refactoring that would be needed to look up the `#[must_use]`
102             // attribute which does exist on the comparison trait methods
103             hir::ExprKind::Binary(bin_op, ..)  => {
104                 match bin_op.node {
105                     hir::BinOpKind::Eq |
106                     hir::BinOpKind::Lt |
107                     hir::BinOpKind::Le |
108                     hir::BinOpKind::Ne |
109                     hir::BinOpKind::Ge |
110                     hir::BinOpKind::Gt => {
111                         Some("comparison")
112                     },
113                     hir::BinOpKind::Add |
114                     hir::BinOpKind::Sub |
115                     hir::BinOpKind::Div |
116                     hir::BinOpKind::Mul |
117                     hir::BinOpKind::Rem => {
118                         Some("arithmetic operation")
119                     },
120                     hir::BinOpKind::And | hir::BinOpKind::Or => {
121                         Some("logical operation")
122                     },
123                     hir::BinOpKind::BitXor |
124                     hir::BinOpKind::BitAnd |
125                     hir::BinOpKind::BitOr |
126                     hir::BinOpKind::Shl |
127                     hir::BinOpKind::Shr => {
128                         Some("bitwise operation")
129                     },
130                 }
131             },
132             hir::ExprKind::Unary(..) => Some("unary operation"),
133             _ => None
134         };
135
136         if let Some(must_use_op) = must_use_op {
137             cx.span_lint(UNUSED_MUST_USE, expr.span,
138                          &format!("unused {} which must be used", must_use_op));
139             op_warned = true;
140         }
141
142         if !(ty_warned || fn_warned || op_warned) {
143             cx.span_lint(UNUSED_RESULTS, s.span, "unused result");
144         }
145
146         fn check_must_use(cx: &LateContext, def_id: DefId, sp: Span, describe_path: &str) -> bool {
147             for attr in cx.tcx.get_attrs(def_id).iter() {
148                 if attr.check_name("must_use") {
149                     let msg = format!("unused {}`{}` which must be used",
150                                           describe_path, cx.tcx.item_path_str(def_id));
151                     let mut err = cx.struct_span_lint(UNUSED_MUST_USE, sp, &msg);
152                     // check for #[must_use = "..."]
153                     if let Some(note) = attr.value_str() {
154                         err.note(&note.as_str());
155                     }
156                     err.emit();
157                     return true;
158                 }
159             }
160             false
161         }
162     }
163 }
164
165 declare_lint! {
166     pub PATH_STATEMENTS,
167     Warn,
168     "path statements with no effect"
169 }
170
171 #[derive(Copy, Clone)]
172 pub struct PathStatements;
173
174 impl LintPass for PathStatements {
175     fn get_lints(&self) -> LintArray {
176         lint_array!(PATH_STATEMENTS)
177     }
178 }
179
180 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PathStatements {
181     fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
182         if let hir::StmtKind::Semi(ref expr, _) = s.node {
183             if let hir::ExprKind::Path(_) = expr.node {
184                 cx.span_lint(PATH_STATEMENTS, s.span, "path statement with no effect");
185             }
186         }
187     }
188 }
189
190 declare_lint! {
191     pub UNUSED_ATTRIBUTES,
192     Warn,
193     "detects attributes that were not used by the compiler"
194 }
195
196 #[derive(Copy, Clone)]
197 pub struct UnusedAttributes;
198
199 impl LintPass for UnusedAttributes {
200     fn get_lints(&self) -> LintArray {
201         lint_array!(UNUSED_ATTRIBUTES)
202     }
203 }
204
205 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {
206     fn check_attribute(&mut self, cx: &LateContext, attr: &ast::Attribute) {
207         debug!("checking attribute: {:?}", attr);
208         // Note that check_name() marks the attribute as used if it matches.
209         for &(ref name, ty, _) in BUILTIN_ATTRIBUTES {
210             match ty {
211                 AttributeType::Whitelisted if attr.check_name(name) => {
212                     debug!("{:?} is Whitelisted", name);
213                     break;
214                 }
215                 _ => (),
216             }
217         }
218
219         let plugin_attributes = cx.sess().plugin_attributes.borrow_mut();
220         for &(ref name, ty) in plugin_attributes.iter() {
221             if ty == AttributeType::Whitelisted && attr.check_name(&name) {
222                 debug!("{:?} (plugin attr) is whitelisted with ty {:?}", name, ty);
223                 break;
224             }
225         }
226
227         let name = attr.name();
228         if !attr::is_used(attr) {
229             debug!("Emitting warning for: {:?}", attr);
230             cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
231             // Is it a builtin attribute that must be used at the crate level?
232             let known_crate = BUILTIN_ATTRIBUTES.iter()
233                 .find(|&&(builtin, ty, _)| name == builtin && ty == AttributeType::CrateLevel)
234                 .is_some();
235
236             // Has a plugin registered this attribute as one which must be used at
237             // the crate level?
238             let plugin_crate = plugin_attributes.iter()
239                 .find(|&&(ref x, t)| name == &**x && AttributeType::CrateLevel == t)
240                 .is_some();
241             if known_crate || plugin_crate {
242                 let msg = match attr.style {
243                     ast::AttrStyle::Outer => {
244                         "crate-level attribute should be an inner attribute: add an exclamation \
245                          mark: #![foo]"
246                     }
247                     ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
248                 };
249                 cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg);
250             }
251         } else {
252             debug!("Attr was used: {:?}", attr);
253         }
254     }
255 }
256
257 declare_lint! {
258     pub(super) UNUSED_PARENS,
259     Warn,
260     "`if`, `match`, `while` and `return` do not need parentheses"
261 }
262
263 #[derive(Copy, Clone)]
264 pub struct UnusedParens;
265
266 impl UnusedParens {
267     fn check_unused_parens_core(&self,
268                                 cx: &EarlyContext,
269                                 value: &ast::Expr,
270                                 msg: &str,
271                                 struct_lit_needs_parens: bool) {
272         if let ast::ExprKind::Paren(ref inner) = value.node {
273             let necessary = struct_lit_needs_parens &&
274                             parser::contains_exterior_struct_lit(&inner);
275             if !necessary {
276                 let span_msg = format!("unnecessary parentheses around {}", msg);
277                 let mut err = cx.struct_span_lint(UNUSED_PARENS,
278                                                   value.span,
279                                                   &span_msg);
280                 // Remove exactly one pair of parentheses (rather than naïvely
281                 // stripping all paren characters)
282                 let mut ate_left_paren = false;
283                 let mut ate_right_paren = false;
284                 let parens_removed = pprust::expr_to_string(value)
285                     .trim_matches(|c| {
286                         match c {
287                             '(' => {
288                                 if ate_left_paren {
289                                     false
290                                 } else {
291                                     ate_left_paren = true;
292                                     true
293                                 }
294                             },
295                             ')' => {
296                                 if ate_right_paren {
297                                     false
298                                 } else {
299                                     ate_right_paren = true;
300                                     true
301                                 }
302                             },
303                             _ => false,
304                         }
305                     }).to_owned();
306                 err.span_suggestion_short(value.span,
307                                           "remove these parentheses",
308                                           parens_removed);
309                 err.emit();
310             }
311         }
312     }
313 }
314
315 impl LintPass for UnusedParens {
316     fn get_lints(&self) -> LintArray {
317         lint_array!(UNUSED_PARENS)
318     }
319 }
320
321 impl EarlyLintPass for UnusedParens {
322     fn check_expr(&mut self, cx: &EarlyContext, e: &ast::Expr) {
323         use syntax::ast::ExprKind::*;
324         let (value, msg, struct_lit_needs_parens) = match e.node {
325             If(ref cond, ..) => (cond, "`if` condition", true),
326             While(ref cond, ..) => (cond, "`while` condition", true),
327             IfLet(_, ref cond, ..) => (cond, "`if let` head expression", true),
328             WhileLet(_, ref cond, ..) => (cond, "`while let` head expression", true),
329             ForLoop(_, ref cond, ..) => (cond, "`for` head expression", true),
330             Match(ref head, _) => (head, "`match` head expression", true),
331             Ret(Some(ref value)) => (value, "`return` value", false),
332             Assign(_, ref value) => (value, "assigned value", false),
333             AssignOp(.., ref value) => (value, "assigned value", false),
334             // either function/method call, or something this lint doesn't care about
335             ref call_or_other => {
336                 let args_to_check;
337                 let call_kind;
338                 match *call_or_other {
339                     Call(_, ref args) => {
340                         call_kind = "function";
341                         args_to_check = &args[..];
342                     },
343                     MethodCall(_, ref args) => {
344                         call_kind = "method";
345                         // first "argument" is self (which sometimes needs parens)
346                         args_to_check = &args[1..];
347                     }
348                     // actual catch-all arm
349                     _ => { return; }
350                 }
351                 // Don't lint if this is a nested macro expansion: otherwise, the lint could
352                 // trigger in situations that macro authors shouldn't have to care about, e.g.,
353                 // when a parenthesized token tree matched in one macro expansion is matched as
354                 // an expression in another and used as a fn/method argument (Issue #47775)
355                 if e.span.ctxt().outer().expn_info()
356                     .map_or(false, |info| info.call_site.ctxt().outer()
357                             .expn_info().is_some()) {
358                         return;
359                 }
360                 let msg = format!("{} argument", call_kind);
361                 for arg in args_to_check {
362                     self.check_unused_parens_core(cx, arg, &msg, false);
363                 }
364                 return;
365             }
366         };
367         self.check_unused_parens_core(cx, &value, msg, struct_lit_needs_parens);
368     }
369
370     fn check_stmt(&mut self, cx: &EarlyContext, s: &ast::Stmt) {
371         let (value, msg) = match s.node {
372             ast::StmtKind::Local(ref local) => {
373                 match local.init {
374                     Some(ref value) => (value, "assigned value"),
375                     None => return,
376                 }
377             }
378             _ => return,
379         };
380         self.check_unused_parens_core(cx, &value, msg, false);
381     }
382 }
383
384 declare_lint! {
385     UNUSED_IMPORT_BRACES,
386     Allow,
387     "unnecessary braces around an imported item"
388 }
389
390 #[derive(Copy, Clone)]
391 pub struct UnusedImportBraces;
392
393 impl UnusedImportBraces {
394     fn check_use_tree(&self, cx: &EarlyContext, use_tree: &ast::UseTree, item: &ast::Item) {
395         if let ast::UseTreeKind::Nested(ref items) = use_tree.kind {
396             // Recursively check nested UseTrees
397             for &(ref tree, _) in items {
398                 self.check_use_tree(cx, tree, item);
399             }
400
401             // Trigger the lint only if there is one nested item
402             if items.len() != 1 {
403                 return;
404             }
405
406             // Trigger the lint if the nested item is a non-self single item
407             let node_ident;
408             match items[0].0.kind {
409                 ast::UseTreeKind::Simple(rename, ..) => {
410                     let orig_ident = items[0].0.prefix.segments.last().unwrap().ident;
411                     if orig_ident.name == keywords::SelfValue.name() {
412                         return;
413                     } else {
414                         node_ident = rename.unwrap_or(orig_ident);
415                     }
416                 }
417                 ast::UseTreeKind::Glob => {
418                     node_ident = ast::Ident::from_str("*");
419                 }
420                 ast::UseTreeKind::Nested(_) => {
421                     return;
422                 }
423             }
424
425             let msg = format!("braces around {} is unnecessary", node_ident.name);
426             cx.span_lint(UNUSED_IMPORT_BRACES, item.span, &msg);
427         }
428     }
429 }
430
431 impl LintPass for UnusedImportBraces {
432     fn get_lints(&self) -> LintArray {
433         lint_array!(UNUSED_IMPORT_BRACES)
434     }
435 }
436
437 impl EarlyLintPass for UnusedImportBraces {
438     fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
439         if let ast::ItemKind::Use(ref use_tree) = item.node {
440             self.check_use_tree(cx, use_tree, item);
441         }
442     }
443 }
444
445 declare_lint! {
446     pub(super) UNUSED_ALLOCATION,
447     Warn,
448     "detects unnecessary allocations that can be eliminated"
449 }
450
451 #[derive(Copy, Clone)]
452 pub struct UnusedAllocation;
453
454 impl LintPass for UnusedAllocation {
455     fn get_lints(&self) -> LintArray {
456         lint_array!(UNUSED_ALLOCATION)
457     }
458 }
459
460 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation {
461     fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
462         match e.node {
463             hir::ExprKind::Box(_) => {}
464             _ => return,
465         }
466
467         for adj in cx.tables.expr_adjustments(e) {
468             if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
469                 let msg = match m {
470                     adjustment::AutoBorrowMutability::Immutable =>
471                         "unnecessary allocation, use & instead",
472                     adjustment::AutoBorrowMutability::Mutable { .. }=>
473                         "unnecessary allocation, use &mut instead"
474                 };
475                 cx.span_lint(UNUSED_ALLOCATION, e.span, msg);
476             }
477         }
478     }
479 }