Skip to content

Commit 6889d09

Browse files
committed
Auto merge of rust-lang#8726 - Serial-ATA:issue-8723, r=dswij,xFrednet
Fix `match_single_binding` suggestion for assign expressions changelog: Fix suggestion for assign expressions in [`match_single_binding`] closes: rust-lang#8723
2 parents d422baa + 554dc41 commit 6889d09

File tree

5 files changed

+178
-64
lines changed

5 files changed

+178
-64
lines changed
+120-53
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
2-
use clippy_utils::source::{indent_of, snippet_block, snippet_with_applicability};
2+
use clippy_utils::macros::HirNode;
3+
use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_with_applicability};
34
use clippy_utils::sugg::Sugg;
45
use clippy_utils::{get_parent_expr, is_refutable, peel_blocks};
56
use rustc_errors::Applicability;
6-
use rustc_hir::{Arm, Expr, ExprKind, Local, Node, PatKind};
7+
use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind};
78
use rustc_lint::LateContext;
9+
use rustc_span::Span;
810

911
use super::MATCH_SINGLE_BINDING;
1012

13+
enum AssignmentExpr {
14+
Assign { span: Span, match_span: Span },
15+
Local { span: Span, pat_span: Span },
16+
}
17+
1118
#[expect(clippy::too_many_lines)]
12-
pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
19+
pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'a>) {
1320
if expr.span.from_expansion() || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
1421
return;
1522
}
@@ -42,61 +49,59 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
4249
let mut applicability = Applicability::MaybeIncorrect;
4350
match arms[0].pat.kind {
4451
PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
45-
// If this match is in a local (`let`) stmt
46-
let (target_span, sugg) = if let Some(parent_let_node) = opt_parent_let(cx, ex) {
47-
(
48-
parent_let_node.span,
52+
let (target_span, sugg) = match opt_parent_assign_span(cx, ex) {
53+
Some(AssignmentExpr::Assign { span, match_span }) => {
54+
let sugg = sugg_with_curlies(
55+
cx,
56+
(ex, expr),
57+
(bind_names, matched_vars),
58+
&*snippet_body,
59+
&mut applicability,
60+
Some(span),
61+
);
62+
63+
span_lint_and_sugg(
64+
cx,
65+
MATCH_SINGLE_BINDING,
66+
span.to(match_span),
67+
"this assignment could be simplified",
68+
"consider removing the `match` expression",
69+
sugg,
70+
applicability,
71+
);
72+
73+
return;
74+
},
75+
Some(AssignmentExpr::Local { span, pat_span }) => (
76+
span,
4977
format!(
5078
"let {} = {};\n{}let {} = {};",
5179
snippet_with_applicability(cx, bind_names, "..", &mut applicability),
5280
snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
5381
" ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
54-
snippet_with_applicability(cx, parent_let_node.pat.span, "..", &mut applicability),
82+
snippet_with_applicability(cx, pat_span, "..", &mut applicability),
5583
snippet_body
5684
),
57-
)
58-
} else {
59-
// If we are in closure, we need curly braces around suggestion
60-
let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
61-
let (mut cbrace_start, mut cbrace_end) = ("".to_string(), "".to_string());
62-
if let Some(parent_expr) = get_parent_expr(cx, expr) {
63-
if let ExprKind::Closure(..) = parent_expr.kind {
64-
cbrace_end = format!("\n{}}}", indent);
65-
// Fix body indent due to the closure
66-
indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
67-
cbrace_start = format!("{{\n{}", indent);
68-
}
69-
}
70-
// If the parent is already an arm, and the body is another match statement,
71-
// we need curly braces around suggestion
72-
let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id);
73-
if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) {
74-
if let ExprKind::Match(..) = arm.body.kind {
75-
cbrace_end = format!("\n{}}}", indent);
76-
// Fix body indent due to the match
77-
indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
78-
cbrace_start = format!("{{\n{}", indent);
79-
}
80-
}
81-
(
82-
expr.span,
83-
format!(
84-
"{}let {} = {};\n{}{}{}",
85-
cbrace_start,
86-
snippet_with_applicability(cx, bind_names, "..", &mut applicability),
87-
snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
88-
indent,
89-
snippet_body,
90-
cbrace_end
91-
),
92-
)
85+
),
86+
None => {
87+
let sugg = sugg_with_curlies(
88+
cx,
89+
(ex, expr),
90+
(bind_names, matched_vars),
91+
&*snippet_body,
92+
&mut applicability,
93+
None,
94+
);
95+
(expr.span, sugg)
96+
},
9397
};
98+
9499
span_lint_and_sugg(
95100
cx,
96101
MATCH_SINGLE_BINDING,
97102
target_span,
98103
"this match could be written as a `let` statement",
99-
"consider using `let` statement",
104+
"consider using a `let` statement",
100105
sugg,
101106
applicability,
102107
);
@@ -110,6 +115,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
110115
indent,
111116
snippet_body
112117
);
118+
113119
span_lint_and_sugg(
114120
cx,
115121
MATCH_SINGLE_BINDING,
@@ -135,15 +141,76 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
135141
}
136142
}
137143

