Skip to content

Commit 4c4b1af

Browse files
committed
Merge pull request #928 from oli-obk/unnecessary_operation
add a companion lint to `no_effect` with suggestions for partially (in-)effective statements
2 parents ecca55c + 1e897f1 commit 4c4b1af

22 files changed

+161
-47
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ All notable changes to this project will be documented in this file.
215215
[`unicode_not_nfc`]: https://github.com/Manishearth/rust-clippy/wiki#unicode_not_nfc
216216
[`unit_cmp`]: https://github.com/Manishearth/rust-clippy/wiki#unit_cmp
217217
[`unnecessary_mut_passed`]: https://github.com/Manishearth/rust-clippy/wiki#unnecessary_mut_passed
218+
[`unnecessary_operation`]: https://github.com/Manishearth/rust-clippy/wiki#unnecessary_operation
218219
[`unneeded_field_pattern`]: https://github.com/Manishearth/rust-clippy/wiki#unneeded_field_pattern
219220
[`unsafe_removed_from_name`]: https://github.com/Manishearth/rust-clippy/wiki#unsafe_removed_from_name
220221
[`unstable_as_mut_slice`]: https://github.com/Manishearth/rust-clippy/wiki#unstable_as_mut_slice

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Table of contents:
1717

1818
## Lints
1919

20-
There are 149 lints included in this crate:
20+
There are 150 lints included in this crate:
2121

