]>
Commit | Line | Data |
---|---|---|
ea8adc8c XL |
1 | use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; |
2 | use rustc::hir::def::Def; | |
abe05a73 XL |
3 | use rustc::hir::{BiAnd, BiOr, BlockCheckMode, Expr, Expr_, Stmt, StmtSemi, UnsafeSource}; |
4 | use utils::{has_drop, in_macro, snippet_opt, span_lint, span_lint_and_sugg}; | |
ea8adc8c XL |
5 | use std::ops::Deref; |
6 | ||
7 | /// **What it does:** Checks for statements which have no effect. | |
8 | /// | |
9 | /// **Why is this bad?** Similar to dead code, these statements are actually | |
10 | /// executed. However, as they have no effect, all they do is make the code less | |
11 | /// readable. | |
12 | /// | |
13 | /// **Known problems:** None. | |
14 | /// | |
15 | /// **Example:** | |
16 | /// ```rust | |
17 | /// 0; | |
18 | /// ``` | |
19 | declare_lint! { | |
20 | pub NO_EFFECT, | |
21 | Warn, | |
22 | "statements with no effect" | |
23 | } | |
24 | ||
25 | /// **What it does:** Checks for expression statements that can be reduced to a | |
26 | /// sub-expression. | |
27 | /// | |
28 | /// **Why is this bad?** Expressions by themselves often have no side-effects. | |
29 | /// Having such expressions reduces readability. | |
30 | /// | |
31 | /// **Known problems:** None. | |
32 | /// | |
33 | /// **Example:** | |
34 | /// ```rust | |
35 | /// compute_array()[0]; | |
36 | /// ``` | |
37 | declare_lint! { | |
38 | pub UNNECESSARY_OPERATION, | |
39 | Warn, | |
40 | "outer expressions with no effect" | |
41 | } | |
42 | ||
43 | fn has_no_effect(cx: &LateContext, expr: &Expr) -> bool { | |
44 | if in_macro(expr.span) { | |
45 | return false; | |
46 | } | |
47 | match expr.node { | |
abe05a73 XL |
48 | Expr_::ExprLit(..) | Expr_::ExprClosure(.., _) => true, |
49 | Expr_::ExprPath(..) => !has_drop(cx, expr), | |
50 | Expr_::ExprIndex(ref a, ref b) | Expr_::ExprBinary(_, ref a, ref b) => { | |
51 | has_no_effect(cx, a) && has_no_effect(cx, b) | |
52 | }, | |
53 | Expr_::ExprArray(ref v) | Expr_::ExprTup(ref v) => v.iter().all(|val| has_no_effect(cx, val)), | |
ea8adc8c XL |
54 | Expr_::ExprRepeat(ref inner, _) | |
55 | Expr_::ExprCast(ref inner, _) | | |
56 | Expr_::ExprType(ref inner, _) | | |
57 | Expr_::ExprUnary(_, ref inner) | | |
58 | Expr_::ExprField(ref inner, _) | | |
59 | Expr_::ExprTupField(ref inner, _) | | |
60 | Expr_::ExprAddrOf(_, ref inner) | | |
61 | Expr_::ExprBox(ref inner) => has_no_effect(cx, inner), | |
62 | Expr_::ExprStruct(_, ref fields, ref base) => { | |
abe05a73 XL |
63 | !has_drop(cx, expr) && fields.iter().all(|field| has_no_effect(cx, &field.expr)) && match *base { |
64 | Some(ref base) => has_no_effect(cx, base), | |
65 | None => true, | |
66 | } | |
ea8adc8c | 67 | }, |
abe05a73 XL |
68 | Expr_::ExprCall(ref callee, ref args) => if let Expr_::ExprPath(ref qpath) = callee.node { |
69 | let def = cx.tables.qpath_def(qpath, callee.hir_id); | |
70 | match def { | |
71 | Def::Struct(..) | Def::Variant(..) | Def::StructCtor(..) | Def::VariantCtor(..) => { | |
72 | !has_drop(cx, expr) && args.iter().all(|arg| has_no_effect(cx, arg)) | |
73 | }, | |
74 | _ => false, | |
ea8adc8c | 75 | } |
abe05a73 XL |
76 | } else { |
77 | false | |
ea8adc8c XL |
78 | }, |
79 | Expr_::ExprBlock(ref block) => { | |
abe05a73 XL |
80 | block.stmts.is_empty() && if let Some(ref expr) = block.expr { |
81 | has_no_effect(cx, expr) | |
82 | } else { | |
83 | false | |
84 | } | |
ea8adc8c XL |
85 | }, |
86 | _ => false, | |
87 | } | |
88 | } | |
89 | ||
90 | #[derive(Copy, Clone)] | |
91 | pub struct Pass; | |
92 | ||
93 | impl LintPass for Pass { | |
94 | fn get_lints(&self) -> LintArray { | |
95 | lint_array!(NO_EFFECT, UNNECESSARY_OPERATION) | |
96 | } | |
97 | } | |
98 | ||
99 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { | |
100 | fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt) { | |
101 | if let StmtSemi(ref expr, _) = stmt.node { | |
102 | if has_no_effect(cx, expr) { | |
103 | span_lint(cx, NO_EFFECT, stmt.span, "statement with no effect"); | |
104 | } else if let Some(reduced) = reduce_expression(cx, expr) { | |
105 | let mut snippet = String::new(); | |
106 | for e in reduced { | |
107 | if in_macro(e.span) { | |
108 | return; | |
109 | } | |
110 | if let Some(snip) = snippet_opt(cx, e.span) { | |
111 | snippet.push_str(&snip); | |
112 | snippet.push(';'); | |
113 | } else { | |
114 | return; | |
115 | } | |
116 | } | |
117 | span_lint_and_sugg( | |
118 | cx, | |
119 | UNNECESSARY_OPERATION, | |
120 | stmt.span, | |
121 | "statement can be reduced", | |
122 | "replace it with", | |
123 | snippet, | |
124 | ); | |
125 | } | |
126 | } | |
127 | } | |
128 | } | |
129 | ||
130 | ||
131 | fn reduce_expression<'a>(cx: &LateContext, expr: &'a Expr) -> Option<Vec<&'a Expr>> { | |
132 | if in_macro(expr.span) { | |
133 | return None; | |
134 | } | |
135 | match expr.node { | |
136 | Expr_::ExprIndex(ref a, ref b) => Some(vec![&**a, &**b]), | |
137 | Expr_::ExprBinary(ref binop, ref a, ref b) if binop.node != BiAnd && binop.node != BiOr => { | |
138 | Some(vec![&**a, &**b]) | |
139 | }, | |
abe05a73 | 140 | Expr_::ExprArray(ref v) | Expr_::ExprTup(ref v) => Some(v.iter().collect()), |
ea8adc8c XL |
141 | Expr_::ExprRepeat(ref inner, _) | |
142 | Expr_::ExprCast(ref inner, _) | | |
143 | Expr_::ExprType(ref inner, _) | | |
144 | Expr_::ExprUnary(_, ref inner) | | |
145 | Expr_::ExprField(ref inner, _) | | |
146 | Expr_::ExprTupField(ref inner, _) | | |
147 | Expr_::ExprAddrOf(_, ref inner) | | |
148 | Expr_::ExprBox(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])), | |
abe05a73 XL |
149 | Expr_::ExprStruct(_, ref fields, ref base) => if has_drop(cx, expr) { |
150 | None | |
151 | } else { | |
ea8adc8c XL |
152 | Some( |
153 | fields | |
154 | .iter() | |
155 | .map(|f| &f.expr) | |
156 | .chain(base) | |
157 | .map(Deref::deref) | |
158 | .collect(), | |
159 | ) | |
160 | }, | |
abe05a73 XL |
161 | Expr_::ExprCall(ref callee, ref args) => if let Expr_::ExprPath(ref qpath) = callee.node { |
162 | let def = cx.tables.qpath_def(qpath, callee.hir_id); | |
163 | match def { | |
164 | Def::Struct(..) | Def::Variant(..) | Def::StructCtor(..) | Def::VariantCtor(..) | |
165 | if !has_drop(cx, expr) => | |
166 | { | |
167 | Some(args.iter().collect()) | |
168 | }, | |
169 | _ => None, | |
ea8adc8c | 170 | } |
abe05a73 XL |
171 | } else { |
172 | None | |
ea8adc8c XL |
173 | }, |
174 | Expr_::ExprBlock(ref block) => { | |
175 | if block.stmts.is_empty() { | |
176 | block.expr.as_ref().and_then(|e| { | |
177 | match block.rules { | |
178 | BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None, | |
179 | BlockCheckMode::DefaultBlock => Some(vec![&**e]), | |
180 | // in case of compiler-inserted signaling blocks | |
181 | _ => reduce_expression(cx, e), | |
182 | } | |
183 | }) | |
184 | } else { | |
185 | None | |
186 | } | |
187 | }, | |
188 | _ => None, | |
189 | } | |
190 | } |