Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect underscored variables with no side effects #7775

Merged
merged 1 commit into from
Oct 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2899,6 +2899,7 @@ Released 2018-09-13
[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding
[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/lib.register_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ store.register_lints(&[
neg_multiply::NEG_MULTIPLY,
new_without_default::NEW_WITHOUT_DEFAULT,
no_effect::NO_EFFECT,
no_effect::NO_EFFECT_UNDERSCORE_BINDING,
no_effect::UNNECESSARY_OPERATION,
non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/lib.register_pedantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(needless_continue::NEEDLESS_CONTINUE),
LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
LintId::of(no_effect::NO_EFFECT_UNDERSCORE_BINDING),
LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
LintId::of(non_expressive_names::SIMILAR_NAMES),
LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
Expand Down
179 changes: 115 additions & 64 deletions clippy_lints/src/no_effect.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
use clippy_utils::is_lint_allowed;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::has_drop;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource};
use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use std::ops::Deref;
Expand All @@ -13,7 +14,7 @@ declare_clippy_lint! {
/// Checks for statements which have no effect.
///
/// ### Why is this bad?
/// Similar to dead code, these statements are actually
/// Unlike dead code, these statements are actually
/// executed. However, as they have no effect, all they do is make the code less
/// readable.
///
Expand All @@ -26,6 +27,28 @@ declare_clippy_lint! {
"statements with no effect"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for binding to underscore prefixed variable without side-effects.
///
/// ### Why is this bad?
/// Unlike dead code, these bindings are actually
/// executed. However, as they have no effect and shouldn't be used further on, all they
/// do is make the code less readable.
///
/// ### Known problems
/// Further usage of this variable is not checked, which can lead to false positives if it is
/// used later in the code.
///
/// ### Example
/// ```rust,ignore
/// let _i_serve_no_purpose = 1;
/// ```
pub NO_EFFECT_UNDERSCORE_BINDING,
pedantic,
"binding to `_` prefixed variable with no side-effect"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for expression statements that can be reduced to a
Expand All @@ -44,6 +67,46 @@ declare_clippy_lint! {
"outer expressions with no effect"
}

declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION, NO_EFFECT_UNDERSCORE_BINDING]);

impl<'tcx> LateLintPass<'tcx> for NoEffect {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if check_no_effect(cx, stmt) {
return;
}
check_unnecessary_operation(cx, stmt);
}
}

fn check_no_effect(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> bool {
if let StmtKind::Semi(expr) = stmt.kind {
xFrednet marked this conversation as resolved.
Show resolved Hide resolved
if has_no_effect(cx, expr) {
span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
return true;
}
} else if let StmtKind::Local(local) = stmt.kind {
if_chain! {
if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id);
if let Some(init) = local.init;
if !local.pat.span.from_expansion();
if has_no_effect(cx, init);
if let PatKind::Binding(_, _, ident, _) = local.pat.kind;
if ident.name.to_ident_string().starts_with('_');
then {
span_lint_hir(
cx,
NO_EFFECT_UNDERSCORE_BINDING,
init.hir_id,
stmt.span,
"binding to `_` prefixed variable with no side-effect"
);
return true;
}
}
}
false
}

fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if expr.span.from_expansion() {
return false;
Expand Down Expand Up @@ -88,71 +151,59 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
}
}

declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION]);

