]> git.lizzy.rs Git - rust.git/blob - src/librustc_lint/unused.rs
Rollup merge of #59432 - phansch:compiletest_docs, r=alexcrichton
[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.def_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_or_empty();
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, ..)| {
277                     name == builtin && ty == AttributeType::CrateLevel
278                 })
279                 .is_some();
280
281             // Has a plugin registered this attribute as one that must be used at
282             // the crate level?
283             let plugin_crate = plugin_attributes.iter()
284                 .find(|&&(ref x, t)| name == x.as_str() && AttributeType::CrateLevel == t)
285                 .is_some();
286             if known_crate || plugin_crate {
287                 let msg = match attr.style {
288                     ast::AttrStyle::Outer => {
289                         "crate-level attribute should be an inner attribute: add an exclamation \
290                          mark: #![foo]"
291                     }
292                     ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
293                 };
294                 cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg);
295             }
296         } else {
297             debug!("Attr was used: {:?}", attr);
298         }
299     }
300 }
301
302 declare_lint! {
303     pub(super) UNUSED_PARENS,
304     Warn,
305     "`if`, `match`, `while` and `return` do not need parentheses"
306 }
307
308 #[derive(Copy, Clone)]
309 pub struct UnusedParens;
310
311 impl UnusedParens {
312     fn check_unused_parens_expr(&self,
313                                 cx: &EarlyContext<'_>,
314                                 value: &ast::Expr,
315                                 msg: &str,
316                                 followed_by_block: bool) {
317         if let ast::ExprKind::Paren(ref inner) = value.node {
318             let necessary = followed_by_block && match inner.node {
319                 ast::ExprKind::Ret(_) | ast::ExprKind::Break(..) => true,
320                 _ => parser::contains_exterior_struct_lit(&inner),
321             };
322             if !necessary {
323                 let expr_text = if let Ok(snippet) = cx.sess().source_map()
324                     .span_to_snippet(value.span) {
325                         snippet
326                     } else {
327                         pprust::expr_to_string(value)
328                     };
329                 Self::remove_outer_parens(cx, value.span, &expr_text, msg);
330             }
331         }
332     }
333
334     fn check_unused_parens_pat(&self,
335                                 cx: &EarlyContext<'_>,
336                                 value: &ast::Pat,
337                                 msg: &str) {
338         if let ast::PatKind::Paren(_) = value.node {
339             let pattern_text = if let Ok(snippet) = cx.sess().source_map()
340                 .span_to_snippet(value.span) {
341                     snippet
342                 } else {
343                     pprust::pat_to_string(value)
344                 };
345             Self::remove_outer_parens(cx, value.span, &pattern_text, msg);
346         }
347     }
348
349     fn remove_outer_parens(cx: &EarlyContext<'_>, span: Span, pattern: &str, msg: &str) {
350         let span_msg = format!("unnecessary parentheses around {}", msg);
351         let mut err = cx.struct_span_lint(UNUSED_PARENS, span, &span_msg);
352         let mut ate_left_paren = false;
353         let mut ate_right_paren = false;
354         let parens_removed = pattern
355             .trim_matches(|c| {
356                 match c {
357                     '(' => {
358                         if ate_left_paren {
359                             false
360                         } else {
361                             ate_left_paren = true;
362                             true
363                         }
364                     },
365                     ')' => {
366                         if ate_right_paren {
367                             false
368                         } else {
369                             ate_right_paren = true;
370                             true
371                         }
372                     },
373                     _ => false,
374                 }
375             }).to_owned();
376         err.span_suggestion_short(
377             span,
378             "remove these parentheses",
379             parens_removed,
380             Applicability::MachineApplicable,
381         );
382         err.emit();
383     }
384 }
385
386 impl LintPass for UnusedParens {
387     fn name(&self) -> &'static str {
388         "UnusedParens"
389     }
390
391     fn get_lints(&self) -> LintArray {
392         lint_array!(UNUSED_PARENS)
393     }
394 }
395
396 impl EarlyLintPass for UnusedParens {
397     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
398         use syntax::ast::ExprKind::*;
399         let (value, msg, followed_by_block) = match e.node {
400             If(ref cond, ..) => (cond, "`if` condition", true),
401             While(ref cond, ..) => (cond, "`while` condition", true),
402             IfLet(_, ref cond, ..) => (cond, "`if let` head expression", true),
403             WhileLet(_, ref cond, ..) => (cond, "`while let` head expression", true),
404             ForLoop(_, ref cond, ..) => (cond, "`for` head expression", true),
405             Match(ref head, _) => (head, "`match` head expression", true),
406             Ret(Some(ref value)) => (value, "`return` value", false),
407             Assign(_, ref value) => (value, "assigned value", false),
408             AssignOp(.., ref value) => (value, "assigned value", false),
409             // either function/method call, or something this lint doesn't care about
410             ref call_or_other => {
411                 let (args_to_check, call_kind) = match *call_or_other {
412                     Call(_, ref args) => (&args[..], "function"),
413                     // first "argument" is self (which sometimes needs parens)
414                     MethodCall(_, ref args) => (&args[1..], "method"),
415                     // actual catch-all arm
416                     _ => {
417                         return;
418                     }
419                 };
420                 // Don't lint if this is a nested macro expansion: otherwise, the lint could
421                 // trigger in situations that macro authors shouldn't have to care about, e.g.,
422                 // when a parenthesized token tree matched in one macro expansion is matched as
423                 // an expression in another and used as a fn/method argument (Issue #47775)
424                 if e.span.ctxt().outer().expn_info()
425                     .map_or(false, |info| info.call_site.ctxt().outer()
426                             .expn_info().is_some()) {
427                         return;
428                 }
429                 let msg = format!("{} argument", call_kind);
430                 for arg in args_to_check {
431                     self.check_unused_parens_expr(cx, arg, &msg, false);
432                 }
433                 return;
434             }
435         };
436         self.check_unused_parens_expr(cx, &value, msg, followed_by_block);
437     }
438
439     fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat, _: &mut bool) {
440         use ast::PatKind::{Paren, Range};
441         // The lint visitor will visit each subpattern of `p`. We do not want to lint any range
442         // pattern no matter where it occurs in the pattern. For something like `&(a..=b)`, there
443         // is a recursive `check_pat` on `a` and `b`, but we will assume that if there are
444         // unnecessary parens they serve a purpose of readability.
445         if let Paren(ref pat) = p.node {
446             match pat.node {
447                 Range(..) => {}
448                 _ => self.check_unused_parens_pat(cx, &p, "pattern")
449             }
450         }
451     }
452
453     fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
454         if let ast::StmtKind::Local(ref local) = s.node {
455             if let Some(ref value) = local.init {
456                 self.check_unused_parens_expr(cx, &value, "assigned value", false);
457             }
458         }
459     }
460 }
461
462 declare_lint! {
463     UNUSED_IMPORT_BRACES,
464     Allow,
465     "unnecessary braces around an imported item"
466 }
467
468 #[derive(Copy, Clone)]
469 pub struct UnusedImportBraces;
470
471 impl UnusedImportBraces {
472     fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) {
473         if let ast::UseTreeKind::Nested(ref items) = use_tree.kind {
474             // Recursively check nested UseTrees
475             for &(ref tree, _) in items {
476                 self.check_use_tree(cx, tree, item);
477             }
478
479             // Trigger the lint only if there is one nested item
480             if items.len() != 1 {
481                 return;
482             }
483
484             // Trigger the lint if the nested item is a non-self single item
485             let node_ident;
486             match items[0].0.kind {
487                 ast::UseTreeKind::Simple(rename, ..) => {
488                     let orig_ident = items[0].0.prefix.segments.last().unwrap().ident;
489                     if orig_ident.name == keywords::SelfLower.name() {
490                         return;
491                     }
492                     node_ident = rename.unwrap_or(orig_ident);
493                 }
494                 ast::UseTreeKind::Glob => {
495                     node_ident = ast::Ident::from_str("*");
496                 }
497                 ast::UseTreeKind::Nested(_) => {
498                     return;
499                 }
500             }
501
502             let msg = format!("braces around {} is unnecessary", node_ident.name);
503             cx.span_lint(UNUSED_IMPORT_BRACES, item.span, &msg);
504         }
505     }
506 }
507
508 impl LintPass for UnusedImportBraces {
509     fn name(&self) -> &'static str {
510         "UnusedImportBraces"
511     }
512
513     fn get_lints(&self) -> LintArray {
514         lint_array!(UNUSED_IMPORT_BRACES)
515     }
516 }
517
518 impl EarlyLintPass for UnusedImportBraces {
519     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
520         if let ast::ItemKind::Use(ref use_tree) = item.node {
521             self.check_use_tree(cx, use_tree, item);
522         }
523     }
524 }
525
526 declare_lint! {
527     pub(super) UNUSED_ALLOCATION,
528     Warn,
529     "detects unnecessary allocations that can be eliminated"
530 }
531
532 #[derive(Copy, Clone)]
533 pub struct UnusedAllocation;
534
535 impl LintPass for UnusedAllocation {
536     fn name(&self) -> &'static str {
537         "UnusedAllocation"
538     }
539
540     fn get_lints(&self) -> LintArray {
541         lint_array!(UNUSED_ALLOCATION)
542     }
543 }
544
545 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation {
546     fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr) {
547         match e.node {
548             hir::ExprKind::Box(_) => {}
549             _ => return,
550         }
551
552         for adj in cx.tables.expr_adjustments(e) {
553             if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
554                 let msg = match m {
555                     adjustment::AutoBorrowMutability::Immutable =>
556                         "unnecessary allocation, use & instead",
557                     adjustment::AutoBorrowMutability::Mutable { .. }=>
558                         "unnecessary allocation, use &mut instead"
559                 };
560                 cx.span_lint(UNUSED_ALLOCATION, e.span, msg);
561             }
562         }
563     }
564 }