Skip to content

Commit d6e3fa8

Browse files
authored
Merge pull request #1032 from Manishearth/mut_mut
Improve `mut_mut` and `collapsible_if`
2 parents 5f6b982 + 9e76bce commit d6e3fa8

File tree

9 files changed

+251
-98
lines changed

9 files changed

+251
-98
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file.
55
* New lints: [`wrong_transmute`]
66
* For compatibility, `cargo clippy` does not defines the `clippy` feature
77
introduced in 0.0.76 anymore
8+
* [`collapsible_if`] now considers `if let`
89

910
## 0.0.77 — 2016-06-21
1011
* Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)*

clippy_lints/src/collapsible_if.rs

Lines changed: 60 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
//! This lint is **warn** by default
1414
1515
use rustc::lint::*;
16-
use rustc::hir::*;
1716
use std::borrow::Cow;
1817
use syntax::codemap::Spanned;
18+
use syntax::ast;
1919

2020
use utils::{in_macro, snippet, snippet_block, span_lint_and_then};
2121

@@ -45,66 +45,92 @@ impl LintPass for CollapsibleIf {
4545
}
4646
}
4747

48-
impl LateLintPass for CollapsibleIf {
49-
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
48+
impl EarlyLintPass for CollapsibleIf {
49+
fn check_expr(&mut self, cx: &EarlyContext, expr: &ast::Expr) {
5050
if !in_macro(cx, expr.span) {
5151
check_if(cx, expr)
5252
}
5353
}
5454
}
5555

56-
fn check_if(cx: &LateContext, e: &Expr) {
57-
if let ExprIf(ref check, ref then, ref else_) = e.node {
58-
if let Some(ref else_) = *else_ {
59-
if_let_chain! {[
60-
let ExprBlock(ref block) = else_.node,
61-
block.stmts.is_empty(),
62-
block.rules == BlockCheckMode::DefaultBlock,
63-
let Some(ref else_) = block.expr,
64-
let ExprIf(_, _, _) = else_.node
65-
], {
56+
fn check_if(cx: &EarlyContext, expr: &ast::Expr) {
57+
match expr.node {
58+
ast::ExprKind::If(ref check, ref then, ref else_) => {
59+
if let Some(ref else_) = *else_ {
60+
check_collapsible_maybe_if_let(cx, else_);
61+
} else {
62+
check_collapsible_no_if_let(cx, expr, check, then);
63+
}
64+
}
65+
ast::ExprKind::IfLet(_, _, _, Some(ref else_)) => {
66+
check_collapsible_maybe_if_let(cx, else_);
67+
}
68+
_ => (),
69+
}
70+
}
71+
72+
fn check_collapsible_maybe_if_let(cx: &EarlyContext, else_: &ast::Expr) {
73+
if_let_chain! {[
74+
let ast::ExprKind::Block(ref block) = else_.node,
75+
block.stmts.is_empty(),
76+
let Some(ref else_) = block.expr,
77+
], {
78+
match else_.node {
79+
ast::ExprKind::If(..) | ast::ExprKind::IfLet(..) => {
6680
span_lint_and_then(cx,
6781
COLLAPSIBLE_IF,
6882
block.span,
6983
"this `else { if .. }` block can be collapsed", |db| {
7084
db.span_suggestion(block.span, "try", snippet_block(cx, else_.span, "..").into_owned());
7185
});
72-
}}
73-
} else if let Some(&Expr { node: ExprIf(ref check_inner, ref content, None), span: sp, .. }) =
74-
single_stmt_of_block(then) {
75-
if e.span.expn_id != sp.expn_id {
76-
return;
7786
}
78-
span_lint_and_then(cx, COLLAPSIBLE_IF, e.span, "this if statement can be collapsed", |db| {
79-
db.span_suggestion(e.span,
80-
"try",
81-
format!("if {} && {} {}",
82-
check_to_string(cx, check),
83-
check_to_string(cx, check_inner),
84-
snippet_block(cx, content.span, "..")));
85-
});
87+
_ => (),
8688
}
87-
}
89+
}}
90+
}
91+
92+
fn check_collapsible_no_if_let(
93+
cx: &EarlyContext,
94+
expr: &ast::Expr,
95+
check: &ast::Expr,
96+
then: &ast::Block,
97+
) {
98+
if_let_chain! {[
99+
let Some(inner) = single_stmt_of_block(then),
100+
let ast::ExprKind::If(ref check_inner, ref content, None) = inner.node,
101+
], {
102+
if expr.span.expn_id != inner.span.expn_id {
103+
return;
104+
}
105+
span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this if statement can be collapsed", |db| {
106+
db.span_suggestion(expr.span,
107+
"try",
108+
format!("if {} && {} {}",
109+
check_to_string(cx, check),
110+
check_to_string(cx, check_inner),
111+
snippet_block(cx, content.span, "..")));
112+
});
113+
}}
88114
}
89115

90-
fn requires_brackets(e: &Expr) -> bool {
116+
fn requires_brackets(e: &ast::Expr) -> bool {
91117
match e.node {
92-
ExprBinary(Spanned { node: n, .. }, _, _) if n == BiEq => false,
118+
ast::ExprKind::Binary(Spanned { node: n, .. }, _, _) if n == ast::BinOpKind::Eq => false,
93119
_ => true,
94120
}
95121
}
96122

97-
fn check_to_string(cx: &LateContext, e: &Expr) -> Cow<'static, str> {
123+
fn check_to_string(cx: &EarlyContext, e: &ast::Expr) -> Cow<'static, str> {
98124
if requires_brackets(e) {
99125
format!("({})", snippet(cx, e.span, "..")).into()
100126
} else {
101127
snippet(cx, e.span, "..")
102128
}
103129
}
104130

105-
fn single_stmt_of_block(block: &Block) -> Option<&Expr> {
131+
fn single_stmt_of_block(block: &ast::Block) -> Option<&ast::Expr> {
106132
if block.stmts.len() == 1 && block.expr.is_none() {
107-
if let StmtExpr(ref expr, _) = block.stmts[0].node {
133+
if let ast::StmtKind::Expr(ref expr, _) = block.stmts[0].node {
108134
single_stmt_of_expr(expr)
109135
} else {
110136
None
@@ -120,8 +146,8 @@ fn single_stmt_of_block(block: &Block) -> Option<&Expr> {
120146
}
121147
}
122148

123-
fn single_stmt_of_expr(expr: &Expr) -> Option<&Expr> {
124-
if let ExprBlock(ref block) = expr.node {
149+
fn single_stmt_of_expr(expr: &ast::Expr) -> Option<&ast::Expr> {
150+
if let ast::ExprKind::Block(ref block) = expr.node {
125151
single_stmt_of_block(block)
126152
} else {
127153
Some(expr)

clippy_lints/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
189189
reg.register_late_lint_pass(box len_zero::LenZero);
190190
reg.register_late_lint_pass(box misc::CmpOwned);
191191
reg.register_late_lint_pass(box attrs::AttrPass);
192-
reg.register_late_lint_pass(box collapsible_if::CollapsibleIf);
192+
reg.register_early_lint_pass(box collapsible_if::CollapsibleIf);
193193
reg.register_late_lint_pass(box block_in_if_condition::BlockInIfCondition);
194194
reg.register_late_lint_pass(box misc::ModuloOne);
195195
reg.register_late_lint_pass(box unicode::Unicode);

clippy_lints/src/minmax.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,9 @@ fn fetch_const(args: &[P<Expr>], m: MinMax) -> Option<(MinMax, Constant, &Expr)>
8383
} else {
8484
None
8585
}
86+
} else if let Some(c) = constant_simple(&args[1]) {
87+
Some((m, c, &args[0]))
8688
} else {
87-
if let Some(c) = constant_simple(&args[1]) {
88-
Some((m, c, &args[0]))
89-
} else {
90-
None
91-
}
89+
None
9290
}
9391
}

clippy_lints/src/mut_mut.rs

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
use rustc::hir;
2+
use rustc::hir::intravisit;
13
use rustc::lint::*;
24
use rustc::ty::{TypeAndMut, TyRef};
3-
use rustc::hir::*;
4-
use utils::{in_external_macro, span_lint};
5+
use utils::{in_external_macro, recover_for_loop, span_lint};
56

67
/// **What it does:** This lint checks for instances of `mut mut` references.
78
///
@@ -27,30 +28,56 @@ impl LintPass for MutMut {
2728
}
2829

2930
impl LateLintPass for MutMut {
30-
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
31-
if in_external_macro(cx, expr.span) {
31+
fn check_block(&mut self, cx: &LateContext, block: &hir::Block) {
32+
intravisit::walk_block(&mut MutVisitor { cx: cx }, block);
33+
}
34+
35+
fn check_ty(&mut self, cx: &LateContext, ty: &hir::Ty) {
36+
use rustc::hir::intravisit::Visitor;
37+
38+
MutVisitor { cx: cx }.visit_ty(ty);
39+
}
40+
}
41+
42+
pub struct MutVisitor<'a, 'tcx: 'a> {
43+
cx: &'a LateContext<'a, 'tcx>,
44+
}
45+
46+
impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for MutVisitor<'a, 'tcx> {
47+
fn visit_expr(&mut self, expr: &'v hir::Expr) {
48+
if in_external_macro(self.cx, expr.span) {
3249
return;
3350
}
3451

35-
if let ExprAddrOf(MutMutable, ref e) = expr.node {
36-
if let ExprAddrOf(MutMutable, _) = e.node {
37-
span_lint(cx, MUT_MUT, expr.span, "generally you want to avoid `&mut &mut _` if possible");
38-
} else {
39-
if let TyRef(_, TypeAndMut { mutbl: MutMutable, .. }) = cx.tcx.expr_ty(e).sty {
40-
span_lint(cx,
41-
MUT_MUT,
42-
expr.span,
43-
"this expression mutably borrows a mutable reference. Consider reborrowing");
44-
}
52+
if let Some((_, arg, body)) = recover_for_loop(expr) {
53+
// A `for` loop lowers to:
54+
// ```rust
55+
// match ::std::iter::Iterator::next(&mut iter) {
56+
// // ^^^^
57+
// ```
58+
// Let's ignore the generated code.
59+
intravisit::walk_expr(self, arg);
60+
intravisit::walk_expr(self, body);
61+
} else if let hir::ExprAddrOf(hir::MutMutable, ref e) = expr.node {
62+
if let hir::ExprAddrOf(hir::MutMutable, _) = e.node {
63+
span_lint(self.cx, MUT_MUT, expr.span, "generally you want to avoid `&mut &mut _` if possible");
64+
} else if let TyRef(_, TypeAndMut { mutbl: hir::MutMutable, .. }) = self.cx.tcx.expr_ty(e).sty {
65+
span_lint(self.cx,
66+
MUT_MUT,
67+
expr.span,
68+
"this expression mutably borrows a mutable reference. Consider reborrowing");
4569
}
4670
}
4771
}
4872

49-
fn check_ty(&mut self, cx: &LateContext, ty: &Ty) {
50-
if let TyRptr(_, MutTy { ty: ref pty, mutbl: MutMutable }) = ty.node {
51-
if let TyRptr(_, MutTy { mutbl: MutMutable, .. }) = pty.node {
52-
span_lint(cx, MUT_MUT, ty.span, "generally you want to avoid `&mut &mut _` if possible");
73+
fn visit_ty(&mut self, ty: &hir::Ty) {
74+
if let hir::TyRptr(_, hir::MutTy { ty: ref pty, mutbl: hir::MutMutable }) = ty.node {
75+
if let hir::TyRptr(_, hir::MutTy { mutbl: hir::MutMutable, .. }) = pty.node {
76+
span_lint(self.cx, MUT_MUT, ty.span, "generally you want to avoid `&mut &mut _` if possible");
5377
}
78+
5479
}
80+
81+
intravisit::walk_ty(self, ty);
5582
}
5683
}

tests/compile-fail/collapsible_if.rs

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,32 @@ fn main() {
2626
// Collaspe `else { if .. }` to `else if ..`
2727
if x == "hello" {
2828
print!("Hello ");
29-
} else { //~ERROR: this `else { if .. }`
30-
//~| HELP try
31-
//~| SUGGESTION } else if y == "world"
29+
} else {
30+
//~^ ERROR: this `else { if .. }`
31+
//~| HELP try
32+
//~| SUGGESTION } else if y == "world"
3233
if y == "world" {
3334
println!("world!")
3435
}
3536
}
3637

3738
if x == "hello" {
3839
print!("Hello ");
39-
} else { //~ERROR this `else { if .. }`
40-
//~| HELP try
41-
//~| SUGGESTION } else if y == "world"
40+
} else {
41+
//~^ ERROR: this `else { if .. }`
42+
//~| HELP try
43+
//~| SUGGESTION } else if let Some(42)
44+
if let Some(42) = Some(42) {
45+
println!("world!")
46+
}
47+
}
48+
49+
if x == "hello" {
50+
print!("Hello ");
51+
} else {
52+
//~^ ERROR this `else { if .. }`
53+
//~| HELP try
54+
//~| SUGGESTION } else if y == "world"
4255
if y == "world" {
4356
println!("world")
4457
}
@@ -47,6 +60,62 @@ fn main() {
4760
}
4861
}
4962

63+
if x == "hello" {
64+
print!("Hello ");
65+
} else {
66+
//~^ ERROR this `else { if .. }`
67+
//~| HELP try
68+
//~| SUGGESTION } else if let Some(42)
69+
if let Some(42) = Some(42) {
70+
println!("world")
71+
}
72+
else {
73+
println!("!")
74+
}
75+
}
76+
77+
if let Some(42) = Some(42) {
78+
print!("Hello ");
79+
} else {
80+
//~^ ERROR this `else { if .. }`
81+
//~| HELP try
82+
//~| SUGGESTION } else if let Some(42)
83+
if let Some(42) = Some(42) {
84+
println!("world")
85+
}
86+
else {
87+
println!("!")
88+
}
89+
}
90+
91+
if let Some(42) = Some(42) {
92+
print!("Hello ");
93+
} else {
94+
//~^ ERROR this `else { if .. }`
95+
//~| HELP try
96+
//~| SUGGESTION } else if x == "hello"
97+
if x == "hello" {
98+
println!("world")
99+
}
100+
else {
101+
println!("!")
102+
}
103+
}
104+
105+
if let Some(42) = Some(42) {
106+
print!("Hello ");
107+
} else {
108+
//~^ ERROR this `else { if .. }`
109+
//~| HELP try
110+
//~| SUGGESTION } else if let Some(42)
111+
if let Some(42) = Some(42) {
112+
println!("world")
113+
}
114+
else {
115+
println!("!")
116+
}
117+
}
118+
50119
// Works because any if with an else statement cannot be collapsed.
51120
if x == "hello" {
52121
if y == "world" {

tests/compile-fail/copies.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#![allow(unused_variables)]
99
#![allow(cyclomatic_complexity)]
1010
#![allow(blacklisted_name)]
11+
#![allow(collapsible_if)]
1112

1213
fn bar<T>(_: T) {}
1314
fn foo() -> bool { unimplemented!() }

0 commit comments

Comments
 (0)