]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/no_effect.rs
Run rustfmt on clippy_lints
[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 use crate::rustc::hir::def::Def;
11 use crate::rustc::hir::{BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource};
12 use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
13 use crate::rustc::{declare_tool_lint, lint_array};
14 use crate::rustc_errors::Applicability;
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)
74                 && fields.iter().all(|field| has_no_effect(cx, &field.expr))
75                 && match *base {
76                     Some(ref base) => has_no_effect(cx, base),
77                     None => true,
78                 }
79         },
80         ExprKind::Call(ref callee, ref args) => {
81             if let ExprKind::Path(ref qpath) = callee.node {
82                 let def = cx.tables.qpath_def(qpath, callee.hir_id);
83                 match def {
84                     Def::Struct(..) | Def::Variant(..) | Def::StructCtor(..) | Def::VariantCtor(..) => {
85                         !has_drop(cx, expr) && args.iter().all(|arg| has_no_effect(cx, arg))
86                     },
87                     _ => false,
88                 }
89             } else {
90                 false
91             }
92         },
93         ExprKind::Block(ref block, _) => {
94             block.stmts.is_empty()
95                 && if let Some(ref expr) = block.expr {
96                     has_no_effect(cx, expr)
97                 } else {
98                     false
99                 }
100         },
101         _ => false,
102     }
103 }
104
105 #[derive(Copy, Clone)]
106 pub struct Pass;
107
108 impl LintPass for Pass {
109     fn get_lints(&self) -> LintArray {
110         lint_array!(NO_EFFECT, UNNECESSARY_OPERATION)
111     }
112 }
113
114 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
115     fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt) {
116         if let StmtKind::Semi(ref expr, _) = stmt.node {
117             if has_no_effect(cx, expr) {
118                 span_lint(cx, NO_EFFECT, stmt.span, "statement with no effect");
119             } else if let Some(reduced) = reduce_expression(cx, expr) {
120                 let mut snippet = String::new();
121                 for e in reduced {
122                     if in_macro(e.span) {
123                         return;
124                     }
125                     if let Some(snip) = snippet_opt(cx, e.span) {
126                         snippet.push_str(&snip);
127                         snippet.push(';');
128                     } else {
129                         return;
130                     }
131                 }
132                 span_lint_and_sugg(
133                     cx,
134                     UNNECESSARY_OPERATION,
135                     stmt.span,
136                     "statement can be reduced",
137                     "replace it with",
138                     snippet,
139                     Applicability::MachineApplicable,
140                 );
141             }
142         }
143     }
144 }
145
146 fn reduce_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr) -> Option<Vec<&'a Expr>> {
147     if in_macro(expr.span) {
148         return None;
149     }
150     match expr.node {
151         ExprKind::Index(ref a, ref b) => Some(vec![&**a, &**b]),
152         ExprKind::Binary(ref binop, ref a, ref b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => {
153             Some(vec![&**a, &**b])
154         },
155         ExprKind::Array(ref v) | ExprKind::Tup(ref v) => Some(v.iter().collect()),
156         ExprKind::Repeat(ref inner, _)
157         | ExprKind::Cast(ref inner, _)
158         | ExprKind::Type(ref inner, _)
159         | ExprKind::Unary(_, ref inner)
160         | ExprKind::Field(ref inner, _)
161         | ExprKind::AddrOf(_, ref inner)
162         | ExprKind::Box(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
163         ExprKind::Struct(_, ref fields, ref base) => {
164             if has_drop(cx, expr) {
165                 None
166             } else {
167                 Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
168             }
169         },
170         ExprKind::Call(ref callee, ref args) => {
171             if let ExprKind::Path(ref qpath) = callee.node {
172                 let def = cx.tables.qpath_def(qpath, callee.hir_id);
173                 match def {
174                     Def::Struct(..) | Def::Variant(..) | Def::StructCtor(..) | Def::VariantCtor(..)
175                         if !has_drop(cx, expr) =>
176                     {
177                         Some(args.iter().collect())
178                     },
179                     _ => None,
180                 }
181             } else {
182                 None
183             }
184         },
185         ExprKind::Block(ref block, _) => {
186             if block.stmts.is_empty() {
187                 block.expr.as_ref().and_then(|e| {
188                     match block.rules {
189                         BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None,
190                         BlockCheckMode::DefaultBlock => Some(vec![&**e]),
191                         // in case of compiler-inserted signaling blocks
192                         _ => reduce_expression(cx, e),
193                     }
194                 })
195             } else {
196                 None
197             }
198         },
199         _ => None,
200     }
201 }