138-
/// Returns true if the `ex` match expression is in a local (`let`) statement
139-
fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
144+
/// Returns true if the `ex` match expression is in a local (`let`) or assign expression
145+
fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<AssignmentExpr> {
140146
let map = &cx.tcx.hir();
141-
if_chain! {
142-
if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id));
143-
if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id));
144-
then {
145-
return Some(parent_let_expr);
146-
}
147+
148+
if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id)) {
149+
return match map.find(map.get_parent_node(parent_arm_expr.hir_id)) {
150+
Some(Node::Local(parent_let_expr)) => Some(AssignmentExpr::Local {
151+
span: parent_let_expr.span,
152+
pat_span: parent_let_expr.pat.span(),
153+
}),
154+
Some(Node::Expr(Expr {
155+
kind: ExprKind::Assign(parent_assign_expr, match_expr, _),
156+
..
157+
})) => Some(AssignmentExpr::Assign {
158+
span: parent_assign_expr.span,
159+
match_span: match_expr.span,
160+
}),
161+
_ => None,
162+
};
147163
}
164+
148165
None
149166
}
167+
168+
fn sugg_with_curlies<'a>(
169+
cx: &LateContext<'a>,
170+
(ex, match_expr): (&Expr<'a>, &Expr<'a>),
171+
(bind_names, matched_vars): (Span, Span),
172+
snippet_body: &str,
173+
applicability: &mut Applicability,
174+
assignment: Option<Span>,
175+
) -> String {
176+
let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
177+
178+
let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new());
179+
if let Some(parent_expr) = get_parent_expr(cx, match_expr) {
180+
if let ExprKind::Closure(..) = parent_expr.kind {
181+
cbrace_end = format!("\n{}}}", indent);
182+
// Fix body indent due to the closure
183+
indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
184+
cbrace_start = format!("{{\n{}", indent);
185+
}
186+
}
187+
188+
// If the parent is already an arm, and the body is another match statement,
189+
// we need curly braces around suggestion
190+
let parent_node_id = cx.tcx.hir().get_parent_node(match_expr.hir_id);
191+
if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) {
192+
if let ExprKind::Match(..) = arm.body.kind {
193+
cbrace_end = format!("\n{}}}", indent);
194+
// Fix body indent due to the match
195+
indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
196+
cbrace_start = format!("{{\n{}", indent);
197+
}
198+
}
199+
200+
let assignment_str = assignment.map_or_else(String::new, |span| {
201+
let mut s = snippet(cx, span, "..").to_string();
202+
s.push_str(" = ");
203+
s
204+
});
205+
206+
format!(
207+
"{}let {} = {};\n{}{}{}{}",
208+
cbrace_start,
209+
snippet_with_applicability(cx, bind_names, "..", applicability),
210+
snippet_with_applicability(cx, matched_vars, "..", applicability),
211+
indent,
212+
assignment_str,
213+
snippet_body,
214+
cbrace_end
215+
)
216+
}

tests/ui/match_single_binding.fixed

+13
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,16 @@ fn main() {
111111
let x = 1;
112112
println!("Not an array index start");
113113
}
114+
115+
#[allow(dead_code)]
116+
fn issue_8723() {
117+
let (mut val, idx) = ("a b", 1);
118+
119+
let (pre, suf) = val.split_at(idx);
120+
val = {
121+
println!("{}", pre);
122+
suf
123+
};
124+
125+
let _ = val;
126+
}

tests/ui/match_single_binding.rs

+14
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,17 @@ fn main() {
126126
_ => println!("Not an array index start"),
127127
}
128128
}
129+
130+
#[allow(dead_code)]
131+
fn issue_8723() {
132+
let (mut val, idx) = ("a b", 1);
133+
134+
val = match val.split_at(idx) {
135+
(pre, suf) => {
136+
println!("{}", pre);
137+
suf
138+
},
139+
};
140+
141+
let _ = val;
142+
}

