Skip to content

Commit 21e6235

Browse files
committed
Auto merge of rust-lang#10921 - Centri3:needless_if, r=blyxyas,Manishearth
Add `needless_if` lint first off: Sorry about the large diff. Seems a ton of tests do this (understandably so). this is basically everything I wanted in rust-lang#10868, while it doesn't lint *all* unnecessary empty blocks, it lints needless if statements; which are basically the crux of the issue (for me) anyway. I've committed code that includes this far too many times 😅 hopefully clippy can help me out soon closes rust-lang#10868 changelog: New lint [`needless_if`]
2 parents edaf740 + 108c04a commit 21e6235

File tree

109 files changed

+839
-476
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+839
-476
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5008,6 +5008,7 @@ Released 2018-09-13
50085008
[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
50095009
[`needless_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_else
50105010
[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
5011+
[`needless_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_if
50115012
[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
50125013
[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
50135014
[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
459459
crate::needless_continue::NEEDLESS_CONTINUE_INFO,
460460
crate::needless_else::NEEDLESS_ELSE_INFO,
461461
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
462+
crate::needless_if::NEEDLESS_IF_INFO,
462463
crate::needless_late_init::NEEDLESS_LATE_INIT_INFO,
463464
crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO,
464465
crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ mod needless_borrowed_ref;
223223
mod needless_continue;
224224
mod needless_else;
225225
mod needless_for_each;
226+
mod needless_if;
226227
mod needless_late_init;
227228
mod needless_parens_on_range_literals;
228229
mod needless_pass_by_value;
@@ -1031,6 +1032,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10311032
store.register_late_pass(|_| Box::new(endian_bytes::EndianBytes));
10321033
store.register_late_pass(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations));
10331034
store.register_late_pass(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync));
1035+
store.register_late_pass(|_| Box::new(needless_if::NeedlessIf));
10341036
// add lints here, do not remove this comment, it's used in `new_lint`
10351037
}
10361038

clippy_lints/src/needless_if.rs

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro, source::snippet_with_applicability};
2+
use rustc_errors::Applicability;
3+
use rustc_hir::{
4+
intravisit::{walk_expr, Visitor},
5+
Expr, ExprKind, Node,
6+
};
7+
use rustc_lint::{LateContext, LateLintPass, LintContext};
8+
use rustc_middle::lint::in_external_macro;
9+
use rustc_session::{declare_lint_pass, declare_tool_lint};
10+
11+
declare_clippy_lint! {
12+
/// ### What it does
13+
/// Checks for empty `if` branches with no else branch.
14+
///
15+
/// ### Why is this bad?
16+
/// It can be entirely omitted, and often the condition too.
17+
///
18+
/// ### Known issues
19+
/// This will usually only suggest to remove the `if` statement, not the condition. Other lints
20+
/// such as `no_effect` will take care of removing the condition if it's unnecessary.
21+
///
22+
/// ### Example
23+
/// ```rust,ignore
24+
/// if really_expensive_condition(&i) {}
25+
/// if really_expensive_condition_with_side_effects(&mut i) {}
26+
/// ```
27+
/// Use instead:
28+
/// ```rust,ignore
29+
/// // <omitted>
30+
/// really_expensive_condition_with_side_effects(&mut i);
31+
/// ```
32+
#[clippy::version = "1.72.0"]
33+
pub NEEDLESS_IF,
34+
complexity,
35+
"checks for empty if branches"
36+
}
37+
declare_lint_pass!(NeedlessIf => [NEEDLESS_IF]);
38+
39+
impl LateLintPass<'_> for NeedlessIf {
40+
fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
41+
if let ExprKind::If(if_expr, block, else_expr) = &expr.kind
42+
&& let ExprKind::Block(block, ..) = block.kind
43+
&& block.stmts.is_empty()
44+
&& block.expr.is_none()
45+
&& else_expr.is_none()
46+
&& !in_external_macro(cx.sess(), expr.span)
47+
{
48+
// Ignore `else if`
49+
if let Some(parent_id) = cx.tcx.hir().opt_parent_id(expr.hir_id)
50+
&& let Some(Node::Expr(Expr {
51+
kind: ExprKind::If(_, _, Some(else_expr)),
52+
..
53+
})) = cx.tcx.hir().find(parent_id)
54+
&& else_expr.hir_id == expr.hir_id
55+
{
56+
return;
57+
}
58+
59+
if is_any_if_let(if_expr) || is_from_proc_macro(cx, expr) {
60+
return;
61+
}
62+
63+
let mut app = Applicability::MachineApplicable;
64+
let snippet = snippet_with_applicability(cx, if_expr.span, "{ ... }", &mut app);
65+
66+
span_lint_and_sugg(
67+
cx,
68+
NEEDLESS_IF,
69+
expr.span,
70+
"this `if` branch is empty",
71+
"you can remove it",
72+
if if_expr.can_have_side_effects() {
73+
format!("{snippet};")
74+
} else {
75+
String::new()
76+
},
77+
app,
78+
);
79+
}
80+
}
81+
}
82+
83+
/// Returns true if any `Expr` contained within this `Expr` is a `Let`, else false.
84+
///
85+
/// Really wish `Expr` had a `walk` method...
86+
fn is_any_if_let(expr: &Expr<'_>) -> bool {
87+
let mut v = IsAnyLetVisitor(false);
88+
89+
v.visit_expr(expr);
90+
v.0
91+
}
92+
93+
struct IsAnyLetVisitor(bool);
94+
95+
impl Visitor<'_> for IsAnyLetVisitor {
96+
fn visit_expr(&mut self, expr: &Expr<'_>) {
97+
if matches!(expr.kind, ExprKind::Let(..)) {
98+
self.0 = true;
99+
} else {
100+
walk_expr(self, expr);
101+
}
102+
}
103+
}

