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