Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 118594f

Browse files
committed
Auto merge of rust-lang#5291 - ThibsG:FixSingleBinding, r=flip1995
Fix match single binding when in a let stmt Fix bad suggestion when `match_single_binding` lints when inside a local (let) statement. Fixes rust-lang#5267 changelog: none
2 parents a5477c5 + 40a04f2 commit 118594f

File tree

4 files changed

+84
-23
lines changed

4 files changed

+84
-23
lines changed

clippy_lints/src/matches.rs

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ use rustc_ast::ast::LitKind;
1414
use rustc_errors::Applicability;
1515
use rustc_hir::def::CtorKind;
1616
use rustc_hir::{
17-
print, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Pat, PatKind,
18-
QPath, RangeEnd,
17+
print, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Node, Pat,
18+
PatKind, QPath, RangeEnd,
1919
};
2020
use rustc_lint::{LateContext, LateLintPass, LintContext};
2121
use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -882,7 +882,7 @@ fn check_wild_in_or_pats(cx: &LateContext<'_, '_>, arms: &[Arm<'_>]) {
882882
}
883883
}
884884

885-
fn check_match_single_binding(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
885+
fn check_match_single_binding<'a>(cx: &LateContext<'_, 'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
886886
if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
887887
return;
888888
}
@@ -914,19 +914,38 @@ fn check_match_single_binding(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[A
914914
let mut applicability = Applicability::MaybeIncorrect;
915915
match arms[0].pat.kind {
916916
PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
917+
// If this match is in a local (`let`) stmt
918+
let (target_span, sugg) = if let Some(parent_let_node) = opt_parent_let(cx, ex) {
919+
(
920+
parent_let_node.span,
921+
format!(
922+
"let {} = {};\n{}let {} = {};",
923+
snippet_with_applicability(cx, bind_names, "..", &mut applicability),
924+
snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
925+
" ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
926+
snippet_with_applicability(cx, parent_let_node.pat.span, "..", &mut applicability),
927+
snippet_body
928+
),
929+
)
930+
} else {
931+
(
932+
expr.span,
933+
format!(
934+
"let {} = {};\n{}{}",
935+
snippet_with_applicability(cx, bind_names, "..", &mut applicability),
936+
snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
937+
" ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
938+
snippet_body
939+
),
940+
)
941+
};
917942
span_lint_and_sugg(
918943
cx,
919944
MATCH_SINGLE_BINDING,
920-
expr.span,
945+
target_span,
921946
"this match could be written as a `let` statement",
922947
"consider using `let` statement",
923-
format!(
924-
"let {} = {};\n{}{}",
925-
snippet_with_applicability(cx, bind_names, "..", &mut applicability),
926-
snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
927-
" ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
928-
snippet_body,
929-
),
948+
sugg,
930949
applicability,
931950
);
932951
},
@@ -945,6 +964,19 @@ fn check_match_single_binding(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[A
945964
}
946965
}
947966

967+
/// Returns true if the `ex` match expression is in a local (`let`) statement
968+
fn opt_parent_let<'a>(cx: &LateContext<'_, 'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
969+
if_chain! {
970+
let map = &cx.tcx.hir();
971+
if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id));
972+
if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id));
973+
then {
974+
return Some(parent_let_expr);
975+
}
976+
}
977+
None
978+
}
979+
948980
/// Gets all arms that are unbounded `PatRange`s.
949981
fn all_ranges<'a, 'tcx>(
950982
cx: &LateContext<'a, 'tcx>,

tests/ui/match_single_binding.fixed

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
// run-rustfix
22

33
#![warn(clippy::match_single_binding)]
4-
#![allow(clippy::many_single_char_names, clippy::toplevel_ref_arg)]
4+
#![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)]
55

66
struct Point {
77
x: i32,
88
y: i32,
99
}
1010

11+
fn coords() -> Point {
12+
Point { x: 1, y: 2 }
13+
}
14+
1115
fn main() {
1216
let a = 1;
1317
let b = 2;
@@ -60,4 +64,7 @@ fn main() {
6064
let mut x = 5;
6165
let ref mut mr = x;
6266
println!("Got a mutable reference to {}", mr);
67+
// Lint
68+
let Point { x, y } = coords();
69+
let product = x * y;
6370
}

tests/ui/match_single_binding.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
// run-rustfix
22

33
#![warn(clippy::match_single_binding)]
4-
#![allow(clippy::many_single_char_names, clippy::toplevel_ref_arg)]
4+
#![allow(unused_variables, clippy::many_single_char_names, clippy::toplevel_ref_arg)]
55

66
struct Point {
77
x: i32,
88
y: i32,
99
}
1010

11+
fn coords() -> Point {
12+
Point { x: 1, y: 2 }
13+
}
14+
1115
fn main() {
1216
let a = 1;
1317
let b = 2;
@@ -72,4 +76,8 @@ fn main() {
7276
match x {
7377
ref mut mr => println!("Got a mutable reference to {}", mr),
7478
}
79+
// Lint
80+
let product = match coords() {
81+
Point { x, y } => x * y,
82+
};
7583
}

tests/ui/match_single_binding.stderr

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: this match could be written as a `let` statement
2-
--> $DIR/match_single_binding.rs:16:5
2+
--> $DIR/match_single_binding.rs:20:5
33
|
44
LL | / match (a, b, c) {
55
LL | | (x, y, z) => {
@@ -18,7 +18,7 @@ LL | }
1818
|
1919

2020
error: this match could be written as a `let` statement
21-
--> $DIR/match_single_binding.rs:22:5
21+
--> $DIR/match_single_binding.rs:26:5
2222
|
2323
LL | / match (a, b, c) {
2424
LL | | (x, y, z) => println!("{} {} {}", x, y, z),
@@ -32,15 +32,15 @@ LL | println!("{} {} {}", x, y, z);
3232
|
3333

3434
error: this match could be replaced by its body itself
35-
--> $DIR/match_single_binding.rs:37:5
35+
--> $DIR/match_single_binding.rs:41:5
3636
|
3737
LL | / match a {
3838
LL | | _ => println!("whatever"),
3939
LL | | }
4040
| |_____^ help: consider using the match body instead: `println!("whatever");`
4141

4242
error: this match could be replaced by its body itself
43-
--> $DIR/match_single_binding.rs:41:5
43+
--> $DIR/match_single_binding.rs:45:5
4444
|
4545
LL | / match a {
4646
LL | | _ => {
@@ -59,7 +59,7 @@ LL | }
5959
|
6060

6161
error: this match could be replaced by its body itself
62-
--> $DIR/match_single_binding.rs:48:5
62+
--> $DIR/match_single_binding.rs:52:5
6363
|
6464
LL | / match a {
6565
LL | | _ => {
@@ -81,7 +81,7 @@ LL | }
8181
|
8282

8383
error: this match could be written as a `let` statement
84-
--> $DIR/match_single_binding.rs:58:5
84+
--> $DIR/match_single_binding.rs:62:5
8585
|
8686
LL | / match p {
8787
LL | | Point { x, y } => println!("Coords: ({}, {})", x, y),
@@ -95,7 +95,7 @@ LL | println!("Coords: ({}, {})", x, y);
9595
|
9696

9797
error: this match could be written as a `let` statement
98-
--> $DIR/match_single_binding.rs:62:5
98+
--> $DIR/match_single_binding.rs:66:5
9999
|
100100
LL | / match p {
101101
LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1),
@@ -109,7 +109,7 @@ LL | println!("Coords: ({}, {})", x1, y1);
109109
|
110110

111111
error: this match could be written as a `let` statement
112-
--> $DIR/match_single_binding.rs:67:5
112+
--> $DIR/match_single_binding.rs:71:5
113113
|
114114
LL | / match x {
115115
LL | | ref r => println!("Got a reference to {}", r),
@@ -123,7 +123,7 @@ LL | println!("Got a reference to {}", r);
123123
|
124124

125125
error: this match could be written as a `let` statement
126-
--> $DIR/match_single_binding.rs:72:5
126+
--> $DIR/match_single_binding.rs:76:5
127127
|
128128
LL | / match x {
129129
LL | | ref mut mr => println!("Got a mutable reference to {}", mr),
@@ -136,5 +136,19 @@ LL | let ref mut mr = x;
136136
LL | println!("Got a mutable reference to {}", mr);
137137
|
138138

139-
error: aborting due to 9 previous errors
139+
error: this match could be written as a `let` statement
140+
--> $DIR/match_single_binding.rs:80:5
141+
|
142+
LL | / let product = match coords() {
143+
LL | | Point { x, y } => x * y,
144+
LL | | };
145+
| |______^
146+
|
147+
help: consider using `let` statement
148+
|
149+
LL | let Point { x, y } = coords();
150+
LL | let product = x * y;
151+
|
152+
153+
error: aborting due to 10 previous errors
140154

0 commit comments

Comments
 (0)