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