Skip to content

Commit e62a543

Browse files
committed
Account for if (let pat = expr) {}
Partially address #82827.
1 parent 1c77a1f commit e62a543

File tree

8 files changed

+212
-227
lines changed

8 files changed

+212
-227
lines changed

compiler/rustc_ast/src/ast.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1139,6 +1139,14 @@ impl Expr {
11391139
}
11401140
}
11411141

1142+
pub fn peel_parens(&self) -> &Expr {
1143+
let mut expr = self;
1144+
while let ExprKind::Paren(inner) = &expr.kind {
1145+
expr = &inner;
1146+
}
1147+
expr
1148+
}
1149+
11421150
/// Attempts to reparse as `Ty` (for diagnostic purposes).
11431151
pub fn to_ty(&self) -> Option<P<Ty>> {
11441152
let kind = match &self.kind {

compiler/rustc_ast_lowering/src/expr.rs

+47-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,32 @@ 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+
let mut err = self.sess.struct_span_err(
370+
vec![start, end],
371+
"invalid parentheses around `let` expression in `if let`",
372+
);
373+
if self.sess.opts.unstable_features.is_nightly_build() {
374+
err.note(
375+
"only supported directly without parentheses in conditions of `if`- and \
376+
`while`-expressions, as well as in `let` chains within parentheses",
377+
);
378+
} else {
379+
err.note("variable declaration using `let` is a statement, not a condition");
380+
}
381+
err.multipart_suggestion(
382+
"`if let` needs to be written without parentheses",
383+
vec![(start, String::new()), (end, String::new())],
384+
rustc_errors::Applicability::MachineApplicable,
385+
);
386+
err.emit();
387+
// Ideally, we'd remove the feature gating of a `let` expression since we are already
388+
// complaining about it here, but `feature_gate::check_crate` has already run by now:
389+
// self.sess.parse_sess.gated_spans.ungate_last(sym::let_chains, paren.span);
390+
}
391+
349392
/// Emit an error and lower `ast::ExprKind::Let(pat, scrutinee)` into:
350393
/// ```rust
351394
/// match scrutinee { pats => true, _ => false }
@@ -356,8 +399,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
356399
if self.sess.opts.unstable_features.is_nightly_build() {
357400
self.sess
358401
.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")
402+
.note(
403+
"only supported directly without parentheses in conditions of `if`- and \
404+
`while`-expressions, as well as in `let` chains within parentheses",
405+
)
361406
.emit();
362407
} else {
363408
self.sess

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

src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr

+16-32
Original file line numberDiff line numberDiff line change
@@ -175,143 +175,127 @@ error: `let` expressions are not supported here
175175
LL | () if (let 0 = 1) => {}
176176
| ^^^^^^^^^
177177
|
178-
= note: only supported directly in conditions of `if`- and `while`-expressions
179-
= note: as well as when nested within `&&` and parenthesis in those conditions
178+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
180179

181180
error: `let` expressions are not supported here
182181
--> $DIR/feature-gate.rs:14:18
183182
|
184183
LL | () if (((let 0 = 1))) => {}
185184
| ^^^^^^^^^
186185
|
187-
= note: only supported directly in conditions of `if`- and `while`-expressions
188-
= note: as well as when nested within `&&` and parenthesis in those conditions
186+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
189187

190188
error: `let` expressions are not supported here
191189
--> $DIR/feature-gate.rs:18:23
192190
|
193191
LL | () if true && let 0 = 1 => {}
194192
| ^^^^^^^^^
195193
|
196-
= note: only supported directly in conditions of `if`- and `while`-expressions
197-
= note: as well as when nested within `&&` and parenthesis in those conditions
194+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
198195

199196
error: `let` expressions are not supported here
200197
--> $DIR/feature-gate.rs:22:15
201198
|
202199
LL | () if let 0 = 1 && true => {}
203200
| ^^^^^^^^^
204201
|
205-
= note: only supported directly in conditions of `if`- and `while`-expressions
206-
= note: as well as when nested within `&&` and parenthesis in those conditions
202+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
207203

208204
error: `let` expressions are not supported here
209205
--> $DIR/feature-gate.rs:26:16
210206
|
211207
LL | () if (let 0 = 1) && true => {}
212208
| ^^^^^^^^^
213209
|
214-
= note: only supported directly in conditions of `if`- and `while`-expressions
215-
= note: as well as when nested within `&&` and parenthesis in those conditions
210+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
216211

217212
error: `let` expressions are not supported here
218213
--> $DIR/feature-gate.rs:30:24
219214
|
220215
LL | () if true && (let 0 = 1) => {}
221216
| ^^^^^^^^^
222217
|
223-
= note: only supported directly in conditions of `if`- and `while`-expressions
224-
= note: as well as when nested within `&&` and parenthesis in those conditions
218+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
225219

226220
error: `let` expressions are not supported here
227221
--> $DIR/feature-gate.rs:34:16
228222
|
229223
LL | () if (let 0 = 1) && (let 0 = 1) => {}
230224
| ^^^^^^^^^
231225
|
232-
= note: only supported directly in conditions of `if`- and `while`-expressions
233-
= note: as well as when nested within `&&` and parenthesis in those conditions
226+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
234227

235228
error: `let` expressions are not supported here
236229
--> $DIR/feature-gate.rs:34:31
237230
|
238231
LL | () if (let 0 = 1) && (let 0 = 1) => {}
239232
| ^^^^^^^^^
240233
|
241-
= note: only supported directly in conditions of `if`- and `while`-expressions
242-
= note: as well as when nested within `&&` and parenthesis in those conditions
234+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
243235

244236
error: `let` expressions are not supported here
245237
--> $DIR/feature-gate.rs:40:15
246238
|
247239
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
248240
| ^^^^^^^^^
249241
|
250-
= note: only supported directly in conditions of `if`- and `while`-expressions
251-
= note: as well as when nested within `&&` and parenthesis in those conditions
242+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
252243

253244
error: `let` expressions are not supported here
254245
--> $DIR/feature-gate.rs:40:28
255246
|
256247
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
257248
| ^^^^^^^^^
258249
|
259-
= note: only supported directly in conditions of `if`- and `while`-expressions
260-
= note: as well as when nested within `&&` and parenthesis in those conditions
250+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
261251

262252
error: `let` expressions are not supported here
263253
--> $DIR/feature-gate.rs:40:42
264254
|
265255
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
266256
| ^^^^^^^^^
267257
|
268-
= note: only supported directly in conditions of `if`- and `while`-expressions
269-
= note: as well as when nested within `&&` and parenthesis in those conditions
258+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
270259

271260
error: `let` expressions are not supported here
272261
--> $DIR/feature-gate.rs:40:55
273262
|
274263
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
275264
| ^^^^^^^^^
276265
|
277-
= note: only supported directly in conditions of `if`- and `while`-expressions
278-
= note: as well as when nested within `&&` and parenthesis in those conditions
266+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
279267

280268
error: `let` expressions are not supported here
281269
--> $DIR/feature-gate.rs:40:68
282270
|
283271
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
284272
| ^^^^^^^^^
285273
|
286-
= note: only supported directly in conditions of `if`- and `while`-expressions
287-
= note: as well as when nested within `&&` and parenthesis in those conditions
274+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
288275

289276
error: `let` expressions are not supported here
290277
--> $DIR/feature-gate.rs:52:15
291278
|
292279
LL | () if let Range { start: _, end: _ } = (true..true) && false => {}
293280
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
294281
|
295-
= note: only supported directly in conditions of `if`- and `while`-expressions
296-
= note: as well as when nested within `&&` and parenthesis in those conditions
282+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
297283

298284
error: `let` expressions are not supported here
299285
--> $DIR/feature-gate.rs:68:16
300286
|
301287
LL | use_expr!((let 0 = 1 && 0 == 0));
302288
| ^^^^^^^^^
303289
|
304-
= note: only supported directly in conditions of `if`- and `while`-expressions
305-
= note: as well as when nested within `&&` and parenthesis in those conditions
290+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
306291

307292
error: `let` expressions are not supported here
308293
--> $DIR/feature-gate.rs:71:16
309294
|
310295
LL | use_expr!((let 0 = 1));
311296
| ^^^^^^^^^
312297
|
313-
= note: only supported directly in conditions of `if`- and `while`-expressions
314-
= note: as well as when nested within `&&` and parenthesis in those conditions
298+
= note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
315299

316300
error: aborting due to 35 previous errors
317301

0 commit comments

Comments
 (0)