]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/no_effect.rs
Merge pull request #3269 from rust-lang-nursery/relicense
[rust.git] / clippy_lints / src / no_effect.rs
1 // Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
3 //
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
9
10
11 use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
12 use crate::rustc::{declare_tool_lint, lint_array};
13 use crate::rustc::hir::def::Def;
14 use crate::rustc::hir::{BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource};
15 use crate::utils::{has_drop, in_macro, snippet_opt, span_lint, span_lint_and_sugg};
16 use std::ops::Deref;
17
18 /// **What it does:** Checks for statements which have no effect.
19 ///
20 /// **Why is this bad?** Similar to dead code, these statements are actually
21 /// executed. However, as they have no effect, all they do is make the code less
22 /// readable.
23 ///
24 /// **Known problems:** None.
25 ///
26 /// **Example:**
27 /// ```rust
28 /// 0;
29 /// ```
30 declare_clippy_lint! {
31     pub NO_EFFECT,
32     complexity,
33     "statements with no effect"
34 }
35
36 /// **What it does:** Checks for expression statements that can be reduced to a
37 /// sub-expression.
38 ///
39 /// **Why is this bad?** Expressions by themselves often have no side-effects.
40 /// Having such expressions reduces readability.
41 ///
42 /// **Known problems:** None.
43 ///
44 /// **Example:**
45 /// ```rust
46 /// compute_array()[0];
47 /// ```
48 declare_clippy_lint! {
49     pub UNNECESSARY_OPERATION,
50     complexity,
51     "outer expressions with no effect"
52 }
53
54 fn has_no_effect(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
55     if in_macro(expr.span) {
56         return false;
57     }
58     match expr.node {
59         ExprKind::Lit(..) | ExprKind::Closure(.., _) => true,
60         ExprKind::Path(..) => !has_drop(cx, expr),
61         ExprKind::Index(ref a, ref b) | ExprKind::Binary(_, ref a, ref b) => {
62             has_no_effect(cx, a) && has_no_effect(cx, b)
63         },
64         ExprKind::Array(ref v) | ExprKind::Tup(ref v) => v.iter().all(|val| has_no_effect(cx, val)),
65         ExprKind::Repeat(ref inner, _) |
66         ExprKind::Cast(ref inner, _) |
67         ExprKind::Type(ref inner, _) |
68         ExprKind::Unary(_, ref inner) |
69         ExprKind::Field(ref inner, _) |
70         ExprKind::AddrOf(_, ref inner) |
71         ExprKind::Box(ref inner) => has_no_effect(cx, inner),
72         ExprKind::Struct(_, ref fields, ref base) => {
73             !has_drop(cx, expr) && fields.iter().all(|field| has_no_effect(cx, &field.expr)) && match *base {
74                 Some(ref base) => has_no_effect(cx, base),
75                 None => true,
76             }
77         },
78         ExprKind::Call(ref callee, ref args) => if let ExprKind::Path(ref qpath) = callee.node {
79             let def = cx.tables.qpath_def(qpath, callee.hir_id);
80             match def {
81                 Def::Struct(..) | Def::Variant(..) | Def::StructCtor(..) | Def::VariantCtor(..) => {
82                     !has_drop(cx, expr) && args.iter().all(|arg| has_no_effect(cx, arg))
83                 },
84                 _ => false,
85             }
86         } else {
87             false
88         },
89         ExprKind::Block(ref block, _) => {
90             block.stmts.is_empty() && if let Some(ref expr) = block.expr {
91                 has_no_effect(cx, expr)
92             } else {
93                 false
94             }
95         },
96         _ => false,
97     }
98 }
99
100 #[derive(Copy, Clone)]
101 pub struct Pass;
102
103 impl LintPass for Pass {
104     fn get_lints(&self) -> LintArray {
105         lint_array!(NO_EFFECT, UNNECESSARY_OPERATION)
106     }
107 }
108
109 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
110     fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt) {
111         if let StmtKind::Semi(ref expr, _) = stmt.node {
112             if has_no_effect(cx, expr) {
113                 span_lint(cx, NO_EFFECT, stmt.span, "statement with no effect");
114             } else if let Some(reduced) = reduce_expression(cx, expr) {
115                 let mut snippet = String::new();
116                 for e in reduced {
117                     if in_macro(e.span) {
118                         return;
119                     }
120                     if let Some(snip) = snippet_opt(cx, e.span) {
121                         snippet.push_str(&snip);
122                         snippet.push(';');
123                     } else {
124                         return;
125                     }
126                 }
127                 span_lint_and_sugg(
128                     cx,
129                     UNNECESSARY_OPERATION,
130                     stmt.span,
131                     "statement can be reduced",
132                     "replace it with",
133                     snippet,
134                 );
135             }
136         }
137     }
138 }
139
140
141 fn reduce_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr) -> Option<Vec<&'a Expr>> {
142     if in_macro(expr.span) {
143         return None;
144     }
145     match expr.node {
146         ExprKind::Index(ref a, ref b) => Some(vec![&**a, &**b]),
147         ExprKind::Binary(ref binop, ref a, ref b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => {
148             Some(vec![&**a, &**b])
149         },
150         ExprKind::Array(ref v) | ExprKind::Tup(ref v) => Some(v.iter().collect()),
151         ExprKind::Repeat(ref inner, _) |
152         ExprKind::Cast(ref inner, _) |
153         ExprKind::Type(ref inner, _) |
154         ExprKind::Unary(_, ref inner) |
155         ExprKind::Field(ref inner, _) |
156         ExprKind::AddrOf(_, ref inner) |
157         ExprKind::Box(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
158         ExprKind::Struct(_, ref fields, ref base) => if has_drop(cx, expr) {
159             None
160         } else {
161             Some(
162                 fields
163                     .iter()
164                     .map(|f| &f.expr)
165                     .chain(base)
166                     .map(Deref::deref)
167                     .collect(),
168             )
169         },
170         ExprKind::Call(ref callee, ref args) => if let ExprKind::Path(ref qpath) = callee.node {
171             let def = cx.tables.qpath_def(qpath, callee.hir_id);
172             match def {
173                 Def::Struct(..) | Def::Variant(..) | Def::StructCtor(..) | Def::VariantCtor(..)
174                     if !has_drop(cx, expr) =>
175                 {
176                     Some(args.iter().collect())
177                 },
178                 _ => None,
179             }
180         } else {
181             None
182         },
183         ExprKind::Block(ref block, _) => {
184             if block.stmts.is_empty() {
185                 block.expr.as_ref().and_then(|e| {
186                     match block.rules {
187                         BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None,
188                         BlockCheckMode::DefaultBlock => Some(vec![&**e]),
189                         // in case of compiler-inserted signaling blocks
190                         _ => reduce_expression(cx, e),
191                     }
192                 })
193             } else {
194                 None
195             }
196         },
197         _ => None,
198     }
199 }