2222
name | default | meaning
2323
---------------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -154,6 +154,7 @@ name
154154
[unicode_not_nfc](https://github.com/Manishearth/rust-clippy/wiki#unicode_not_nfc) | allow | using a unicode literal not in NFC normal form (see [unicode tr15](http://www.unicode.org/reports/tr15/) for further information)
155155
[unit_cmp](https://github.com/Manishearth/rust-clippy/wiki#unit_cmp) | warn | comparing unit values (which is always `true` or `false`, respectively)
156156
[unnecessary_mut_passed](https://github.com/Manishearth/rust-clippy/wiki#unnecessary_mut_passed) | warn | an argument is passed as a mutable reference although the function/method only demands an immutable reference
157+
[unnecessary_operation](https://github.com/Manishearth/rust-clippy/wiki#unnecessary_operation) | warn | outer expressions with no effect
157158
[unneeded_field_pattern](https://github.com/Manishearth/rust-clippy/wiki#unneeded_field_pattern) | warn | Struct fields are bound to a wildcard instead of using `..`
158159
[unsafe_removed_from_name](https://github.com/Manishearth/rust-clippy/wiki#unsafe_removed_from_name) | warn | unsafe removed from name
159160
[unused_collect](https://github.com/Manishearth/rust-clippy/wiki#unused_collect) | warn | `collect()`ing an iterator without using the result; this is usually better written as a for loop

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
519519
neg_multiply::NEG_MULTIPLY,
520520
new_without_default::NEW_WITHOUT_DEFAULT,
521521
no_effect::NO_EFFECT,
522+
no_effect::UNNECESSARY_OPERATION,
522523
non_expressive_names::MANY_SINGLE_CHAR_NAMES,
523524
open_options::NONSENSICAL_OPEN_OPTIONS,
524525
overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,

src/no_effect.rs

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
2-
use rustc::hir::def::Def;
2+
use rustc::hir::def::{Def, PathResolution};
33
use rustc::hir::{Expr, Expr_, Stmt, StmtSemi};
4-
use utils::{in_macro, span_lint};
4+
use utils::{in_macro, span_lint, snippet_opt, span_lint_and_then};
5+
use std::ops::Deref;
56

67
/// **What it does:** This lint checks for statements which have no effect.
78
///
@@ -16,6 +17,19 @@ declare_lint! {
1617
"statements with no effect"
1718
}
1819

20+
/// **What it does:** This lint checks for expression statements that can be reduced to a sub-expression
21+
///
22+
/// **Why is this bad?** Expressions by themselves often have no side-effects. Having such expressions reduces redability.
23+
///
24+
/// **Known problems:** None.
25+
///
26+
/// **Example:** `compute_array()[0];`
27+
declare_lint! {
28+
pub UNNECESSARY_OPERATION,
29+
Warn,
30+
"outer expressions with no effect"
31+
}
32+
1933
fn has_no_effect(cx: &LateContext, expr: &Expr) -> bool {
2034
if in_macro(cx, expr.span) {
2135
return false;
@@ -68,7 +82,7 @@ pub struct NoEffectPass;
6882

6983
impl LintPass for NoEffectPass {
7084
fn get_lints(&self) -> LintArray {
71-
lint_array!(NO_EFFECT)
85+
lint_array!(NO_EFFECT, UNNECESSARY_OPERATION)
7286
}
7387
}
7488

@@ -77,7 +91,65 @@ impl LateLintPass for NoEffectPass {
7791
if let StmtSemi(ref expr, _) = stmt.node {
7892
if has_no_effect(cx, expr) {
7993
span_lint(cx, NO_EFFECT, stmt.span, "statement with no effect");
94+
} else if let Some(reduced) = reduce_expression(cx, expr) {
95+
let mut snippet = String::new();
96+
for e in reduced {
97+
if in_macro(cx, e.span) {
98+
return;
99+
}
100+
if let Some(snip) = snippet_opt(cx, e.span) {
101+
snippet.push_str(&snip);
102+
snippet.push(';');
103+
} else {
104+
return;
105+
}
106+
}
107+
span_lint_and_then(cx, UNNECESSARY_OPERATION, stmt.span, "statement can be reduced", |db| {
108+
db.span_suggestion(stmt.span, "replace it with", snippet);
109+
});
110+
}
111+
}
112+
}
113+
}
114+
115+
116+
fn reduce_expression<'a>(cx: &LateContext, expr: &'a Expr) -> Option<Vec<&'a Expr>> {
117+
if in_macro(cx, expr.span) {
118+
return None;
119+
}
120+
match expr.node {
121+
Expr_::ExprIndex(ref a, ref b) |
122+
Expr_::ExprBinary(_, ref a, ref b) => Some(vec![&**a, &**b]),
123+
Expr_::ExprVec(ref v) |
124+
Expr_::ExprTup(ref v) => Some(v.iter().map(Deref::deref).collect()),
125+
Expr_::ExprRepeat(ref inner, _) |
126+
Expr_::ExprCast(ref inner, _) |
127+
Expr_::ExprType(ref inner, _) |
128+
Expr_::ExprUnary(_, ref inner) |
129+
Expr_::ExprField(ref inner, _) |
130+
Expr_::ExprTupField(ref inner, _) |
131+
Expr_::ExprAddrOf(_, ref inner) |
132+
Expr_::ExprBox(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
133+
Expr_::ExprStruct(_, ref fields, ref base) => Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect()),
134+
Expr_::ExprCall(ref callee, ref args) => {
135+
match cx.tcx.def_map.borrow().get(&callee.id).map(PathResolution::full_def) {
136+
Some(Def::Struct(..)) |
137+
Some(Def::Variant(..)) => Some(args.iter().map(Deref::deref).collect()),
138+
_ => None,
139+
}
140+
}
141+
Expr_::ExprBlock(ref block) => {
142+
if block.stmts.is_empty() {
143+
block.expr.as_ref().and_then(|e| if e.span == expr.span {
144+
// in case of compiler-inserted signaling blocks
145+
reduce_expression(cx, e)
146+
} else {
147+
Some(vec![e])
148+
})
149+
} else {
150+
None
80151
}
81152
}
153+
_ => None,
82154
}
83155
}

tests/compile-fail/absurd-extreme-comparisons.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#![plugin(clippy)]
33

44
#![deny(absurd_extreme_comparisons)]
5-
#![allow(unused, eq_op, no_effect)]
5+
#![allow(unused, eq_op, no_effect, unnecessary_operation)]
66
fn main() {
77
const Z: u32 = 0;
88

tests/compile-fail/arithmetic.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#![plugin(clippy)]
33

44
#![deny(integer_arithmetic, float_arithmetic)]
5-
#![allow(unused, shadow_reuse, shadow_unrelated, no_effect)]
5+
#![allow(unused, shadow_reuse, shadow_unrelated, no_effect, unnecessary_operation)]
66
fn main() {
77
let i = 1i32;
88
1 + i; //~ERROR integer arithmetic detected
@@ -11,17 +11,17 @@ fn main() {
1111
i / 2; // no error, this is part of the expression in the preceding line
1212
i - 2 + 2 - i; //~ERROR integer arithmetic detected
1313
-i; //~ERROR integer arithmetic detected
14-
14+
1515
i & 1; // no wrapping
16-
i | 1;
16+
i | 1;
1717
i ^ 1;
1818
i >> 1;
1919
i << 1;
20-
20+
2121
let f = 1.0f32;
22-
22+
2323
f * 2.0; //~ERROR floating-point arithmetic detected
24-
24+
2525
1.0 + f; //~ERROR floating-point arithmetic detected
2626
f * 2.0; //~ERROR floating-point arithmetic detected
2727
f / 2.0; //~ERROR floating-point arithmetic detected

tests/compile-fail/array_indexing.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
#![deny(indexing_slicing)]
55
#![deny(out_of_bounds_indexing)]
6-
#![allow(no_effect)]
6+
#![allow(no_effect, unnecessary_operation)]
77

88
fn main() {
99
let x = [1,2,3,4];

tests/compile-fail/bit_masks.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const THREE_BITS : i64 = 7;
55
const EVEN_MORE_REDIRECTION : i64 = THREE_BITS;
66

77
#[deny(bad_bit_mask)]
8-
#[allow(ineffective_bit_mask, identity_op, no_effect)]
8+
#[allow(ineffective_bit_mask, identity_op, no_effect, unnecessary_operation)]
99
fn main() {
1010
let x = 5;
1111

@@ -45,7 +45,7 @@ fn main() {
4545
}
4646

4747
#[deny(ineffective_bit_mask)]
48-
#[allow(bad_bit_mask, no_effect)]
48+
#[allow(bad_bit_mask, no_effect, unnecessary_operation)]
4949
fn ineffective() {
5050
let x = 5;
5151

tests/compile-fail/cast.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#![plugin(clippy)]
33

44
#[deny(cast_precision_loss, cast_possible_truncation, cast_sign_loss, cast_possible_wrap)]
5-
#[allow(no_effect)]
5+
#[allow(no_effect, unnecessary_operation)]
66
fn main() {
77
// Test cast_precision_loss
88
1i32 as f32; //~ERROR casting i32 to f32 causes a loss of precision (i32 is 32 bits wide, but f32's mantissa is only 23 bits wide)

tests/compile-fail/cmp_nan.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#![plugin(clippy)]
33

44
#[deny(cmp_nan)]
5-
#[allow(float_cmp, no_effect)]
5+
#[allow(float_cmp, no_effect, unnecessary_operation)]
66
fn main() {
77
let x = 5f32;
88
x == std::f32::NAN; //~ERROR doomed comparison with NAN

tests/compile-fail/cmp_owned.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#![plugin(clippy)]
33

44
#[deny(cmp_owned)]
5+
#[allow(unnecessary_operation)]
56
fn main() {
67
fn with_to_string(x : &str) {
78
x != "foo".to_string();

tests/compile-fail/copies.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![feature(plugin, inclusive_range_syntax)]
22
#![plugin(clippy)]
33

4-
#![allow(dead_code, no_effect)]
4+
#![allow(dead_code, no_effect, unnecessary_operation)]
55
#![allow(let_and_return)]
66
#![allow(needless_return)]
77
#![allow(unused_variables)]

tests/compile-fail/eq_op.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
#[deny(eq_op)]
55
#[allow(identity_op)]
6-
#[allow(no_effect, unused_variables)]
6+
#[allow(no_effect, unused_variables, unnecessary_operation)]
77
#[deny(nonminimal_bool)]
88
fn main() {
99
// simple values and comparisons

tests/compile-fail/float_cmp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#![plugin(clippy)]
33

44
#![deny(float_cmp)]
5-
#![allow(unused, no_effect)]
5+
#![allow(unused, no_effect, unnecessary_operation)]
66

77
use std::ops::Add;
88

tests/compile-fail/identity_op.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const ONE : i64 = 1;
55
const NEG_ONE : i64 = -1;
66
const ZERO : i64 = 0;
77

8-
#[allow(eq_op, no_effect)]
8+
#[allow(eq_op, no_effect, unnecessary_operation)]
99
#[deny(identity_op)]
1010
fn main() {
1111
let x = 0;

tests/compile-fail/invalid_upcast_comparisons.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#![plugin(clippy)]
33

44
#![deny(invalid_upcast_comparisons)]
5-
#![allow(unused, eq_op, no_effect)]
5+
#![allow(unused, eq_op, no_effect, unnecessary_operation)]
66
fn main() {
77
let zero: u32 = 0;
88
let u8_max: u8 = 255;

tests/compile-fail/methods.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ struct MyErrorWithParam<T> {
341341
x: T
342342
}
343343

344+
#[allow(unnecessary_operation)]
344345
fn starts_with() {
345346
"".chars().next() == Some(' ');
346347
//~^ ERROR starts_with

tests/compile-fail/modulo_one.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![feature(plugin)]
22
#![plugin(clippy)]
33
#![deny(modulo_one)]
4-
#![allow(no_effect)]
4+
#![allow(no_effect, unnecessary_operation)]
55

66
fn main() {
77
10 % 1; //~ERROR any number modulo 1 will be 0

tests/compile-fail/mut_mut.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![feature(plugin)]
22
#![plugin(clippy)]
33

4-
#![allow(unused, no_effect)]
4+
#![allow(unused, no_effect, unnecessary_operation)]
55

66
//#![plugin(regex_macros)]
77
//extern crate regex;

tests/compile-fail/neg_multiply.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22

33
#![plugin(clippy)]
44
#![deny(neg_multiply)]
5-
#![allow(no_effect)]
5+
#![allow(no_effect, unnecessary_operation)]
66

77
use std::ops::Mul;
88

99
struct X;
1010

1111
impl Mul<isize> for X {
1212
type Output = X;
13-
13+
1414
fn mul(self, _r: isize) -> Self {
1515
self
1616
}
1717
}
1818

1919
impl Mul<X> for isize {
2020
type Output = X;
21-
21+
2222
fn mul(self, _r: X) -> X {
2323
X
2424
}
@@ -34,7 +34,7 @@ fn main() {
3434
//~^ ERROR Negation by multiplying with -1
3535

3636
-1 * -1; // should be ok
37-
37+
3838
X * -1; // should be ok
3939
-1 * X; // should also be ok
4040
}

0 commit comments

Comments
 (0)