tests/ui/match_single_binding.stderr

+29-9
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ LL | | }
99
| |_____^
1010
|
1111
= note: `-D clippy::match-single-binding` implied by `-D warnings`
12-
help: consider using `let` statement
12+
help: consider using a `let` statement
1313
|
1414
LL ~ let (x, y, z) = (a, b, c);
1515
LL + {
@@ -25,7 +25,7 @@ LL | | (x, y, z) => println!("{} {} {}", x, y, z),
2525
LL | | }
2626
| |_____^
2727
|
28-
help: consider using `let` statement
28+
help: consider using a `let` statement
2929
|
3030
LL ~ let (x, y, z) = (a, b, c);
3131
LL + println!("{} {} {}", x, y, z);
@@ -88,7 +88,7 @@ LL | | Point { x, y } => println!("Coords: ({}, {})", x, y),
8888
LL | | }
8989
| |_____^
9090
|
91-
help: consider using `let` statement
91+
help: consider using a `let` statement
9292
|
9393
LL ~ let Point { x, y } = p;
9494
LL + println!("Coords: ({}, {})", x, y);
@@ -102,7 +102,7 @@ LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1),
102102
LL | | }
103103
| |_____^
104104
|
105-
help: consider using `let` statement
105+
help: consider using a `let` statement
106106
|
107107
LL ~ let Point { x: x1, y: y1 } = p;
108108
LL + println!("Coords: ({}, {})", x1, y1);
@@ -116,7 +116,7 @@ LL | | ref r => println!("Got a reference to {}", r),
116116
LL | | }
117117
| |_____^
118118
|
119-
help: consider using `let` statement
119+
help: consider using a `let` statement
120120
|
121121
LL ~ let ref r = x;
122122
LL + println!("Got a reference to {}", r);
@@ -130,7 +130,7 @@ LL | | ref mut mr => println!("Got a mutable reference to {}", mr),
130130
LL | | }
131131
| |_____^
132132
|
133-
help: consider using `let` statement
133+
help: consider using a `let` statement
134134
|
135135
LL ~ let ref mut mr = x;
136136
LL + println!("Got a mutable reference to {}", mr);
@@ -144,7 +144,7 @@ LL | | Point { x, y } => x * y,
144144
LL | | };
145145
| |______^
146146
|
147-
help: consider using `let` statement
147+
help: consider using a `let` statement
148148
|
149149
LL ~ let Point { x, y } = coords();
150150
LL + let product = x * y;
@@ -159,7 +159,7 @@ LL | | unwrapped => unwrapped,
159159
LL | | })
160160
| |_________^
161161
|
162-
help: consider using `let` statement
162+
help: consider using a `let` statement
163163
|
164164
LL ~ .map(|i| {
165165
LL + let unwrapped = i.unwrap();
@@ -176,5 +176,25 @@ LL | | _ => println!("Not an array index start"),
176176
LL | | }
177177
| |_____^ help: consider using the match body instead: `println!("Not an array index start");`
178178

179-
error: aborting due to 12 previous errors
179+
error: this assignment could be simplified
180+
--> $DIR/match_single_binding.rs:134:5
181+
|
182+
LL | / val = match val.split_at(idx) {
183+
LL | | (pre, suf) => {
184+
LL | | println!("{}", pre);
185+
LL | | suf
186+
LL | | },
187+
LL | | };
188+
| |_____^
189+
|
190+
help: consider removing the `match` expression
191+
|
192+
LL ~ let (pre, suf) = val.split_at(idx);
193+
LL + val = {
194+
LL + println!("{}", pre);
195+
LL + suf
196+
LL ~ };
197+
|
198+
199+
error: aborting due to 13 previous errors
180200

tests/ui/match_single_binding2.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ LL | | },
88
| |_____________^
99
|
1010
= note: `-D clippy::match-single-binding` implied by `-D warnings`
11-
help: consider using `let` statement
11+
help: consider using a `let` statement
1212
|
1313
LL ~ Some((iter, _item)) => {
1414
LL + let (min, max) = iter.size_hint();
@@ -24,7 +24,7 @@ LL | | (a, b) => println!("a {:?} and b {:?}", a, b),
2424
LL | | }
2525
| |_____________^
2626
|
27-
help: consider using `let` statement
27+
help: consider using a `let` statement
2828
|
2929
LL ~ let (a, b) = get_tup();
3030
LL + println!("a {:?} and b {:?}", a, b);

0 commit comments

Comments
 (0)