Skip to content

Commit 72da786

Browse files
authored
Rollup merge of rust-lang#97295 - c410-f3r:yet-another-let-chain, r=compiler-errors
[rustc_parse] Forbid `let`s in certain places Currently only forbids in locals to resolve rust-lang#94927 (comment) but feel free to point any other places.
2 parents a336ba5 + 7475867 commit 72da786

13 files changed

+468
-196
lines changed

compiler/rustc_parse/src/parser/expr.rs

+27-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use super::{
77
};
88
use crate::maybe_recover_from_interpolated_ty_qpath;
99

10+
use core::mem;
1011
use rustc_ast::ptr::P;
1112
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
1213
use rustc_ast::tokenstream::Spacing;
@@ -26,7 +27,6 @@ use rustc_session::lint::BuiltinLintDiagnostics;
2627
use rustc_span::source_map::{self, Span, Spanned};
2728
use rustc_span::symbol::{kw, sym, Ident, Symbol};
2829
use rustc_span::{BytePos, Pos};
29-
use std::mem;
3030

3131
/// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression
3232
/// dropped into the token stream, which happens while parsing the result of
@@ -2343,7 +2343,9 @@ impl<'a> Parser<'a> {
23432343

23442344
/// Parses the condition of a `if` or `while` expression.
23452345
fn parse_cond_expr(&mut self) -> PResult<'a, P<Expr>> {
2346-
let cond = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
2346+
let cond = self.with_let_management(true, |local_self| {
2347+
local_self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)
2348+
})?;
23472349

