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