Skip to content

Commit 6a55aa1

Browse files
authored
Rollup merge of #82854 - estebank:issue-82827, r=oli-obk
Account for `if (let pat = expr) {}` Fix #82827.
2 parents 0ee2f4c + aa7ac6e commit 6a55aa1

File tree

9 files changed

+262
-229
lines changed

9 files changed

+262
-229
lines changed

compiler/rustc_ast/src/ast.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,14 @@ impl Expr {
11291129
}
11301130
}
11311131

1132+
pub fn peel_parens(&self) -> &Expr {
1133+
let mut expr = self;
1134+
while let ExprKind::Paren(inner) = &expr.kind {
1135+
expr = &inner;
1136+
}
1137+
expr
1138+
}
1139+
11321140
/// Attempts to reparse as `Ty` (for diagnostic purposes).
11331141
pub fn to_ty(&self) -> Option<P<Ty>> {
11341142
let kind = match &self.kind {

compiler/rustc_ast_lowering/src/expr.rs

+40-2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,23 @@ impl<'hir> LoweringContext<'_, 'hir> {
9797
ExprKind::Let(ref pat, ref scrutinee) => {
9898
self.lower_expr_if_let(e.span, pat, scrutinee, then, else_opt.as_deref())
9999
}
100+
ExprKind::Paren(ref paren) => match paren.peel_parens().kind {
101+
ExprKind::Let(ref pat, ref scrutinee) => {
102+
// A user has written `if (let Some(x) = foo) {`, we want to avoid
103+
// confusing them with mentions of nightly features.
104+
// If this logic is changed, you will also likely need to touch
105+
// `unused::UnusedParens::check_expr`.
106+
self.if_let_expr_with_parens(cond, &paren.peel_parens());
107+
self.lower_expr_if_let(
108+
e.span,
109+
pat,
110+
scrutinee,
111+
then,
112+
else_opt.as_deref(),
113+
)
114+
}
115+
_ => self.lower_expr_if(cond, then, else_opt.as_deref()),
116+
},
100117
_ => self.lower_expr_if(cond, then, else_opt.as_deref()),
101118
},
102119
ExprKind::While(ref cond, ref body, opt_label) => self
@@ -346,6 +363,25 @@ impl<'hir> LoweringContext<'_, 'hir> {
346363
hir::ExprKind::Call(f, self.lower_exprs(&real_args))
347364
}
348365

366+
fn if_let_expr_with_parens(&mut self, cond: &Expr, paren: &Expr) {
367+
let start = cond.span.until(paren.span);
368+
let end = paren.span.shrink_to_hi().until(cond.span.shrink_to_hi());
369+
self.sess
370+
.struct_span_err(
371+
vec![start, end],
372+
"invalid parentheses around `let` expression in `if let`",
373+
)
374+
.multipart_suggestion(
375+
"`if let` needs to be written without parentheses",
376+
vec![(start, String::new()), (end, String::new())],
377+
rustc_errors::Applicability::MachineApplicable,
378+
)
379+
.emit();
380+
// Ideally, we'd remove the feature gating of a `let` expression since we are already
381+
// complaining about it here, but `feature_gate::check_crate` has already run by now:
382+
// self.sess.parse_sess.gated_spans.ungate_last(sym::let_chains, paren.span);
383+
}
384+
349385
/// Emit an error and lower `ast::ExprKind::Let(pat, scrutinee)` into:
350386
/// ```rust
351387
/// match scrutinee { pats => true, _ => false }
@@ -356,8 +392,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
356392
if self.sess.opts.unstable_features.is_nightly_build() {
357393
self.sess
358394
.struct_span_err(span, "`let` expressions are not supported here")
359-
.note("only supported directly in conditions of `if`- and `while`-expressions")
360-
.note("as well as when nested within `&&` and parenthesis in those conditions")
395+
.note(
396+
"only supported directly without parentheses in conditions of `if`- and \
397+
`while`-expressions, as well as in `let` chains within parentheses",
398+
)
361399
.emit();
362400
} else {
363401
self.sess

compiler/rustc_ast_passes/src/feature_gate.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -638,8 +638,16 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
638638
}
639639
};
640640
}
641-
gate_all!(if_let_guard, "`if let` guards are experimental");
642-
gate_all!(let_chains, "`let` expressions in this position are experimental");
641+
gate_all!(
642+
if_let_guard,
643+
"`if let` guards are experimental",
644+
"you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`"
645+
);
646+
gate_all!(
647+
let_chains,
648+
"`let` expressions in this position are experimental",
649+
"you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`"
650+
);
643651
gate_all!(
644652
async_closure,
645653
"async closures are unstable",

compiler/rustc_lint/src/unused.rs

+28-3
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ trait UnusedDelimLint {
602602
use rustc_ast::ExprKind::*;
603603
let (value, ctx, followed_by_block, left_pos, right_pos) = match e.kind {
604604
// Do not lint `unused_braces` in `if let` expressions.
605-
If(ref cond, ref block, ..)
605+
If(ref cond, ref block, _)
606606
if !matches!(cond.kind, Let(_, _)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
607607
{
608608
let left = e.span.lo() + rustc_span::BytePos(2);
@@ -816,8 +816,33 @@ impl UnusedParens {
816816

817817
impl EarlyLintPass for UnusedParens {
818818
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
819-
if let ExprKind::Let(ref pat, ..) | ExprKind::ForLoop(ref pat, ..) = e.kind {
820-
self.check_unused_parens_pat(cx, pat, false, false);
819+
match e.kind {
820+
ExprKind::Let(ref pat, _) | ExprKind::ForLoop(ref pat, ..) => {
821+
self.check_unused_parens_pat(cx, pat, false, false);
822+
}
823+
// We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already
824+
// handle a hard error for them during AST lowering in `lower_expr_mut`, but we still
825+
// want to complain about things like `if let 42 = (42)`.
826+
ExprKind::If(ref cond, ref block, ref else_)
827+
if matches!(cond.peel_parens().kind, ExprKind::Let(..)) =>
828+
{
829+
self.check_unused_delims_expr(
830+
cx,
831+
cond.peel_parens(),
832+
UnusedDelimsCtx::LetScrutineeExpr,
833+
true,
834+
None,
835+
None,
836+
);
837+
for stmt in &block.stmts {
838+
<Self as UnusedDelimLint>::check_stmt(self, cx, stmt);
839+
}
840+
if let Some(e) = else_ {
841+
<Self as UnusedDelimLint>::check_expr(self, cx, e);
842+
}
843+
return;
844+
}
845+
_ => {}
821846
}
822847

823848
<Self as UnusedDelimLint>::check_expr(self, cx, e)

src/test/ui/pattern/issue-82290.stderr

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ error: `let` expressions are not supported here
44
LL | if true && let x = 1 {
55
| ^^^^^^^^^
66
|
7-
= note: only supported directly in conditions of `if`- and `while`-expressions
8-
= note: as well as when nested within `&&` and parenthesis in those conditions
7+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
98

109
warning: the feature `let_chains` is incomplete and may not be safe to use and/or cause compiler crashes
1110
--> $DIR/issue-82290.rs:1:12

0 commit comments

Comments
 (0)