]> git.lizzy.rs Git - rust.git/blob - src/librustc_lint/unused.rs
Auto merge of #50329 - Zoxc:opt-3, r=alexcrichton
[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::StmtSemi(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::BiEq | hir::BiLt | hir::BiLe | hir::BiNe | hir::BiGe | hir::BiGt => {
106                         Some("comparison")
107                     },
108                     hir::BiAdd | hir::BiSub | hir::BiDiv | hir::BiMul | hir::BiRem => {
109                         Some("arithmetic operation")
110                     },
111                     hir::BiAnd | hir::BiOr => {
112                         Some("logical operation")
113                     },
114                     hir::BiBitXor | hir::BiBitAnd | hir::BiBitOr | hir::BiShl | hir::BiShr => {
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                     // check for #[must_use="..."]
139                     if let Some(s) = attr.value_str() {
140                         msg.push_str(": ");
141                         msg.push_str(&s.as_str());
142                     }
143                     cx.span_lint(UNUSED_MUST_USE, sp, &msg);
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::StmtSemi(ref expr, _) = s.node {
170             if let hir::ExprPath(_) = 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         let name = unwrap_or!(attr.name(), return);
196
197         // Note that check_name() marks the attribute as used if it matches.
198         for &(ref name, ty, _) in BUILTIN_ATTRIBUTES {
199             match ty {
200                 AttributeType::Whitelisted if attr.check_name(name) => {
201                     debug!("{:?} is Whitelisted", name);
202                     break;
203                 }
204                 _ => (),
205             }
206         }
207
208         let plugin_attributes = cx.sess().plugin_attributes.borrow_mut();
209         for &(ref name, ty) in plugin_attributes.iter() {
210             if ty == AttributeType::Whitelisted && attr.check_name(&name) {
211                 debug!("{:?} (plugin attr) is whitelisted with ty {:?}", name, ty);
212                 break;
213             }
214         }
215
216         if !attr::is_used(attr) {
217             debug!("Emitting warning for: {:?}", attr);
218             cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
219             // Is it a builtin attribute that must be used at the crate level?
220             let known_crate = BUILTIN_ATTRIBUTES.iter()
221                 .find(|&&(builtin, ty, _)| name == builtin && ty == AttributeType::CrateLevel)
222                 .is_some();
223
224             // Has a plugin registered this attribute as one which must be used at
225             // the crate level?
226             let plugin_crate = plugin_attributes.iter()
227                 .find(|&&(ref x, t)| name == &**x && AttributeType::CrateLevel == t)
228                 .is_some();
229             if known_crate || plugin_crate {
230                 let msg = match attr.style {
231                     ast::AttrStyle::Outer => {
232                         "crate-level attribute should be an inner attribute: add an exclamation \
233                          mark: #![foo]"
234                     }
235                     ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
236                 };
237                 cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg);
238             }
239         } else {
240             debug!("Attr was used: {:?}", attr);
241         }
242     }
243 }
244
245 declare_lint! {
246     pub(super) UNUSED_PARENS,
247     Warn,
248     "`if`, `match`, `while` and `return` do not need parentheses"
249 }
250
251 #[derive(Copy, Clone)]
252 pub struct UnusedParens;
253
254 impl UnusedParens {
255     fn check_unused_parens_core(&self,
256                                 cx: &EarlyContext,
257                                 value: &ast::Expr,
258                                 msg: &str,
259                                 struct_lit_needs_parens: bool) {
260         if let ast::ExprKind::Paren(ref inner) = value.node {
261             let necessary = struct_lit_needs_parens &&
262                             parser::contains_exterior_struct_lit(&inner);
263             if !necessary {
264                 let span_msg = format!("unnecessary parentheses around {}", msg);
265                 let mut err = cx.struct_span_lint(UNUSED_PARENS,
266                                                   value.span,
267                                                   &span_msg);
268                 // Remove exactly one pair of parentheses (rather than naïvely
269                 // stripping all paren characters)
270                 let mut ate_left_paren = false;
271                 let mut ate_right_paren = false;
272                 let parens_removed = pprust::expr_to_string(value)
273                     .trim_matches(|c| {
274                         match c {
275                             '(' => {
276                                 if ate_left_paren {
277                                     false
278                                 } else {
279                                     ate_left_paren = true;
280                                     true
281                                 }
282                             },
283                             ')' => {
284                                 if ate_right_paren {
285                                     false
286                                 } else {
287                                     ate_right_paren = true;
288                                     true
289                                 }
290                             },
291                             _ => false,
292                         }
293                     }).to_owned();
294                 err.span_suggestion_short(value.span,
295                                           "remove these parentheses",
296                                           parens_removed);
297                 err.emit();
298             }
299         }
300     }
301 }
302
303 impl LintPass for UnusedParens {
304     fn get_lints(&self) -> LintArray {
305         lint_array!(UNUSED_PARENS)
306     }
307 }
308
309 impl EarlyLintPass for UnusedParens {
310     fn check_expr(&mut self, cx: &EarlyContext, e: &ast::Expr) {
311         use syntax::ast::ExprKind::*;
312         let (value, msg, struct_lit_needs_parens) = match e.node {
313             If(ref cond, ..) => (cond, "`if` condition", true),
314             While(ref cond, ..) => (cond, "`while` condition", true),
315             IfLet(_, ref cond, ..) => (cond, "`if let` head expression", true),
316             WhileLet(_, ref cond, ..) => (cond, "`while let` head expression", true),
317             ForLoop(_, ref cond, ..) => (cond, "`for` head expression", true),
318             Match(ref head, _) => (head, "`match` head expression", true),
319             Ret(Some(ref value)) => (value, "`return` value", false),
320             Assign(_, ref value) => (value, "assigned value", false),
321             AssignOp(.., ref value) => (value, "assigned value", false),
322             // either function/method call, or something this lint doesn't care about
323             ref call_or_other => {
324                 let args_to_check;
325                 let call_kind;
326                 match *call_or_other {
327                     Call(_, ref args) => {
328                         call_kind = "function";
329                         args_to_check = &args[..];
330                     },
331                     MethodCall(_, ref args) => {
332                         call_kind = "method";
333                         // first "argument" is self (which sometimes needs parens)
334                         args_to_check = &args[1..];
335                     }
336                     // actual catch-all arm
337                     _ => { return; }
338                 }
339                 // Don't lint if this is a nested macro expansion: otherwise, the lint could
340                 // trigger in situations that macro authors shouldn't have to care about, e.g.,
341                 // when a parenthesized token tree matched in one macro expansion is matched as
342                 // an expression in another and used as a fn/method argument (Issue #47775)
343                 if e.span.ctxt().outer().expn_info()
344                     .map_or(false, |info| info.call_site.ctxt().outer()
345                             .expn_info().is_some()) {
346                         return;
347                 }
348                 let msg = format!("{} argument", call_kind);
349                 for arg in args_to_check {
350                     self.check_unused_parens_core(cx, arg, &msg, false);
351                 }
352                 return;
353             }
354         };
355         self.check_unused_parens_core(cx, &value, msg, struct_lit_needs_parens);
356     }
357
358     fn check_stmt(&mut self, cx: &EarlyContext, s: &ast::Stmt) {
359         let (value, msg) = match s.node {
360             ast::StmtKind::Local(ref local) => {
361                 match local.init {
362                     Some(ref value) => (value, "assigned value"),
363                     None => return,
364                 }
365             }
366             _ => return,
367         };
368         self.check_unused_parens_core(cx, &value, msg, false);
369     }
370 }
371
372 declare_lint! {
373     UNUSED_IMPORT_BRACES,
374     Allow,
375     "unnecessary braces around an imported item"
376 }
377
378 #[derive(Copy, Clone)]
379 pub struct UnusedImportBraces;
380
381 impl UnusedImportBraces {
382     fn check_use_tree(&self, cx: &EarlyContext, use_tree: &ast::UseTree, item: &ast::Item) {
383         if let ast::UseTreeKind::Nested(ref items) = use_tree.kind {
384             // Recursively check nested UseTrees
385             for &(ref tree, _) in items {
386                 self.check_use_tree(cx, tree, item);
387             }
388
389             // Trigger the lint only if there is one nested item
390             if items.len() != 1 {
391                 return;
392             }
393
394             // Trigger the lint if the nested item is a non-self single item
395             let node_ident;
396             match items[0].0.kind {
397                 ast::UseTreeKind::Simple(rename) => {
398                     let orig_ident = items[0].0.prefix.segments.last().unwrap().ident;
399                     if orig_ident.name == keywords::SelfValue.name() {
400                         return;
401                     } else {
402                         node_ident = rename.unwrap_or(orig_ident);
403                     }
404                 }
405                 ast::UseTreeKind::Glob => {
406                     node_ident = ast::Ident::from_str("*");
407                 }
408                 ast::UseTreeKind::Nested(_) => {
409                     return;
410                 }
411             }
412
413             let msg = format!("braces around {} is unnecessary", node_ident.name);
414             cx.span_lint(UNUSED_IMPORT_BRACES, item.span, &msg);
415         }
416     }
417 }
418
419 impl LintPass for UnusedImportBraces {
420     fn get_lints(&self) -> LintArray {
421         lint_array!(UNUSED_IMPORT_BRACES)
422     }
423 }
424
425 impl EarlyLintPass for UnusedImportBraces {
426     fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
427         if let ast::ItemKind::Use(ref use_tree) = item.node {
428             self.check_use_tree(cx, use_tree, item);
429         }
430     }
431 }
432
433 declare_lint! {
434     pub(super) UNUSED_ALLOCATION,
435     Warn,
436     "detects unnecessary allocations that can be eliminated"
437 }
438
439 #[derive(Copy, Clone)]
440 pub struct UnusedAllocation;
441
442 impl LintPass for UnusedAllocation {
443     fn get_lints(&self) -> LintArray {
444         lint_array!(UNUSED_ALLOCATION)
445     }
446 }
447
448 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation {
449     fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
450         match e.node {
451             hir::ExprBox(_) => {}
452             _ => return,
453         }
454
455         for adj in cx.tables.expr_adjustments(e) {
456             if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
457                 let msg = match m {
458                     adjustment::AutoBorrowMutability::Immutable =>
459                         "unnecessary allocation, use & instead",
460                     adjustment::AutoBorrowMutability::Mutable { .. }=>
461                         "unnecessary allocation, use &mut instead"
462                 };
463                 cx.span_lint(UNUSED_ALLOCATION, e.span, msg);
464             }
465         }
466     }
467 }