Skip to content

Commit 4ace190

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

File tree

11 files changed

+65
-27
lines changed

11 files changed

+65
-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: 47 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;
@@ -32,6 +32,7 @@ use crate::errors::*;
3232
use crate::fluent_generated as fluent;
3333

3434
pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
35+
//println!("check_match: {:?}, span: {:?}", def_id, tcx.def_span(def_id));
3536
let typeck_results = tcx.typeck(def_id);
3637
let (thir, expr) = tcx.thir_body(def_id)?;
3738
let thir = thir.borrow();
@@ -62,7 +63,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
6263

6364
for param in thir.params.iter() {
6465
if let Some(box ref pattern) = param.pat {
65-
visitor.check_binding_is_irrefutable(pattern, origin, None, None);
66+
visitor.check_binding_is_irrefutable(pattern, origin, None, None, None);
6667
}
6768
}
6869
visitor.error
@@ -160,8 +161,8 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
160161
} => {
161162
self.check_match(scrutinee, arms, MatchSource::Normal, span);
162163
}
163-
ExprKind::Let { box ref pat, expr } => {
164-
self.check_let(pat, Some(expr), ex.span);
164+
ExprKind::Let { box ref pat, expr, pat_hir_id } => {
165+
self.check_let(pat, Some(expr), ex.span, pat_hir_id);
165166
}
166167
ExprKind::LogicalOp { op: LogicalOp::And, .. }
167168
if !matches!(self.let_source, LetSource::None) =>
@@ -182,13 +183,19 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
182183
fn visit_stmt(&mut self, stmt: &'p Stmt<'tcx>) {
183184
match stmt.kind {
184185
StmtKind::Let {
185-
box ref pattern, initializer, else_block, lint_level, span, ..
186+
box ref pattern,
187+
initializer,
188+
else_block,
189+
lint_level,
190+
span,
191+
pat_hir_id,
192+
..
186193
} => {
187194
self.with_lint_level(lint_level, |this| {
188195
let let_source =
189196
if else_block.is_some() { LetSource::LetElse } else { LetSource::PlainLet };
190197
this.with_let_source(let_source, |this| {
191-
this.check_let(pattern, initializer, span)
198+
this.check_let(pattern, initializer, span, pat_hir_id)
192199
});
193200
visit::walk_stmt(this, stmt);
194201
});
@@ -262,7 +269,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
262269
ExprKind::Scope { value, lint_level, .. } => {
263270
self.with_lint_level(lint_level, |this| this.visit_land_rhs(&this.thir[value]))
264271
}
265-
ExprKind::Let { box ref pat, expr } => {
272+
ExprKind::Let { box ref pat, expr, pat_hir_id: _ } => {
266273
let expr = &self.thir()[expr];
267274
self.with_let_source(LetSource::None, |this| {
268275
this.visit_expr(expr);
@@ -439,11 +446,23 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
439446
}
440447

441448
#[instrument(level = "trace", skip(self))]
442-
fn check_let(&mut self, pat: &'p Pat<'tcx>, scrutinee: Option<ExprId>, span: Span) {
449+
fn check_let(
450+
&mut self,
451+
pat: &'p Pat<'tcx>,
452+
scrutinee: Option<ExprId>,
453+
span: Span,
454+
pat_hir_id: HirId,
455+
) {
443456
assert!(self.let_source != LetSource::None);
444457
let scrut = scrutinee.map(|id| &self.thir[id]);
445458
if let LetSource::PlainLet = self.let_source {
446-
self.check_binding_is_irrefutable(pat, "local binding", scrut, Some(span))
459+
self.check_binding_is_irrefutable(
460+
pat,
461+
"local binding",
462+
scrut,
463+
Some(span),
464+
Some(pat_hir_id),
465+
)
447466
} else {
448467
let Ok(refutability) = self.is_let_irrefutable(pat, scrut) else { return };
449468
if matches!(refutability, Irrefutable) {
@@ -519,6 +538,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
519538
"`for` loop binding",
520539
None,
521540
None,
541+
None,
522542
);
523543
} else {
524544
// span after scrutinee, or after `.match`. That is, the braces, arms,
@@ -665,6 +685,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
665685
origin: &str,
666686
scrut: Option<&Expr<'tcx>>,
667687
sp: Option<Span>,
688+
pat_hir_id: Option<HirId>,
668689
) {
669690
let pattern_ty = pat.ty;
670691

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

773811
/// 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)