23482350
if let ExprKind::Let(..) = cond.kind {
23492351
// Remove the last feature gating of a `let` expression since it's stable.
@@ -2356,6 +2358,13 @@ impl<'a> Parser<'a> {
23562358
/// Parses a `let $pat = $expr` pseudo-expression.
23572359
/// The `let` token has already been eaten.
23582360
fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
2361+
if !self.let_expr_allowed {
2362+
self.struct_span_err(
2363+
self.prev_token.span,
2364+
"expected expression, found `let` statement",
2365+
)
2366+
.emit();
2367+
}
23592368
let lo = self.prev_token.span;
23602369
let pat = self.parse_pat_allow_top_alt(
23612370
None,
@@ -2672,6 +2681,8 @@ impl<'a> Parser<'a> {
26722681
}
26732682

26742683
pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
2684+
// Used to check the `let_chains` and `if_let_guard` features mostly by scaning
2685+
// `&&` tokens.
26752686
fn check_let_expr(expr: &Expr) -> (bool, bool) {
26762687
match expr.kind {
26772688
ExprKind::Binary(_, ref lhs, ref rhs) => {
@@ -2694,7 +2705,7 @@ impl<'a> Parser<'a> {
26942705
)?;
26952706
let guard = if this.eat_keyword(kw::If) {
26962707
let if_span = this.prev_token.span;
2697-
let cond = this.parse_expr()?;
2708+
let cond = this.with_let_management(true, |local_this| local_this.parse_expr())?;
26982709
let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond);
26992710
if has_let_expr {
27002711
if does_not_have_bin_op {
@@ -3256,4 +3267,17 @@ impl<'a> Parser<'a> {
32563267
Ok((res, trailing))
32573268
})
32583269
}
3270+
3271+
// Calls `f` with the internal `let_expr_allowed` set to `let_expr_allowed` and then
3272+
// sets the internal `let_expr_allowed` back to its original value.
3273+
fn with_let_management<T>(
3274+
&mut self,
3275+
let_expr_allowed: bool,
3276+
f: impl FnOnce(&mut Self) -> T,
3277+
) -> T {
3278+
let last_let_expr_allowed = mem::replace(&mut self.let_expr_allowed, let_expr_allowed);
3279+
let rslt = f(self);
3280+
self.let_expr_allowed = last_let_expr_allowed;
3281+
rslt
3282+
}
32593283
}

compiler/rustc_parse/src/parser/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,15 @@ pub struct Parser<'a> {
147147
/// This allows us to recover when the user forget to add braces around
148148
/// multiple statements in the closure body.
149149
pub current_closure: Option<ClosureSpans>,
150+
/// Used to track where `let`s are allowed. For example, `if true && let 1 = 1` is valid
151+
/// but `[1, 2, 3][let _ = ()]` is not.
152+
let_expr_allowed: bool,
150153
}
151154

152155
// This type is used a lot, e.g. it's cloned when matching many declarative macro rules. Make sure
153156
// it doesn't unintentionally get bigger.
154157
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
155-
rustc_data_structures::static_assert_size!(Parser<'_>, 328);
158+
rustc_data_structures::static_assert_size!(Parser<'_>, 336);
156159

157160
/// Stores span information about a closure.
158161
#[derive(Clone)]
@@ -455,6 +458,7 @@ impl<'a> Parser<'a> {
455458
inner_attr_ranges: Default::default(),
456459
},
457460
current_closure: None,
461+
let_expr_allowed: false,
458462
};
459463

460464
// Make parser point to the first token.

src/test/ui/mir/issue-92893.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
struct Bug<A = [(); (let a = (), 1).1]> {
22
//~^ `let` expressions are not supported here
3-
//~^^ `let` expressions in this position are unstable [E0658]
3+
//~| `let` expressions in this position are unstable [E0658]
4+
//~| expected expression, found `let` statement
45
a: A
56
}
67

src/test/ui/mir/issue-92893.stderr

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
error: expected expression, found `let` statement
2+
--> $DIR/issue-92893.rs:1:22
3+
|
4+
LL | struct Bug<A = [(); (let a = (), 1).1]> {
5+
| ^^^
6+
17
error: `let` expressions are not supported here
28
--> $DIR/issue-92893.rs:1:22
39
|
@@ -15,6 +21,6 @@ LL | struct Bug<A = [(); (let a = (), 1).1]> {
1521
= note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
1622
= help: add `#![feature(let_chains)]` to the crate attributes to enable
1723

18-
error: aborting due to 2 previous errors
24+
error: aborting due to 3 previous errors
1925

2026
For more information about this error, try `rustc --explain E0658`.

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

+2
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,10 @@ fn _macros() {
5858
}
5959
use_expr!((let 0 = 1 && 0 == 0));
6060
//~^ ERROR `let` expressions in this position are unstable
61+
//~| ERROR expected expression, found `let` statement
6162
use_expr!((let 0 = 1));
6263
//~^ ERROR `let` expressions in this position are unstable
64+
//~| ERROR expected expression, found `let` statement
6365
match () {
6466
#[cfg(FALSE)]
6567
() if let 0 = 1 => {}

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

+16-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
1+
error: expected expression, found `let` statement
2+
--> $DIR/feature-gate.rs:59:16
3+
|
4+
LL | use_expr!((let 0 = 1 && 0 == 0));
5+
| ^^^
6+
7+
error: expected expression, found `let` statement
8+
--> $DIR/feature-gate.rs:62:16
9+
|
10+
LL | use_expr!((let 0 = 1));
11+
| ^^^
12+
113
error: no rules expected the token `let`
2-
--> $DIR/feature-gate.rs:69:15
14+
--> $DIR/feature-gate.rs:71:15
315
|
416
LL | macro_rules! use_expr {
517
| --------------------- when calling this macro
@@ -58,7 +70,7 @@ LL | () if let Range { start: _, end: _ } = (true..true) && false => {}
5870
= help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
5971

6072
error[E0658]: `if let` guards are experimental
61-
--> $DIR/feature-gate.rs:65:12
73+
--> $DIR/feature-gate.rs:67:12
6274
|
6375
LL | () if let 0 = 1 => {}
6476
| ^^^^^^^^^^^^
@@ -203,14 +215,14 @@ LL | use_expr!((let 0 = 1 && 0 == 0));
203215
= help: add `#![feature(let_chains)]` to the crate attributes to enable
204216

205217
error[E0658]: `let` expressions in this position are unstable
206-
--> $DIR/feature-gate.rs:61:16
218+
--> $DIR/feature-gate.rs:62:16
207219
|
208220
LL | use_expr!((let 0 = 1));
209221
| ^^^^^^^^^
210222
|
211223
= note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
212224
= help: add `#![feature(let_chains)]` to the crate attributes to enable
213225

214-
error: aborting due to 23 previous errors
226+
error: aborting due to 25 previous errors
215227

216228
For more information about this error, try `rustc --explain E0658`.

src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs

+71-17
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,11 @@ fn _macros() {
8181
use_expr!((let 0 = 1 && 0 == 0));
8282
//~^ ERROR `let` expressions are not supported here
8383
//~| ERROR `let` expressions are not supported here
84+
//~| ERROR expected expression, found `let` statement
8485
use_expr!((let 0 = 1));
8586
//~^ ERROR `let` expressions are not supported here
8687
//~| ERROR `let` expressions are not supported here
88+
//~| ERROR expected expression, found `let` statement
8789
}
8890

8991
fn nested_within_if_expr() {
@@ -147,7 +149,8 @@ fn nested_within_if_expr() {
147149
//~| ERROR mismatched types
148150
//~| ERROR mismatched types
149151

150-
if let true = let true = true {} //~ ERROR `let` expressions are not supported here
152+
if let true = let true = true {}
153+
//~^ ERROR `let` expressions are not supported here
151154
}
152155

153156
fn nested_within_while_expr() {
@@ -211,7 +214,8 @@ fn nested_within_while_expr() {
211214
//~| ERROR mismatched types
212215
//~| ERROR mismatched types
213216

214-
while let true = let true = true {} //~ ERROR `let` expressions are not supported here
217+
while let true = let true = true {}
218+
//~^ ERROR `let` expressions are not supported here
215219
}
216220

217221
fn not_error_because_clarified_intent() {
@@ -225,45 +229,85 @@ fn not_error_because_clarified_intent() {
225229
}
226230

227231
fn outside_if_and_while_expr() {
228-
&let 0 = 0; //~ ERROR `let` expressions are not supported here
232+
&let 0 = 0;
233+
//~^ ERROR `let` expressions are not supported here
234+
//~| ERROR expected expression, found `let` statement
229235

230-
!let 0 = 0; //~ ERROR `let` expressions are not supported here
231-
*let 0 = 0; //~ ERROR `let` expressions are not supported here
232-
//~^ ERROR type `bool` cannot be dereferenced
233-
-let 0 = 0; //~ ERROR `let` expressions are not supported here
234-
//~^ ERROR cannot apply unary operator `-` to type `bool`
236+
!let 0 = 0;
237+
//~^ ERROR `let` expressions are not supported here
238+
//~| ERROR expected expression, found `let` statement
239+
*let 0 = 0;
240+
//~^ ERROR `let` expressions are not supported here
241+
//~| ERROR type `bool` cannot be dereferenced
242+
//~| ERROR expected expression, found `let` statement
243+
-let 0 = 0;
244+
//~^ ERROR `let` expressions are not supported here
245+
//~| ERROR cannot apply unary operator `-` to type `bool`
246+
//~| ERROR expected expression, found `let` statement
235247

236248
fn _check_try_binds_tighter() -> Result<(), ()> {
237249
let 0 = 0?;
238250
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
239251
Ok(())
240252
}
241-
(let 0 = 0)?; //~ ERROR `let` expressions are not supported here
242-
//~^ ERROR the `?` operator can only be used in a function that returns `Result`
253+
(let 0 = 0)?;
254+
//~^ ERROR `let` expressions are not supported here
255+
//~| ERROR the `?` operator can only be used in a function that returns `Result`
243256
//~| ERROR the `?` operator can only be applied to values that implement `Try`
257+
//~| ERROR expected expression, found `let` statement
244258

245-
true || let 0 = 0; //~ ERROR `let` expressions are not supported here
246-
(true || let 0 = 0); //~ ERROR `let` expressions are not supported here
247-
true && (true || let 0 = 0); //~ ERROR `let` expressions are not supported here
259+
true || let 0 = 0;
260+
//~^ ERROR `let` expressions are not supported here
261+
//~| ERROR expected expression, found `let` statement
262+
(true || let 0 = 0);
263+
//~^ ERROR `let` expressions are not supported here
264+
//~| ERROR expected expression, found `let` statement
265+
true && (true || let 0 = 0);
266+
//~^ ERROR `let` expressions are not supported here
267+
//~| ERROR expected expression, found `let` statement
248268

249269
let mut x = true;
250-
x = let 0 = 0; //~ ERROR `let` expressions are not supported here
270+
x = let 0 = 0;
271+
//~^ ERROR `let` expressions are not supported here
272+
//~| ERROR expected expression, found `let` statement
251273

252-
true..(let 0 = 0); //~ ERROR `let` expressions are not supported here
253-
..(let 0 = 0); //~ ERROR `let` expressions are not supported here
254-
(let 0 = 0)..; //~ ERROR `let` expressions are not supported here
274+
true..(let 0 = 0);
275+
//~^ ERROR `let` expressions are not supported here
276+
//~| ERROR expected expression, found `let` statement
277+
..(let 0 = 0);
278+
//~^ ERROR `let` expressions are not supported here
279+
//~| ERROR expected expression, found `let` statement
280+
(let 0 = 0)..;
281+
//~^ ERROR `let` expressions are not supported here
282+
//~| ERROR expected expression, found `let` statement
255283

256284
(let Range { start: _, end: _ } = true..true || false);
257285
//~^ ERROR `let` expressions are not supported here
258286
//~| ERROR mismatched types
287+
//~| ERROR expected expression, found `let` statement
259288

260289
(let true = let true = true);
261290
//~^ ERROR `let` expressions are not supported here
291+
//~| ERROR expected expression, found `let` statement
292+
//~| ERROR expected expression, found `let` statement
293+
294+
{
295+
#[cfg(FALSE)]
296+
let x = true && let y = 1;
297+
//~^ ERROR expected expression, found `let` statement
298+
}
299+
300+
#[cfg(FALSE)]
301+
{
302+
[1, 2, 3][let _ = ()]
303+
//~^ ERROR expected expression, found `let` statement
304+
}
262305

263306
// Check function tail position.
264307
&let 0 = 0
265308
//~^ ERROR `let` expressions are not supported here
266309
//~| ERROR mismatched types
310+
//~| ERROR expected expression, found `let` statement
267311
}
268312

269313
// Let's make sure that `let` inside const generic arguments are considered.
@@ -335,4 +379,14 @@ fn with_parenthesis() {
335379
let fun = || true;
336380
if let true = (true && fun()) && (true) {
337381
}
382+
383+
#[cfg(FALSE)]
384+
let x = (true && let y = 1);
385+
//~^ ERROR expected expression, found `let` statement
386+
387+
#[cfg(FALSE)]
388+
{
389+
([1, 2, 3][let _ = ()])
390+
//~^ ERROR expected expression, found `let` statement
391+
}
338392
}

0 commit comments

Comments
 (0)