Skip to content

Commit 09a3a24

Browse files
committed
Supress suggest let else when no let in refutable bindings
Signed-off-by: xizheyin <[email protected]>
1 parent 8cbd78c commit 09a3a24

File tree

3 files changed

+34
-18
lines changed

3 files changed

+34
-18
lines changed

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use rustc_errors::codes::*;
66
use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, struct_span_code_err};
77
use rustc_hir::def::*;
88
use rustc_hir::def_id::LocalDefId;
9-
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, MatchSource};
9+
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, LocalSource, MatchSource, Node};
1010
use rustc_infer::infer::TyCtxtInferExt;
1111
use rustc_lint::Level;
1212
use rustc_middle::bug;
@@ -62,7 +62,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
6262

6363
for param in thir.params.iter() {
6464
if let Some(box ref pattern) = param.pat {
65-
visitor.check_binding_is_irrefutable(pattern, origin, None, None);
65+
visitor.check_binding_is_irrefutable(pattern, origin, None, None, None);
6666
}
6767
}
6868
visitor.error
@@ -161,7 +161,7 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
161161
self.check_match(scrutinee, arms, MatchSource::Normal, span);
162162
}
163163
ExprKind::Let { box ref pat, expr } => {
164-
self.check_let(pat, Some(expr), ex.span);
164+
self.check_let(pat, Some(expr), ex.span, None);
165165
}
166166
ExprKind::LogicalOp { op: LogicalOp::And, .. }
167167
if !matches!(self.let_source, LetSource::None) =>
@@ -188,7 +188,12 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
188188
let let_source =
189189
if else_block.is_some() { LetSource::LetElse } else { LetSource::PlainLet };
190190
this.with_let_source(let_source, |this| {
191-
this.check_let(pattern, initializer, span)
191+
// FIXME(#145548): We get hir_id from the lint_level, replace it if thir has a better way to get it.
192+
let hir_id = match lint_level {
193+
LintLevel::Explicit(hir_id) => Some(hir_id),
194+
_ => None,
195+
};
196+
this.check_let(pattern, initializer, span, hir_id)
192197
});
193198
visit::walk_stmt(this, stmt);
194199
});
@@ -439,11 +444,17 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
439444
}
440445

441446
#[instrument(level = "trace", skip(self))]
442-
fn check_let(&mut self, pat: &'p Pat<'tcx>, scrutinee: Option<ExprId>, span: Span) {
447+
fn check_let(
448+
&mut self,
449+
pat: &'p Pat<'tcx>,
450+
scrutinee: Option<ExprId>,
451+
span: Span,
452+
hir_id: Option<HirId>,
453+
) {
443454
assert!(self.let_source != LetSource::None);
444455
let scrut = scrutinee.map(|id| &self.thir[id]);
445456
if let LetSource::PlainLet = self.let_source {
446-
self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span))
457+
self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span), hir_id)
447458
} else {
448459
let Ok(refutability) = self.is_let_irrefutable(pat, scrut) else { return };
449460
if matches!(refutability, Irrefutable) {
@@ -519,6 +530,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
519530
"`for` loop binding",
520531
None,
521532
None,
533+
None,
522534
);
523535
} else {
524536
// span after scrutinee, or after `.match`. That is, the braces, arms,
@@ -665,6 +677,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
665677
origin: &str,
666678
scrut: Option<&Expr<'tcx>>,
667679
sp: Option<Span>,
680+
hir_id: Option<HirId>,
668681
) {
669682
let pattern_ty = pat.ty;
670683

@@ -715,6 +728,8 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
715728
&& self.tcx.sess.source_map().is_span_accessible(span)
716729
&& interpreted_as_const.is_none()
717730
&& scrut.is_some()
731+
// we only suggest `let else` when the pattern is not desugared from an assignment
732+
&& !self.is_from_destructing_assignment(hir_id)
718733
{
719734
let mut bindings = vec![];
720735
pat.each_binding(|name, _, _, _| bindings.push(name));
@@ -768,6 +783,19 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
768783
adt_defined_here,
769784
}));
770785
}
786+
787+
/// Check if hir_id of LetStmt is desugared into a `let`, i.e., `LocalSource::AssignDesugar`
788+
/// rather than a real `let` the user wrote. This helps suppress suggestions for `let` statements
789+
/// when we're dealing with an assignment like `Some(x) = rhs`. See #145548.
790+
fn is_from_destructing_assignment(&self, hir_id: Option<HirId>) -> bool {
791+
if let Some(hir_id) = hir_id {
792+
// pat_hir_id is the hir_id of the pattern in the `let` statement/expr.
793+
if let Node::LetStmt(let_stmt) = self.tcx.hir_node(hir_id) {
794+
return matches!(let_stmt.source, LocalSource::AssignDesugar(_));
795+
}
796+
}
797+
false
798+
}
771799
}
772800

773801
/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.

tests/ui/destructuring-assignment/non-exhaustive-destructure.stderr

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ LL | None = Some(3);
77
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
88
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
99
= note: the matched value is of type `Option<i32>`
10-
help: you might want to use `if let` to ignore the variant that isn't matched
11-
|
12-
LL | if None = Some(3) { todo!() };
13-
| ++ +++++++++++
1410

1511
error: aborting due to 1 previous error
1612

tests/ui/let-else/suggest-with-let-issue-145548.stderr

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ LL | Some(x) = Some(1);
77
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
88
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
99
= note: the matched value is of type `Option<i32>`
10-
help: you might want to use `let else` to handle the variant that isn't matched
11-
|
12-
LL | Some(x) = Some(1) else { todo!() };
13-
| ++++++++++++++++
1410

1511
error[E0005]: refutable pattern in local binding
1612
--> $DIR/suggest-with-let-issue-145548.rs:11:9
@@ -35,10 +31,6 @@ LL | Some(()) = Some(());
3531
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
3632
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
3733
= note: the matched value is of type `Option<()>`
38-
help: you might want to use `if let` to ignore the variant that isn't matched
39-
|
40-
LL | if Some(()) = Some(()) { todo!() };
41-
| ++ +++++++++++
4234

4335
error: aborting due to 3 previous errors
4436

0 commit comments

Comments
 (0)