Skip to content

Commit 6b22bba

Browse files
committed
Lint on underscore variable assignment
Fix tests after no_effect update Add a drop testcase Don't lint _ variables in macro expansion Address review comments and update tests Don't shadow unnecessary operation lint if no_effect is allowed Revert shadowing change and remove no_effect allows Update clippy_lints/src/no_effect.rs Co-authored-by: Takayuki Nakata <[email protected]> Update clippy_lints/src/no_effect.rs Co-authored-by: Takayuki Nakata <[email protected]> Address review comments
1 parent 4996e17 commit 6b22bba

11 files changed

+170
-86
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2899,6 +2899,7 @@ Released 2018-09-13
28992899
[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
29002900
[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
29012901
[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
2902+
[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding
29022903
[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
29032904
[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
29042905
[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ store.register_lints(&[
362362
neg_multiply::NEG_MULTIPLY,
363363
new_without_default::NEW_WITHOUT_DEFAULT,
364364
no_effect::NO_EFFECT,
365+
no_effect::NO_EFFECT_UNDERSCORE_BINDING,
365366
no_effect::UNNECESSARY_OPERATION,
366367
non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
367368
non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,

clippy_lints/src/lib.register_pedantic.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
7272
LintId::of(needless_continue::NEEDLESS_CONTINUE),
7373
LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
7474
LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
75+
LintId::of(no_effect::NO_EFFECT_UNDERSCORE_BINDING),
7576
LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
7677
LintId::of(non_expressive_names::SIMILAR_NAMES),
7778
LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),

clippy_lints/src/no_effect.rs

Lines changed: 115 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
2+
use clippy_utils::is_lint_allowed;
23
use clippy_utils::source::snippet_opt;
34
use clippy_utils::ty::has_drop;
45
use rustc_errors::Applicability;
56
use rustc_hir::def::{DefKind, Res};
6-
use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource};
7+
use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource};
78
use rustc_lint::{LateContext, LateLintPass};
89
use rustc_session::{declare_lint_pass, declare_tool_lint};
910
use std::ops::Deref;
@@ -13,7 +14,7 @@ declare_clippy_lint! {
1314
/// Checks for statements which have no effect.
1415
///
1516
/// ### Why is this bad?
16-
/// Similar to dead code, these statements are actually
17+
/// Unlike dead code, these statements are actually
1718
/// executed. However, as they have no effect, all they do is make the code less
1819
/// readable.
1920
///
@@ -26,6 +27,28 @@ declare_clippy_lint! {
2627
"statements with no effect"
2728
}
2829

30+
declare_clippy_lint! {
31+
/// ### What it does
32+
/// Checks for binding to underscore prefixed variable without side-effects.
33+
///
34+
/// ### Why is this bad?
35+
/// Unlike dead code, these bindings are actually
36+
/// executed. However, as they have no effect and shouldn't be used further on, all they
37+
/// do is make the code less readable.
38+
///
39+
/// ### Known problems
40+
/// Further usage of this variable is not checked, which can lead to false positives if it is
41+
/// used later in the code.
42+
///
43+
/// ### Example
44+
/// ```rust,ignore
45+
/// let _i_serve_no_purpose = 1;
46+
/// ```
47+
pub NO_EFFECT_UNDERSCORE_BINDING,
48+
pedantic,
49+
"binding to `_` prefixed variable with no side-effect"
50+
}
51+
2952
declare_clippy_lint! {
3053
/// ### What it does
3154
/// Checks for expression statements that can be reduced to a
@@ -44,6 +67,46 @@ declare_clippy_lint! {
4467
"outer expressions with no effect"
4568
}
4669

70+
declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION, NO_EFFECT_UNDERSCORE_BINDING]);
71+
72+
impl<'tcx> LateLintPass<'tcx> for NoEffect {
73+
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
74+
if check_no_effect(cx, stmt) {
75+
return;
76+
}
77+
check_unnecessary_operation(cx, stmt);
78+
}
79+
}
80+
81+
fn check_no_effect(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> bool {
82+
if let StmtKind::Semi(expr) = stmt.kind {
83+
if has_no_effect(cx, expr) {
84+
span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
85+
return true;
86+
}
87+
} else if let StmtKind::Local(local) = stmt.kind {
88+
if_chain! {
89+
if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id);
90+
if let Some(init) = local.init;
91+
if !local.pat.span.from_expansion();
92+
if has_no_effect(cx, init);
93+
if let PatKind::Binding(_, _, ident, _) = local.pat.kind;
94+
if ident.name.to_ident_string().starts_with('_');
95+
then {
96+
span_lint_hir(
97+
cx,
98+
NO_EFFECT_UNDERSCORE_BINDING,
99+
init.hir_id,
100+
stmt.span,
101+
"binding to `_` prefixed variable with no side-effect"
102+
);
103+
return true;
104+
}
105+
}
106+
}
107+
false
108+
}
109+
47110
fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
48111
if expr.span.from_expansion() {
49112
return false;
@@ -88,71 +151,59 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
88151
}
89152
}
90153

91-
declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION]);
92-
93-
impl<'tcx> LateLintPass<'tcx> for NoEffect {
94-
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
95-
if let StmtKind::Semi(expr) = stmt.kind {
96-
if has_no_effect(cx, expr) {
97-
span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
98-
} else if let Some(reduced) = reduce_expression(cx, expr) {
99-
for e in &reduced {
100-
if e.span.from_expansion() {
101-
return;
102-
}
103-
}
104-
if let ExprKind::Index(..) = &expr.kind {
105-
let snippet;
106-
if_chain! {
107-
if let Some(arr) = snippet_opt(cx, reduced[0].span);
108-
if let Some(func) = snippet_opt(cx, reduced[1].span);
109-
then {
110-
snippet = format!("assert!({}.len() > {});", &arr, &func);
111-
} else {
112-
return;
113-
}
114-
}
115-
span_lint_hir_and_then(
116-
cx,
117-
UNNECESSARY_OPERATION,
118-
expr.hir_id,
119-
stmt.span,
120-
"unnecessary operation",
121-
|diag| {
122-
diag.span_suggestion(
123-
stmt.span,
124-
"statement can be written as",
125-
snippet,
126-
Applicability::MaybeIncorrect,
127-
);
128-
},
129-
);
154+
fn check_unnecessary_operation(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
155+
if_chain! {
156+
if let StmtKind::Semi(expr) = stmt.kind;
157+
if let Some(reduced) = reduce_expression(cx, expr);
158+
if !&reduced.iter().any(|e| e.span.from_expansion());
159+
then {
160+
if let ExprKind::Index(..) = &expr.kind {
161+
let snippet;
162+
if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) {
163+
snippet = format!("assert!({}.len() > {});", &arr, &func);
130164
} else {
131-
let mut snippet = String::new();
132-
for e in reduced {
133-
if let Some(snip) = snippet_opt(cx, e.span) {
134-
snippet.push_str(&snip);
135-
snippet.push(';');
136-
} else {
137-
return;
138-
}
165+
return;
166+
}
167+
span_lint_hir_and_then(
168+
cx,
169+
UNNECESSARY_OPERATION,
170+
expr.hir_id,
171+
stmt.span,
172+
"unnecessary operation",
173+
|diag| {
174+
diag.span_suggestion(
175+
stmt.span,
176+
"statement can be written as",
177+
snippet,
178+
Applicability::MaybeIncorrect,
179+
);
180+
},
181+
);
182+
} else {
183+
let mut snippet = String::new();
184+
for e in reduced {
185+
if let Some(snip) = snippet_opt(cx, e.span) {
186+
snippet.push_str(&snip);
187+
snippet.push(';');
188+
} else {
189+
return;
139190
}
140-
span_lint_hir_and_then(
141-
cx,
142-
UNNECESSARY_OPERATION,
143-
expr.hir_id,
144-
stmt.span,
145-
"unnecessary operation",
146-
|diag| {
147-
diag.span_suggestion(
148-
stmt.span,
149-
"statement can be reduced to",
150-
snippet,
151-
Applicability::MachineApplicable,
152-
);
153-
},
154-
);
155191
}
192+
span_lint_hir_and_then(
193+
cx,
194+
UNNECESSARY_OPERATION,
195+
expr.hir_id,
196+
stmt.span,
197+
"unnecessary operation",
198+
|diag| {
199+
diag.span_suggestion(
200+
stmt.span,
201+
"statement can be reduced to",
202+
snippet,
203+
Applicability::MachineApplicable,
204+
);
205+
},
206+
);
156207
}
157208
}
158209
}

tests/ui/cfg_attr_rustfmt.fixed

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

4-
#![allow(unused, clippy::no_effect)]
4+
#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
55
#![warn(clippy::deprecated_cfg_attr)]
66

77
// This doesn't get linted, see known problems

tests/ui/cfg_attr_rustfmt.rs

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

4-
#![allow(unused, clippy::no_effect)]
4+
#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
55
#![warn(clippy::deprecated_cfg_attr)]
66

77
// This doesn't get linted, see known problems

tests/ui/no_effect.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![feature(box_syntax)]
2-
#![warn(clippy::no_effect)]
2+
#![warn(clippy::no_effect_underscore_binding)]
33
#![allow(dead_code)]
44
#![allow(path_statements)]
55
#![allow(clippy::deref_addrof)]
@@ -90,13 +90,19 @@ fn main() {
9090
|| x += 5;
9191
let s: String = "foo".into();
9292
FooString { s: s };
93+
let _unused = 1;
94+
let _penguin = || println!("Some helpful closure");
95+
let _duck = Struct { field: 0 };
96+
let _cat = [2, 4, 6, 8][2];
9397

9498
#[allow(clippy::no_effect)]
9599
0;
96100

97101
// Do not warn
98102
get_number();
99103
unsafe { unsafe_fn() };
104+
let _used = get_struct();
105+
let _x = vec![1];
100106
DropUnit;
101107
DropStruct { field: 0 };
102108
DropTuple(0);

tests/ui/no_effect.stderr

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,5 +156,31 @@ error: statement with no effect
156156
LL | FooString { s: s };
157157
| ^^^^^^^^^^^^^^^^^^^
158158

159-
error: aborting due to 26 previous errors
159+
error: binding to `_` prefixed variable with no side-effect
160+
--> $DIR/no_effect.rs:93:5
161+
|
162+
LL | let _unused = 1;
163+
| ^^^^^^^^^^^^^^^^
164+
|
165+
= note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings`
166+
167+
error: binding to `_` prefixed variable with no side-effect
168+
--> $DIR/no_effect.rs:94:5
169+
|
170+
LL | let _penguin = || println!("Some helpful closure");
171+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
172+
173+
error: binding to `_` prefixed variable with no side-effect
174+
--> $DIR/no_effect.rs:95:5
175+
|
176+
LL | let _duck = Struct { field: 0 };
177+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
178+
179+
error: binding to `_` prefixed variable with no side-effect
180+
--> $DIR/no_effect.rs:96:5
181+
|
182+
LL | let _cat = [2, 4, 6, 8][2];
183+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
184+
185+
error: aborting due to 30 previous errors
160186

tests/ui/option_if_let_else.fixed

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
// edition:2018
22
// run-rustfix
33
#![warn(clippy::option_if_let_else)]
4-
#![allow(clippy::redundant_closure)]
5-
#![allow(clippy::ref_option_ref, clippy::equatable_if_let)]
4+
#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)]
65

76
fn bad1(string: Option<&str>) -> (bool, &str) {
87
string.map_or((false, "hello"), |x| (true, x))

tests/ui/option_if_let_else.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
// edition:2018
22
// run-rustfix
33
#![warn(clippy::option_if_let_else)]
4-
#![allow(clippy::redundant_closure)]
5-
#![allow(clippy::ref_option_ref, clippy::equatable_if_let)]
4+
#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)]
65

76
fn bad1(string: Option<&str>) -> (bool, &str) {
87
if let Some(x) = string {

0 commit comments

Comments
 (0)