impl<'tcx> LateLintPass<'tcx> for NoEffect {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if let StmtKind::Semi(expr) = stmt.kind {
if has_no_effect(cx, expr) {
span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
} else if let Some(reduced) = reduce_expression(cx, expr) {
for e in &reduced {
if e.span.from_expansion() {
return;
}
}
if let ExprKind::Index(..) = &expr.kind {
let snippet;
if_chain! {
if let Some(arr) = snippet_opt(cx, reduced[0].span);
if let Some(func) = snippet_opt(cx, reduced[1].span);
then {
snippet = format!("assert!({}.len() > {});", &arr, &func);
} else {
return;
}
}
span_lint_hir_and_then(
cx,
UNNECESSARY_OPERATION,
expr.hir_id,
stmt.span,
"unnecessary operation",
|diag| {
diag.span_suggestion(
stmt.span,
"statement can be written as",
snippet,
Applicability::MaybeIncorrect,
);
},
);
fn check_unnecessary_operation(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if_chain! {
if let StmtKind::Semi(expr) = stmt.kind;
if let Some(reduced) = reduce_expression(cx, expr);
if !&reduced.iter().any(|e| e.span.from_expansion());
then {
if let ExprKind::Index(..) = &expr.kind {
let snippet;
if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) {
snippet = format!("assert!({}.len() > {});", &arr, &func);
} else {
let mut snippet = String::new();
for e in reduced {
if let Some(snip) = snippet_opt(cx, e.span) {
snippet.push_str(&snip);
snippet.push(';');
} else {
return;
}
return;
}
span_lint_hir_and_then(
cx,
UNNECESSARY_OPERATION,
expr.hir_id,
stmt.span,
"unnecessary operation",
|diag| {
diag.span_suggestion(
stmt.span,
"statement can be written as",
snippet,
Applicability::MaybeIncorrect,
);
},
);
} else {
let mut snippet = String::new();
for e in reduced {
if let Some(snip) = snippet_opt(cx, e.span) {
snippet.push_str(&snip);
snippet.push(';');
} else {
return;
}
span_lint_hir_and_then(
cx,
UNNECESSARY_OPERATION,
expr.hir_id,
stmt.span,
"unnecessary operation",
|diag| {
diag.span_suggestion(
stmt.span,
"statement can be reduced to",
snippet,
Applicability::MachineApplicable,
);
},
);
}
span_lint_hir_and_then(
cx,
UNNECESSARY_OPERATION,
expr.hir_id,
stmt.span,
"unnecessary operation",
|diag| {
diag.span_suggestion(
stmt.span,
"statement can be reduced to",
snippet,
Applicability::MachineApplicable,
);
},
);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/cfg_attr_rustfmt.fixed
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// run-rustfix
#![feature(stmt_expr_attributes)]

#![allow(unused, clippy::no_effect)]
#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
#![warn(clippy::deprecated_cfg_attr)]

// This doesn't get linted, see known problems
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/cfg_attr_rustfmt.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// run-rustfix
#![feature(stmt_expr_attributes)]

#![allow(unused, clippy::no_effect)]
#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
#![warn(clippy::deprecated_cfg_attr)]

// This doesn't get linted, see known problems
Expand Down
8 changes: 7 additions & 1 deletion tests/ui/no_effect.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![feature(box_syntax)]
#![warn(clippy::no_effect)]
#![warn(clippy::no_effect_underscore_binding)]
#![allow(dead_code)]
#![allow(path_statements)]
#![allow(clippy::deref_addrof)]
Expand Down Expand Up @@ -90,13 +90,19 @@ fn main() {
|| x += 5;
let s: String = "foo".into();
FooString { s: s };
let _unused = 1;
F3real marked this conversation as resolved.
Show resolved Hide resolved
let _penguin = || println!("Some helpful closure");
let _duck = Struct { field: 0 };
let _cat = [2, 4, 6, 8][2];
xFrednet marked this conversation as resolved.
Show resolved Hide resolved

#[allow(clippy::no_effect)]
0;

// Do not warn
get_number();
unsafe { unsafe_fn() };
let _used = get_struct();
let _x = vec![1];
DropUnit;
DropStruct { field: 0 };
DropTuple(0);
Expand Down
28 changes: 27 additions & 1 deletion tests/ui/no_effect.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -156,5 +156,31 @@ error: statement with no effect
LL | FooString { s: s };
| ^^^^^^^^^^^^^^^^^^^

error: aborting due to 26 previous errors
error: binding to `_` prefixed variable with no side-effect
--> $DIR/no_effect.rs:93:5
|
LL | let _unused = 1;
| ^^^^^^^^^^^^^^^^
|
= note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings`

error: binding to `_` prefixed variable with no side-effect
--> $DIR/no_effect.rs:94:5
|
LL | let _penguin = || println!("Some helpful closure");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: binding to `_` prefixed variable with no side-effect
--> $DIR/no_effect.rs:95:5
|
LL | let _duck = Struct { field: 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: binding to `_` prefixed variable with no side-effect
--> $DIR/no_effect.rs:96:5
|
LL | let _cat = [2, 4, 6, 8][2];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 30 previous errors

3 changes: 1 addition & 2 deletions tests/ui/option_if_let_else.fixed
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// edition:2018
// run-rustfix
#![warn(clippy::option_if_let_else)]
#![allow(clippy::redundant_closure)]
#![allow(clippy::ref_option_ref, clippy::equatable_if_let)]
#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)]

fn bad1(string: Option<&str>) -> (bool, &str) {
string.map_or((false, "hello"), |x| (true, x))
Expand Down
3 changes: 1 addition & 2 deletions tests/ui/option_if_let_else.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// edition:2018
// run-rustfix
#![warn(clippy::option_if_let_else)]
#![allow(clippy::redundant_closure)]
#![allow(clippy::ref_option_ref, clippy::equatable_if_let)]
#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)]

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