Skip to content

Commit 6a2078f

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

File tree

11 files changed

+64
-27
lines changed

11 files changed

+64
-27
lines changed

compiler/rustc_middle/src/thir.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ pub enum StmtKind<'tcx> {
241241

242242
/// Span of the `let <PAT> = <INIT>` part.
243243
span: Span,
244+
245+
pat_hir_id: HirId,
244246
},
245247
}
246248

@@ -396,6 +398,7 @@ pub enum ExprKind<'tcx> {
396398
Let {
397399
expr: ExprId,
398400
pat: Box<Pat<'tcx>>,
401+
pat_hir_id: HirId,
399402
},
400403
/// A `match` expression.
401404
Match {
@@ -1129,7 +1132,7 @@ mod size_asserts {
11291132
static_assert_size!(ExprKind<'_>, 40);
11301133
static_assert_size!(Pat<'_>, 64);
11311134
static_assert_size!(PatKind<'_>, 48);
1132-
static_assert_size!(Stmt<'_>, 48);
1133-
static_assert_size!(StmtKind<'_>, 48);
1135+
static_assert_size!(Stmt<'_>, 56);
1136+
static_assert_size!(StmtKind<'_>, 56);
11341137
// tidy-alphabetical-end
11351138
}

compiler/rustc_middle/src/thir/visit.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
7979
PointerCoercion { source, cast: _, is_from_as_cast: _ } => {
8080
visitor.visit_expr(&visitor.thir()[source])
8181
}
82-
Let { expr, ref pat } => {
82+
Let { expr, ref pat, pat_hir_id: _ } => {
8383
visitor.visit_expr(&visitor.thir()[expr]);
8484
visitor.visit_pat(pat);
8585
}
@@ -209,6 +209,7 @@ pub fn walk_stmt<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
209209
lint_level: _,
210210
else_block,
211211
span: _,
212+
pat_hir_id: _,
212213
} => {
213214
if let Some(init) = initializer {
214215
visitor.visit_expr(&visitor.thir()[*init]);

compiler/rustc_mir_build/src/builder/block.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
8686
lint_level,
8787
else_block: Some(else_block),
8888
span: _,
89+
pat_hir_id: _,
8990
} => {
9091
// When lowering the statement `let <pat> = <expr> else { <else> };`,
9192
// the `<else>` block is nested in the parent scope enclosing this statement.
@@ -235,6 +236,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
235236
lint_level,
236237
else_block: None,
237238
span: _,
239+
pat_hir_id: _,
238240
} => {
239241
let ignores_expr_result = matches!(pattern.kind, PatKind::Wild);
240242
this.block_context.push(BlockFrame::Statement { ignores_expr_result });

compiler/rustc_mir_build/src/builder/matches/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
186186
})
187187
}
188188
ExprKind::Use { source } => this.then_else_break_inner(block, source, args),
189-
ExprKind::Let { expr, ref pat } => this.lower_let_expr(
189+
ExprKind::Let { expr, ref pat, pat_hir_id: _ } => this.lower_let_expr(
190190
block,
191191
expr,
192192
pat,
@@ -774,7 +774,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
774774
visibility_scope: Option<SourceScope>,
775775
) {
776776
match self.thir.exprs[guard_expr].kind {
777-
ExprKind::Let { expr: _, pat: ref guard_pat } => {
777+
ExprKind::Let { expr: _, pat: ref guard_pat, pat_hir_id: _ } => {
778778
// FIXME: pass a proper `opt_match_place`
779779
self.declare_bindings(visibility_scope, scope_span, guard_pat, None, None);
780780
}

compiler/rustc_mir_build/src/thir/cx/block.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
112112
else_block,
113113
lint_level: LintLevel::Explicit(local.hir_id),
114114
span,
115+
pat_hir_id: local.pat.hir_id,
115116
},
116117
};
117118
Some(self.thir.stmts.push(stmt))

compiler/rustc_mir_build/src/thir/cx/expr.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
892892
hir::ExprKind::Let(let_expr) => ExprKind::Let {
893893
expr: self.mirror_expr(let_expr.init),
894894
pat: self.pattern_from_hir(let_expr.pat),
895+
pat_hir_id: let_expr.pat.hir_id,
895896
},
896897
hir::ExprKind::If(cond, then, else_opt) => ExprKind::If {
897898
if_then_scope: region::Scope {

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

Lines changed: 46 additions & 9 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
@@ -160,8 +160,8 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
160160
} => {
161161
self.check_match(scrutinee, arms, MatchSource::Normal, span);
162162
}
163-
ExprKind::Let { box ref pat, expr } => {
164-
self.check_let(pat, Some(expr), ex.span);
163+
ExprKind::Let { box ref pat, expr, pat_hir_id } => {
164+
self.check_let(pat, Some(expr), ex.span, pat_hir_id);
165165
}
166166
ExprKind::LogicalOp { op: LogicalOp::And, .. }
167167
if !matches!(self.let_source, LetSource::None) =>
@@ -182,13 +182,19 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
182182
fn visit_stmt(&mut self, stmt: &'p Stmt<'tcx>) {
183183
match stmt.kind {
184184
StmtKind::Let {
185-
box ref pattern, initializer, else_block, lint_level, span, ..
185+
box ref pattern,
186+
initializer,
187+
else_block,
188+
lint_level,
189+
span,
190+
pat_hir_id,
191+
..
186192
} => {
187193
self.with_lint_level(lint_level, |this| {
188194
let let_source =
189195
if else_block.is_some() { LetSource::LetElse } else { LetSource::PlainLet };
190196
this.with_let_source(let_source, |this| {
191-
this.check_let(pattern, initializer, span)
197+
this.check_let(pattern, initializer, span, pat_hir_id)
192198
});
193199
visit::walk_stmt(this, stmt);
194200
});
@@ -262,7 +268,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
262268
ExprKind::Scope { value, lint_level, .. } => {
263269
self.with_lint_level(lint_level, |this| this.visit_land_rhs(&this.thir[value]))
264270
}
265-
ExprKind::Let { box ref pat, expr } => {
271+
ExprKind::Let { box ref pat, expr, pat_hir_id: _ } => {
266272
let expr = &self.thir()[expr];
267273
self.with_let_source(LetSource::None, |this| {
268274
this.visit_expr(expr);
@@ -439,11 +445,23 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
439445
}
440446

441447
#[instrument(level = "trace", skip(self))]
442-
fn check_let(&mut self, pat: &'p Pat<'tcx>, scrutinee: Option<ExprId>, span: Span) {
448+
fn check_let(
449+
&mut self,
450+
pat: &'p Pat<'tcx>,
451+
scrutinee: Option<ExprId>,
452+
span: Span,
453+
pat_hir_id: HirId,
454+
) {
443455
assert!(self.let_source != LetSource::None);
444456
let scrut = scrutinee.map(|id| &self.thir[id]);
445457
if let LetSource::PlainLet = self.let_source {
446-
self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span))
458+
self.check_binding_is_irrefutable(
459+
pat,
460+
"local binding",
461+
scrut,
462+
Some(span),
463+
Some(pat_hir_id),
464+
)
447465
} else {
448466
let Ok(refutability) = self.is_let_irrefutable(pat, scrut) else { return };
449467
if matches!(refutability, Irrefutable) {
@@ -519,6 +537,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
519537
"`for` loop binding",
520538
None,
521539
None,
540+
None,
522541
);
523542
} else {
524543
// span after scrutinee, or after `.match`. That is, the braces, arms,
@@ -665,6 +684,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
665684
origin: &str,
666685
scrut: Option<&Expr<'tcx>>,
667686
sp: Option<Span>,
687+
pat_hir_id: Option<HirId>,
668688
) {
669689
let pattern_ty = pat.ty;
670690

@@ -715,6 +735,9 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
715735
&& self.tcx.sess.source_map().is_span_accessible(span)
716736
&& interpreted_as_const.is_none()
717737
&& scrut.is_some()
738+
// we only suggest `let else` when the pattern is not desugared from an assignment
739+
&& let Some(pat_hir_id) = pat_hir_id
740+
&& !self.is_from_destructing_assignment(pat_hir_id)
718741
{
719742
let mut bindings = vec![];
720743
pat.each_binding(|name, _, _, _| bindings.push(name));
@@ -768,6 +791,20 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
768791
adt_defined_here,
769792
}));
770793
}
794+
795+
/// Check if `pat` is desugared into a `let`, i.e., `LocalSource::AssignDesugar`
796+
/// rather than a real `let` the user wrote.
797+
/// This helps suppress suggestions for `let` statements/exprs when
798+
/// we're dealing with an assignment like `Some(x) = rhs`.
799+
fn is_from_destructing_assignment(&self, pat_hir_id: HirId) -> bool {
800+
// pat_hir_id is the hir_id of the pattern in the `let` statement/expr.
801+
for (_, node) in self.tcx.hir_parent_iter(pat_hir_id) {
802+
if let Node::LetStmt(let_stmt) = node {
803+
return matches!(let_stmt.source, LocalSource::AssignDesugar(_));
804+
}
805+
}
806+
false
807+
}
771808
}
772809

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

compiler/rustc_mir_build/src/thir/print.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
144144
else_block,
145145
lint_level,
146146
span,
147+
pat_hir_id,
147148
} => {
148149
print_indented!(self, "kind: Let {", depth_lvl + 1);
149150
print_indented!(
@@ -175,6 +176,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
175176

176177
print_indented!(self, format!("lint_level: {:?}", lint_level), depth_lvl + 2);
177178
print_indented!(self, format!("span: {:?}", span), depth_lvl + 2);
179+
print_indented!(self, format!("pat_hir_id: {:?}", pat_hir_id), depth_lvl + 2);
178180
print_indented!(self, "}", depth_lvl + 1);
179181
}
180182
}
@@ -337,11 +339,12 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
337339
print_indented!(self, "}", depth_lvl + 2);
338340
print_indented!(self, "}", depth_lvl);
339341
}
340-
Let { expr, pat } => {
342+
Let { expr, pat, pat_hir_id } => {
341343
print_indented!(self, "Let {", depth_lvl);
342344
print_indented!(self, "expr:", depth_lvl + 1);
343345
self.print_expr(*expr, depth_lvl + 2);
344346
print_indented!(self, format!("pat: {:?}", pat), depth_lvl + 1);
347+
print_indented!(self, format!("pat_hir_id: {:?}", pat_hir_id), depth_lvl + 1);
345348
print_indented!(self, "}", depth_lvl);
346349
}
347350
Match { scrutinee, arms, .. } => {

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)