Skip to content
Open
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
61 changes: 37 additions & 24 deletions clippy_lints/src/matches/match_single_binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_path, walk_stmt};
use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, Node, PatKind, Path, Stmt, StmtKind};
use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, PatKind, Path, Stmt, StmtKind};
use rustc_lint::LateContext;
use rustc_span::{Span, Symbol};

Expand Down Expand Up @@ -307,26 +307,6 @@ fn expr_in_nested_block(cx: &LateContext<'_>, match_expr: &Expr<'_>) -> bool {
false
}

fn expr_must_have_curlies(cx: &LateContext<'_>, match_expr: &Expr<'_>) -> bool {
let parent = cx.tcx.parent_hir_node(match_expr.hir_id);
if let Node::Expr(Expr {
kind: ExprKind::Closure(..) | ExprKind::Binary(..),
..
})
| Node::AnonConst(..) = parent
{
return true;
}

if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id)
&& let ExprKind::Match(..) = arm.body.kind
{
return true;
}

false
}

fn indent_of_nth_line(snippet: &str, nth: usize) -> Option<usize> {
snippet
.lines()
Expand Down Expand Up @@ -379,14 +359,47 @@ fn sugg_with_curlies<'a>(

let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new());
if !expr_in_nested_block(cx, match_expr)
&& ((needs_var_binding && is_var_binding_used_later) || expr_must_have_curlies(cx, match_expr))
{
let mut add_curlies = || {
cbrace_end = format!("\n{indent}}}");
// Fix body indent due to the closure
indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
cbrace_start = format!("{{\n{indent}");
snippet_body = reindent_snippet_if_in_block(&snippet_body, !assignment_str.is_empty());
};

if !expr_in_nested_block(cx, match_expr) {
let mut parent = cx.tcx.parent_hir_node(match_expr.hir_id);
if let Node::Expr(Expr {
kind: ExprKind::Assign(..),
hir_id,
..
}) = parent
{
parent = cx.tcx.parent_hir_node(*hir_id);
}
if let Node::Stmt(stmt) = parent {
parent = cx.tcx.parent_hir_node(stmt.hir_id);
}

match parent {
Node::Block(..)
| Node::Expr(Expr {
kind: ExprKind::Block(..) | ExprKind::ConstBlock(..),
..
}) => {
if needs_var_binding && is_var_binding_used_later {
add_curlies();
}
},
Node::Expr(..)
| Node::AnonConst(..)
| Node::Item(Item {
kind: ItemKind::Const(..),
..
}) => add_curlies(),
Node::Arm(arm) if let ExprKind::Match(..) = arm.body.kind => add_curlies(),
_ => {},
}
}

format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}")
Expand Down
79 changes: 79 additions & 0 deletions tests/ui/match_single_binding.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,82 @@ fn issue15269(a: usize, b: usize, c: usize) -> bool {
a < b
&& b < c
}

#[allow(
irrefutable_let_patterns,
clippy::blocks_in_conditions,
clippy::unused_unit,
clippy::let_unit_value,
clippy::unit_arg,
clippy::unnecessary_operation
)]
fn issue15537(a: i32) -> ((), (), ()) {
let y = (
{ todo!() },
{
{ a };
()
},
(),
);

let y = [
{ todo!() },
{
{ a };
()
},
(),
];

fn call(x: (), y: (), z: ()) {}
let y = call(
{ todo!() },
{
{ a };
()
},
(),
);

struct Foo;
impl Foo {
fn method(&self, x: (), y: (), z: ()) {}
}
let x = Foo;
x.method(
{ todo!() },
{
{ a };
()
},
(),
);

-{
{ a };
1
};

_ = { a };
1;

if let x = {
{ a };
1
} {}

if {
{ a };
true
} {
todo!()
}

[1, 2, 3][{
{ a };
1usize
}];

todo!()
}
81 changes: 81 additions & 0 deletions tests/ui/match_single_binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,84 @@ fn issue15269(a: usize, b: usize, c: usize) -> bool {
(a, b) => b < c,
}
}

#[allow(
irrefutable_let_patterns,
clippy::blocks_in_conditions,
clippy::unused_unit,
clippy::let_unit_value,
clippy::unit_arg,
clippy::unnecessary_operation
)]
fn issue15537(a: i32) -> ((), (), ()) {
let y = (
{ todo!() },
match { a } {
//~^ match_single_binding
_ => (),
},
(),
);

let y = [
{ todo!() },
match { a } {
//~^ match_single_binding
_ => (),
},
(),
];

fn call(x: (), y: (), z: ()) {}
let y = call(
{ todo!() },
match { a } {
//~^ match_single_binding
_ => (),
},
(),
);

struct Foo;
impl Foo {
fn method(&self, x: (), y: (), z: ()) {}
}
let x = Foo;
x.method(
{ todo!() },
match { a } {
//~^ match_single_binding
_ => (),
},
(),
);

-match { a } {
//~^ match_single_binding
_ => 1,
};

_ = match { a } {
//~^ match_single_binding
_ => 1,
};

if let x = match { a } {
//~^ match_single_binding
_ => 1,
} {}

if match { a } {
//~^ match_single_binding
_ => true,
} {
todo!()
}

[1, 2, 3][match { a } {
//~^ match_single_binding
_ => 1usize,
}];

todo!()
}
Loading