tests/ui-internal/if_chain_style.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
#![warn(clippy::if_chain_style)]
2-
#![allow(clippy::no_effect, clippy::nonminimal_bool, clippy::missing_clippy_version_attribute)]
2+
#![allow(
3+
clippy::needless_if,
4+
clippy::no_effect,
5+
clippy::nonminimal_bool,
6+
clippy::missing_clippy_version_attribute
7+
)]
38

49
extern crate if_chain;
510

tests/ui-internal/if_chain_style.stderr

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: this `if` can be part of the inner `if_chain!`
2-
--> $DIR/if_chain_style.rs:9:5
2+
--> $DIR/if_chain_style.rs:14:5
33
|
44
LL | / if true {
55
LL | | let x = "";
@@ -11,14 +11,14 @@ LL | | }
1111
| |_____^
1212
|
1313
help: this `let` statement can also be in the `if_chain!`
14-
--> $DIR/if_chain_style.rs:10:9
14+
--> $DIR/if_chain_style.rs:15:9
1515
|
1616
LL | let x = "";
1717
| ^^^^^^^^^^^
1818
= note: `-D clippy::if-chain-style` implied by `-D warnings`
1919

2020
error: `if a && b;` should be `if a; if b;`
21-
--> $DIR/if_chain_style.rs:19:12
21+
--> $DIR/if_chain_style.rs:24:12
2222
|
2323
LL | if true
2424
| ____________^
@@ -27,25 +27,25 @@ LL | | && false;
2727
| |____________________^
2828

2929
error: `let` expression should be inside `then { .. }`
30-
--> $DIR/if_chain_style.rs:24:9
30+
--> $DIR/if_chain_style.rs:29:9
3131
|
3232
LL | let x = "";
3333
| ^^^^^^^^^^^
3434

3535
error: this `if` can be part of the outer `if_chain!`
36-
--> $DIR/if_chain_style.rs:35:13
36+
--> $DIR/if_chain_style.rs:40:13
3737
|
3838
LL | if true {}
3939
| ^^^^^^^^^^
4040
|
4141
help: this `let` statement can also be in the `if_chain!`
42-
--> $DIR/if_chain_style.rs:33:13
42+
--> $DIR/if_chain_style.rs:38:13
4343
|
4444
LL | let x = "";
4545
| ^^^^^^^^^^^
4646

4747
error: `if_chain!` only has one `if`
48-
--> $DIR/if_chain_style.rs:29:5
48+
--> $DIR/if_chain_style.rs:34:5
4949
|
5050
LL | / if_chain! {
5151
LL | | // single `if` condition
@@ -59,13 +59,13 @@ LL | | }
5959
= note: this error originates in the macro `__if_chain` which comes from the expansion of the macro `if_chain` (in Nightly builds, run with -Z macro-backtrace for more info)
6060

6161
error: `let` expression should be above the `if_chain!`
62-
--> $DIR/if_chain_style.rs:40:9
62+
--> $DIR/if_chain_style.rs:45:9
6363
|
6464
LL | let x = "";
6565
| ^^^^^^^^^^^
6666

6767
error: this `if_chain!` can be merged with the outer `if_chain!`
68-
--> $DIR/if_chain_style.rs:46:13
68+
--> $DIR/if_chain_style.rs:51:13
6969
|
7070
LL | / if_chain! {
7171
LL | | if true;
@@ -75,7 +75,7 @@ LL | | }
7575
| |_____________^
7676
|
7777
help: these `let` statements can also be in the `if_chain!`
78-
--> $DIR/if_chain_style.rs:43:13
78+
--> $DIR/if_chain_style.rs:48:13
7979
|
8080
LL | / let x = "";
8181
LL | | let x = "";

tests/ui-toml/excessive_nesting/excessive_nesting.rs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#![allow(clippy::no_effect)]
88
#![allow(clippy::unnecessary_operation)]
99
#![allow(clippy::never_loop)]
10+
#![allow(clippy::needless_if)]
1011
#![warn(clippy::excessive_nesting)]
1112
#![allow(clippy::collapsible_if)]
1213

0 commit comments

Comments
 (0)