Skip to content

Commit f83c94c

Browse files
authored
fix: manual_let_else missing binding mode (#14204)
fixes #13768 The existing implemention forgets to handle the binding mode (i.e. `ref` and `ref mut`), so I just modified it to cover these cases. changelog: [`manual_let_else`]: fix missing binding mode
2 parents 19930f9 + 68679f2 commit f83c94c

File tree

3 files changed

+94
-19
lines changed

3 files changed

+94
-19
lines changed

clippy_lints/src/manual_let_else.rs

+19-18
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use clippy_utils::higher::IfLetOrMatch;
55
use clippy_utils::source::snippet_with_context;
66
use clippy_utils::ty::is_type_diagnostic_item;
77
use clippy_utils::{is_lint_allowed, is_never_expr, msrvs, pat_and_expr_can_be_question_mark, peel_blocks};
8-
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
8+
use rustc_ast::BindingMode;
9+
use rustc_data_structures::fx::FxHashMap;
910
use rustc_errors::Applicability;
1011
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind};
1112
use rustc_lint::{LateContext, LintContext};
@@ -113,7 +114,7 @@ fn emit_manual_let_else(
113114
cx: &LateContext<'_>,
114115
span: Span,
115116
expr: &Expr<'_>,
116-
ident_map: &FxHashMap<Symbol, &Pat<'_>>,
117+
ident_map: &FxHashMap<Symbol, (&Pat<'_>, BindingMode)>,
117118
pat: &Pat<'_>,
118119
else_body: &Expr<'_>,
119120
) {
@@ -167,7 +168,7 @@ fn emit_manual_let_else(
167168
fn replace_in_pattern(
168169
cx: &LateContext<'_>,
169170
span: Span,
170-
ident_map: &FxHashMap<Symbol, &Pat<'_>>,
171+
ident_map: &FxHashMap<Symbol, (&Pat<'_>, BindingMode)>,
171172
pat: &Pat<'_>,
172173
app: &mut Applicability,
173174
top_level: bool,
@@ -185,15 +186,16 @@ fn replace_in_pattern(
185186

186187
match pat.kind {
187188
PatKind::Binding(_ann, _id, binding_name, opt_subpt) => {
188-
let Some(pat_to_put) = ident_map.get(&binding_name.name) else {
189+
let Some((pat_to_put, binding_mode)) = ident_map.get(&binding_name.name) else {
189190
break 'a;
190191
};
192+
let sn_pfx = binding_mode.prefix_str();
191193
let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app);
192194
if let Some(subpt) = opt_subpt {
193195
let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false);
194-
return format!("{sn_ptp} @ {subpt}");
196+
return format!("{sn_pfx}{sn_ptp} @ {subpt}");
195197
}
196-
return sn_ptp.to_string();
198+
return format!("{sn_pfx}{sn_ptp}");
197199
},
198200
PatKind::Or(pats) => {
199201
let patterns = pats
@@ -211,17 +213,18 @@ fn replace_in_pattern(
211213
.iter()
212214
.map(|fld| {
213215
if let PatKind::Binding(_, _, name, None) = fld.pat.kind
214-
&& let Some(pat_to_put) = ident_map.get(&name.name)
216+
&& let Some((pat_to_put, binding_mode)) = ident_map.get(&name.name)
215217
{
218+
let sn_pfx = binding_mode.prefix_str();
216219
let (sn_fld_name, _) = snippet_with_context(cx, fld.ident.span, span.ctxt(), "", app);
217220
let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app);
218221
// TODO: this is a bit of a hack, but it does its job. Ideally, we'd check if pat_to_put is
219222
// a PatKind::Binding but that is also hard to get right.
220223
if sn_fld_name == sn_ptp {
221224
// Field init shorthand
222-
return format!("{sn_fld_name}");
225+
return format!("{sn_pfx}{sn_fld_name}");
223226
}
224-
return format!("{sn_fld_name}: {sn_ptp}");
227+
return format!("{sn_fld_name}: {sn_pfx}{sn_ptp}");
225228
}
226229
let (sn_fld, _) = snippet_with_context(cx, fld.span, span.ctxt(), "", app);
227230
sn_fld.into_owned()
@@ -334,7 +337,7 @@ fn expr_simple_identity_map<'a, 'hir>(
334337
local_pat: &'a Pat<'hir>,
335338
let_pat: &'_ Pat<'hir>,
336339
expr: &'_ Expr<'hir>,
337-
) -> Option<FxHashMap<Symbol, &'a Pat<'hir>>> {
340+
) -> Option<FxHashMap<Symbol, (&'a Pat<'hir>, BindingMode)>> {
338341
let peeled = peel_blocks(expr);
339342
let (sub_pats, paths) = match (local_pat.kind, peeled.kind) {
340343
(PatKind::Tuple(pats, _), ExprKind::Tup(exprs)) | (PatKind::Slice(pats, ..), ExprKind::Array(exprs)) => {
@@ -351,9 +354,9 @@ fn expr_simple_identity_map<'a, 'hir>(
351354
return None;
352355
}
353356

354-
let mut pat_bindings = FxHashSet::default();
355-
let_pat.each_binding_or_first(&mut |_ann, _hir_id, _sp, ident| {
356-
pat_bindings.insert(ident);
357+
let mut pat_bindings = FxHashMap::default();
358+
let_pat.each_binding_or_first(&mut |binding_mode, _hir_id, _sp, ident| {
359+
pat_bindings.insert(ident, binding_mode);
357360
});
358361
if pat_bindings.len() < paths.len() {
359362
// This rebinds some bindings from the outer scope, or it repeats some copy-able bindings multiple
@@ -366,12 +369,10 @@ fn expr_simple_identity_map<'a, 'hir>(
366369
for (sub_pat, path) in sub_pats.iter().zip(paths.iter()) {
367370
if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind
368371
&& let [path_seg] = path.segments
372+
&& let ident = path_seg.ident
373+
&& let Some(let_binding_mode) = pat_bindings.remove(&ident)
369374
{
370-
let ident = path_seg.ident;
371-
if !pat_bindings.remove(&ident) {
372-
return None;
373-
}
374-
ident_map.insert(ident.name, sub_pat);
375+
ident_map.insert(ident.name, (sub_pat, let_binding_mode));
375376
} else {
376377
return None;
377378
}

tests/ui/manual_let_else.rs

+34
Original file line numberDiff line numberDiff line change
@@ -480,3 +480,37 @@ fn issue12337() {
480480
//~^ manual_let_else
481481
};
482482
}
483+
484+
mod issue13768 {
485+
enum Foo {
486+
Str(String),
487+
None,
488+
}
489+
490+
fn foo(value: Foo) {
491+
let signature = match value {
492+
//~^ manual_let_else
493+
Foo::Str(ref val) => val,
494+
_ => {
495+
println!("No signature found");
496+
return;
497+
},
498+
};
499+
}
500+
501+
enum Bar {
502+
Str { inner: String },
503+
None,
504+
}
505+
506+
fn bar(mut value: Bar) {
507+
let signature = match value {
508+
//~^ manual_let_else
509+
Bar::Str { ref mut inner } => inner,
510+
_ => {
511+
println!("No signature found");
512+
return;
513+
},
514+
};
515+
}
516+
}

tests/ui/manual_let_else.stderr

+41-1
Original file line numberDiff line numberDiff line change
@@ -489,5 +489,45 @@ error: this could be rewritten as `let...else`
489489
LL | let v = if let Some(v_some) = g() { v_some } else { return };
490490
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };`
491491

492-
error: aborting due to 31 previous errors
492+
error: this could be rewritten as `let...else`
493+
--> tests/ui/manual_let_else.rs:491:9
494+
|
495+
LL | / let signature = match value {
496+
LL | |
497+
LL | | Foo::Str(ref val) => val,
498+
LL | | _ => {
499+
... |
500+
LL | | },
501+
LL | | };
502+
| |__________^
503+
|
504+
help: consider writing
505+
|
506+
LL ~ let Foo::Str(ref signature) = value else {
507+
LL + println!("No signature found");
508+
LL + return;
509+
LL + };
510+
|
511+
512+
error: this could be rewritten as `let...else`
513+
--> tests/ui/manual_let_else.rs:507:9
514+
|
515+
LL | / let signature = match value {
516+
LL | |
517+
LL | | Bar::Str { ref mut inner } => inner,
518+
LL | | _ => {
519+
... |
520+
LL | | },
521+
LL | | };
522+
| |__________^
523+
|
524+
help: consider writing
525+
|
526+
LL ~ let Bar::Str { inner: ref mut signature } = value else {
527+
LL + println!("No signature found");
528+
LL + return;
529+
LL + };
530+
|
531+
532+
error: aborting due to 33 previous errors
493533

0 commit comments

Comments
 (0)