[rustc_parse] Forbid lets in certain places

This commit is contained in:
Caio 2022-06-25 08:08:38 -03:00
parent e02d645110
commit 747586732b
13 changed files with 468 additions and 196 deletions

View File

@ -7,6 +7,7 @@ use super::{
};
use crate::maybe_recover_from_interpolated_ty_qpath;
use core::mem;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::Spacing;
@ -26,7 +27,6 @@ use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_span::source_map::{self, Span, Spanned};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, Pos};
use std::mem;
/// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression
/// dropped into the token stream, which happens while parsing the result of
@ -2343,7 +2343,9 @@ impl<'a> Parser<'a> {
/// Parses the condition of a `if` or `while` expression.
fn parse_cond_expr(&mut self) -> PResult<'a, P<Expr>> {
let cond = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
let cond = self.with_let_management(true, |local_self| {
local_self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)
})?;
if let ExprKind::Let(..) = cond.kind {
// Remove the last feature gating of a `let` expression since it's stable.
@ -2356,6 +2358,13 @@ impl<'a> Parser<'a> {
/// Parses a `let $pat = $expr` pseudo-expression.
/// The `let` token has already been eaten.
fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
if !self.let_expr_allowed {
self.struct_span_err(
self.prev_token.span,
"expected expression, found `let` statement",
)
.emit();
}
let lo = self.prev_token.span;
let pat = self.parse_pat_allow_top_alt(
None,
@ -2672,6 +2681,8 @@ impl<'a> Parser<'a> {
}
pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
// Used to check the `let_chains` and `if_let_guard` features mostly by scaning
// `&&` tokens.
fn check_let_expr(expr: &Expr) -> (bool, bool) {
match expr.kind {
ExprKind::Binary(_, ref lhs, ref rhs) => {
@ -2694,7 +2705,7 @@ impl<'a> Parser<'a> {
)?;
let guard = if this.eat_keyword(kw::If) {
let if_span = this.prev_token.span;
let cond = this.parse_expr()?;
let cond = this.with_let_management(true, |local_this| local_this.parse_expr())?;
let (has_let_expr, does_not_have_bin_op) = check_let_expr(&cond);
if has_let_expr {
if does_not_have_bin_op {
@ -3256,4 +3267,17 @@ impl<'a> Parser<'a> {
Ok((res, trailing))
})
}
// Calls `f` with the internal `let_expr_allowed` set to `let_expr_allowed` and then
// sets the internal `let_expr_allowed` back to its original value.
fn with_let_management<T>(
&mut self,
let_expr_allowed: bool,
f: impl FnOnce(&mut Self) -> T,
) -> T {
let last_let_expr_allowed = mem::replace(&mut self.let_expr_allowed, let_expr_allowed);
let rslt = f(self);
self.let_expr_allowed = last_let_expr_allowed;
rslt
}
}

View File

@ -147,12 +147,15 @@ pub struct Parser<'a> {
/// This allows us to recover when the user forget to add braces around
/// multiple statements in the closure body.
pub current_closure: Option<ClosureSpans>,
/// Used to track where `let`s are allowed. For example, `if true && let 1 = 1` is valid
/// but `[1, 2, 3][let _ = ()]` is not.
let_expr_allowed: bool,
}
// This type is used a lot, e.g. it's cloned when matching many declarative macro rules. Make sure
// it doesn't unintentionally get bigger.
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
rustc_data_structures::static_assert_size!(Parser<'_>, 328);
rustc_data_structures::static_assert_size!(Parser<'_>, 336);
/// Stores span information about a closure.
#[derive(Clone)]
@ -455,6 +458,7 @@ impl<'a> Parser<'a> {
inner_attr_ranges: Default::default(),
},
current_closure: None,
let_expr_allowed: false,
};
// Make parser point to the first token.

View File

@ -1,6 +1,7 @@
struct Bug<A = [(); (let a = (), 1).1]> {
//~^ `let` expressions are not supported here
//~^^ `let` expressions in this position are unstable [E0658]
//~| `let` expressions in this position are unstable [E0658]
//~| expected expression, found `let` statement
a: A
}

View File

@ -1,3 +1,9 @@
error: expected expression, found `let` statement
--> $DIR/issue-92893.rs:1:22
|
LL | struct Bug<A = [(); (let a = (), 1).1]> {
| ^^^
error: `let` expressions are not supported here
--> $DIR/issue-92893.rs:1:22
|
@ -15,6 +21,6 @@ LL | struct Bug<A = [(); (let a = (), 1).1]> {
= note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
= help: add `#![feature(let_chains)]` to the crate attributes to enable
error: aborting due to 2 previous errors
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0658`.

View File

@ -58,8 +58,10 @@ fn _macros() {
}
use_expr!((let 0 = 1 && 0 == 0));
//~^ ERROR `let` expressions in this position are unstable
//~| ERROR expected expression, found `let` statement
use_expr!((let 0 = 1));
//~^ ERROR `let` expressions in this position are unstable
//~| ERROR expected expression, found `let` statement
match () {
#[cfg(FALSE)]
() if let 0 = 1 => {}

View File

@ -1,5 +1,17 @@
error: expected expression, found `let` statement
--> $DIR/feature-gate.rs:59:16
|
LL | use_expr!((let 0 = 1 && 0 == 0));
| ^^^
error: expected expression, found `let` statement
--> $DIR/feature-gate.rs:62:16
|
LL | use_expr!((let 0 = 1));
| ^^^
error: no rules expected the token `let`
--> $DIR/feature-gate.rs:69:15
--> $DIR/feature-gate.rs:71:15
|
LL | macro_rules! use_expr {
| --------------------- when calling this macro
@ -58,7 +70,7 @@ LL | () if let Range { start: _, end: _ } = (true..true) && false => {}
= help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
error[E0658]: `if let` guards are experimental
--> $DIR/feature-gate.rs:65:12
--> $DIR/feature-gate.rs:67:12
|
LL | () if let 0 = 1 => {}
| ^^^^^^^^^^^^
@ -203,7 +215,7 @@ LL | use_expr!((let 0 = 1 && 0 == 0));
= help: add `#![feature(let_chains)]` to the crate attributes to enable
error[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:61:16
--> $DIR/feature-gate.rs:62:16
|
LL | use_expr!((let 0 = 1));
| ^^^^^^^^^
@ -211,6 +223,6 @@ LL | use_expr!((let 0 = 1));
= note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
= help: add `#![feature(let_chains)]` to the crate attributes to enable
error: aborting due to 23 previous errors
error: aborting due to 25 previous errors
For more information about this error, try `rustc --explain E0658`.

View File

@ -81,9 +81,11 @@ fn _macros() {
use_expr!((let 0 = 1 && 0 == 0));
//~^ ERROR `let` expressions are not supported here
//~| ERROR `let` expressions are not supported here
//~| ERROR expected expression, found `let` statement
use_expr!((let 0 = 1));
//~^ ERROR `let` expressions are not supported here
//~| ERROR `let` expressions are not supported here
//~| ERROR expected expression, found `let` statement
}
fn nested_within_if_expr() {
@ -147,7 +149,8 @@ fn nested_within_if_expr() {
//~| ERROR mismatched types
//~| ERROR mismatched types
if let true = let true = true {} //~ ERROR `let` expressions are not supported here
if let true = let true = true {}
//~^ ERROR `let` expressions are not supported here
}
fn nested_within_while_expr() {
@ -211,7 +214,8 @@ fn nested_within_while_expr() {
//~| ERROR mismatched types
//~| ERROR mismatched types
while let true = let true = true {} //~ ERROR `let` expressions are not supported here
while let true = let true = true {}
//~^ ERROR `let` expressions are not supported here
}
fn not_error_because_clarified_intent() {
@ -225,45 +229,85 @@ fn not_error_because_clarified_intent() {
}
fn outside_if_and_while_expr() {
&let 0 = 0; //~ ERROR `let` expressions are not supported here
&let 0 = 0;
//~^ ERROR `let` expressions are not supported here
//~| ERROR expected expression, found `let` statement
!let 0 = 0; //~ ERROR `let` expressions are not supported here
*let 0 = 0; //~ ERROR `let` expressions are not supported here
//~^ ERROR type `bool` cannot be dereferenced
-let 0 = 0; //~ ERROR `let` expressions are not supported here
//~^ ERROR cannot apply unary operator `-` to type `bool`
!let 0 = 0;
//~^ ERROR `let` expressions are not supported here
//~| ERROR expected expression, found `let` statement
*let 0 = 0;
//~^ ERROR `let` expressions are not supported here
//~| ERROR type `bool` cannot be dereferenced
//~| ERROR expected expression, found `let` statement
-let 0 = 0;
//~^ ERROR `let` expressions are not supported here
//~| ERROR cannot apply unary operator `-` to type `bool`
//~| ERROR expected expression, found `let` statement
fn _check_try_binds_tighter() -> Result<(), ()> {
let 0 = 0?;
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
Ok(())
}
(let 0 = 0)?; //~ ERROR `let` expressions are not supported here
//~^ ERROR the `?` operator can only be used in a function that returns `Result`
(let 0 = 0)?;
//~^ ERROR `let` expressions are not supported here
//~| ERROR the `?` operator can only be used in a function that returns `Result`
//~| ERROR the `?` operator can only be applied to values that implement `Try`
//~| ERROR expected expression, found `let` statement
true || let 0 = 0; //~ ERROR `let` expressions are not supported here
(true || let 0 = 0); //~ ERROR `let` expressions are not supported here
true && (true || let 0 = 0); //~ ERROR `let` expressions are not supported here
true || let 0 = 0;
//~^ ERROR `let` expressions are not supported here
//~| ERROR expected expression, found `let` statement
(true || let 0 = 0);
//~^ ERROR `let` expressions are not supported here
//~| ERROR expected expression, found `let` statement
true && (true || let 0 = 0);
//~^ ERROR `let` expressions are not supported here
//~| ERROR expected expression, found `let` statement
let mut x = true;
x = let 0 = 0; //~ ERROR `let` expressions are not supported here
x = let 0 = 0;
//~^ ERROR `let` expressions are not supported here
//~| ERROR expected expression, found `let` statement
true..(let 0 = 0); //~ ERROR `let` expressions are not supported here
..(let 0 = 0); //~ ERROR `let` expressions are not supported here
(let 0 = 0)..; //~ ERROR `let` expressions are not supported here
true..(let 0 = 0);
//~^ ERROR `let` expressions are not supported here
//~| ERROR expected expression, found `let` statement
..(let 0 = 0);
//~^ ERROR `let` expressions are not supported here
//~| ERROR expected expression, found `let` statement
(let 0 = 0)..;
//~^ ERROR `let` expressions are not supported here
//~| ERROR expected expression, found `let` statement
(let Range { start: _, end: _ } = true..true || false);
//~^ ERROR `let` expressions are not supported here
//~| ERROR mismatched types
//~| ERROR expected expression, found `let` statement
(let true = let true = true);
//~^ ERROR `let` expressions are not supported here
//~| ERROR expected expression, found `let` statement
//~| ERROR expected expression, found `let` statement
{
#[cfg(FALSE)]
let x = true && let y = 1;
//~^ ERROR expected expression, found `let` statement
}
#[cfg(FALSE)]
{
[1, 2, 3][let _ = ()]
//~^ ERROR expected expression, found `let` statement
}
// Check function tail position.
&let 0 = 0
//~^ ERROR `let` expressions are not supported here
//~| ERROR mismatched types
//~| ERROR expected expression, found `let` statement
}
// Let's make sure that `let` inside const generic arguments are considered.
@ -335,4 +379,14 @@ fn with_parenthesis() {
let fun = || true;
if let true = (true && fun()) && (true) {
}
#[cfg(FALSE)]
let x = (true && let y = 1);
//~^ ERROR expected expression, found `let` statement
#[cfg(FALSE)]
{
([1, 2, 3][let _ = ()])
//~^ ERROR expected expression, found `let` statement
}
}

View File

@ -17,6 +17,7 @@ fn main() {
//~| ERROR `let` expressions are not supported here
//~| ERROR mismatched types
//~| ERROR mismatched types
//~| ERROR expected expression, found `let` statement
return;
};

View File

@ -9,6 +9,12 @@ help: wrap the expression in parentheses
LL | let Some(n) = (opt && n == 1) else {
| + +
error: expected expression, found `let` statement
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:15:26
|
LL | let Some(n) = opt && let another = n else {
| ^^^
error: a `&&` expression cannot be directly assigned in `let...else`
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:15:19
|
@ -21,43 +27,43 @@ LL | let Some(n) = (opt && let another = n) else {
| + +
error: this `if` expression is missing a block after the condition
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:23:5
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:24:5
|
LL | if let Some(n) = opt else {
| ^^
|
help: add a block here
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:23:25
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:24:25
|
LL | if let Some(n) = opt else {
| ^
error: this `if` expression is missing a block after the condition
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:27:5
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:28:5
|
LL | if let Some(n) = opt && n == 1 else {
| ^^
|
help: add a block here
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:27:35
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:28:35
|
LL | if let Some(n) = opt && n == 1 else {
| ^
error: this `if` expression is missing a block after the condition
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:31:5
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:32:5
|
LL | if let Some(n) = opt && let another = n else {
| ^^
|
help: add a block here
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:31:44
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:32:44
|
LL | if let Some(n) = opt && let another = n else {
| ^
error: expected `{`, found keyword `else`
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:37:33
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:38:33
|
LL | while let Some(n) = opt else {
| ----- ----------------- ^^^^ expected `{`
@ -66,7 +72,7 @@ LL | while let Some(n) = opt else {
| while parsing the body of this `while` expression
error: expected `{`, found keyword `else`
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:43:43
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:44:43
|
LL | while let Some(n) = opt && n == 1 else {
| ----- --------------------------- ^^^^ expected `{`
@ -75,7 +81,7 @@ LL | while let Some(n) = opt && n == 1 else {
| while parsing the body of this `while` expression
error: expected `{`, found keyword `else`
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:49:52
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:50:52
|
LL | while let Some(n) = opt && let another = n else {
| ----- ------------------------------------ ^^^^ expected `{`
@ -131,6 +137,6 @@ LL | let Some(n) = opt && let another = n else {
= note: expected type `bool`
found enum `Option<_>`
error: aborting due to 13 previous errors
error: aborting due to 14 previous errors
For more information about this error, try `rustc --explain E0308`.

View File

@ -39,6 +39,7 @@ fn _macros() {
noop_expr!((let 0 = 1));
//~^ ERROR `let` expressions in this position are unstable [E0658]
//~| ERROR expected expression, found `let` statement
macro_rules! use_expr {
($e:expr) => {
@ -48,9 +49,9 @@ fn _macros() {
}
#[cfg(FALSE)] (let 0 = 1);
//~^ ERROR `let` expressions in this position are unstable [E0658]
//~| ERROR expected expression, found `let` statement
use_expr!(let 0 = 1);
//~^ ERROR no rules expected the token `let`
// ^--- FIXME(53667): Consider whether `Let` can be added to `ident_can_begin_expr`.
}
fn main() {}

View File

@ -1,5 +1,17 @@
error: expected expression, found `let` statement
--> $DIR/feature-gate.rs:50:20
|
LL | #[cfg(FALSE)] (let 0 = 1);
| ^^^
error: expected expression, found `let` statement
--> $DIR/feature-gate.rs:40:17
|
LL | noop_expr!((let 0 = 1));
| ^^^
error: no rules expected the token `let`
--> $DIR/feature-gate.rs:51:15
--> $DIR/feature-gate.rs:53:15
|
LL | macro_rules! use_expr {
| --------------------- when calling this macro
@ -62,7 +74,7 @@ LL | while let Range { start: _, end: _ } = (true..true) && false {}
= help: add `#![feature(let_chains)]` to the crate attributes to enable
error[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:49:20
--> $DIR/feature-gate.rs:50:20
|
LL | #[cfg(FALSE)] (let 0 = 1);
| ^^^^^^^^^
@ -79,6 +91,6 @@ LL | noop_expr!((let 0 = 1));
= note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
= help: add `#![feature(let_chains)]` to the crate attributes to enable
error: aborting due to 9 previous errors
error: aborting due to 11 previous errors
For more information about this error, try `rustc --explain E0658`.

View File

@ -0,0 +1,17 @@
// check-pass
// known-bug
#![feature(let_chains)]
fn main() {
let _opt = Some(1i32);
#[cfg(FALSE)]
{
if let Some(elem) = _opt && {
[1, 2, 3][let _ = ()];
true
} {
}
}
}