Rollup merge of #95008 - c410-f3r:let-chains-paren, r=wesleywiser

[`let_chains`] Forbid `let` inside parentheses

Parenthesizes are mostly a no-op in let chains, in other words, they are mostly ignored.

```rust
let opt = Some(Some(1i32));

if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
    println!("`b` is declared inside but used outside");
}
```

As seen above, such behavior can lead to confusion.

A proper fix or nested encapsulation would probably require research, time and a modified MIR graph so in this PR I simply denied any `let` inside parentheses. Non-let stuff are still allowed.

```rust
fn main() {
    let fun = || true;

    if let true = (true && fun()) && (true) {
        println!("Allowed");
    }
}
```

It is worth noting that `let ...`  is not an expression and the RFC did not mention this specific situation.

cc `@matthewjasper`
This commit is contained in:
Dylan DPC 2022-04-11 20:00:40 +02:00 committed by GitHub
commit 2ad701e450
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 751 additions and 473 deletions

View file

@ -120,12 +120,21 @@ impl<'a> AstValidator<'a> {
let err = "`let` expressions are not supported here";
let mut diag = sess.struct_span_err(expr.span, err);
diag.note("only supported directly in conditions of `if` and `while` expressions");
diag.note("as well as when nested within `&&` and parentheses in those conditions");
if let ForbiddenLetReason::ForbiddenWithOr(span) = forbidden_let_reason {
diag.span_note(
span,
"`||` operators are not currently supported in let chain expressions",
);
match forbidden_let_reason {
ForbiddenLetReason::GenericForbidden => {}
ForbiddenLetReason::NotSupportedOr(span) => {
diag.span_note(
span,
"`||` operators are not supported in let chain expressions",
);
}
ForbiddenLetReason::NotSupportedParentheses(span) => {
diag.span_note(
span,
"`let`s wrapped in parentheses are not supported in a context with let \
chains",
);
}
}
diag.emit();
} else {
@ -1009,9 +1018,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.with_let_management(Some(ForbiddenLetReason::GenericForbidden), |this, forbidden_let_reason| {
match &expr.kind {
ExprKind::Binary(Spanned { node: BinOpKind::Or, span }, lhs, rhs) => {
let forbidden_let_reason = Some(ForbiddenLetReason::ForbiddenWithOr(*span));
this.with_let_management(forbidden_let_reason, |this, _| this.visit_expr(lhs));
this.with_let_management(forbidden_let_reason, |this, _| this.visit_expr(rhs));
let local_reason = Some(ForbiddenLetReason::NotSupportedOr(*span));
this.with_let_management(local_reason, |this, _| this.visit_expr(lhs));
this.with_let_management(local_reason, |this, _| this.visit_expr(rhs));
}
ExprKind::If(cond, then, opt_else) => {
this.visit_block(then);
@ -1036,7 +1045,23 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
}
}
ExprKind::Paren(_) | ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, ..) => {
ExprKind::Paren(local_expr) => {
fn has_let_expr(expr: &Expr) -> bool {
match expr.kind {
ExprKind::Binary(_, ref lhs, ref rhs) => has_let_expr(lhs) || has_let_expr(rhs),
ExprKind::Let(..) => true,
_ => false,
}
}
let local_reason = if has_let_expr(local_expr) {
Some(ForbiddenLetReason::NotSupportedParentheses(local_expr.span))
}
else {
forbidden_let_reason
};
this.with_let_management(local_reason, |this, _| this.visit_expr(local_expr));
}
ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, ..) => {
this.with_let_management(forbidden_let_reason, |this, _| visit::walk_expr(this, expr));
return;
}
@ -1810,8 +1835,13 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) ->
/// Used to forbid `let` expressions in certain syntactic locations.
#[derive(Clone, Copy)]
enum ForbiddenLetReason {
/// A let chain with the `||` operator
ForbiddenWithOr(Span),
/// `let` is not valid and the source environment is not important
GenericForbidden,
/// A let chain with the `||` operator
NotSupportedOr(Span),
/// A let chain with invalid parentheses
///
/// For exemple, `let 1 = 1 && (expr && expr)` is allowed
/// but `(let 1 = 1 && (let 1 = 1 && (let 1 = 1))) && let a = 1` is not
NotSupportedParentheses(Span),
}

View file

@ -25,6 +25,67 @@ use std::ops::Range;
fn main() {}
fn _if() {
if (let 0 = 1) {}
//~^ ERROR `let` expressions are not supported here
if (((let 0 = 1))) {}
//~^ ERROR `let` expressions are not supported here
if (let 0 = 1) && true {}
//~^ ERROR `let` expressions are not supported here
if true && (let 0 = 1) {}
//~^ ERROR `let` expressions are not supported here
if (let 0 = 1) && (let 0 = 1) {}
//~^ ERROR `let` expressions are not supported here
//~| ERROR `let` expressions are not supported here
if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
//~^ ERROR `let` expressions are not supported here
//~| ERROR `let` expressions are not supported here
//~| ERROR `let` expressions are not supported here
}
fn _while() {
while (let 0 = 1) {}
//~^ ERROR `let` expressions are not supported here
while (((let 0 = 1))) {}
//~^ ERROR `let` expressions are not supported here
while (let 0 = 1) && true {}
//~^ ERROR `let` expressions are not supported here
while true && (let 0 = 1) {}
//~^ ERROR `let` expressions are not supported here
while (let 0 = 1) && (let 0 = 1) {}
//~^ ERROR `let` expressions are not supported here
//~| ERROR `let` expressions are not supported here
while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
//~^ ERROR `let` expressions are not supported here
//~| ERROR `let` expressions are not supported here
//~| ERROR `let` expressions are not supported here
}
fn _macros() {
macro_rules! use_expr {
($e:expr) => {
if $e {}
while $e {}
}
}
use_expr!((let 0 = 1 && 0 == 0));
//~^ ERROR `let` expressions are not supported here
//~| ERROR `let` expressions are not supported here
use_expr!((let 0 = 1));
//~^ ERROR `let` expressions are not supported here
//~| ERROR `let` expressions are not supported here
}
fn nested_within_if_expr() {
if &let 0 = 0 {} //~ ERROR `let` expressions are not supported here
//~^ ERROR mismatched types
@ -234,3 +295,44 @@ fn inside_const_generic_arguments() {
//~| ERROR expressions must be enclosed in braces
>::O == 5 {}
}
fn with_parenthesis() {
let opt = Some(Some(1i32));
if (let Some(a) = opt && true) {
//~^ ERROR `let` expressions are not supported here
}
if (let Some(a) = opt) && true {
//~^ ERROR `let` expressions are not supported here
}
if (let Some(a) = opt) && (let Some(b) = a) {
//~^ ERROR `let` expressions are not supported here
//~| ERROR `let` expressions are not supported here
}
if let Some(a) = opt && (true && true) {
}
if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
//~^ ERROR `let` expressions are not supported here
//~| ERROR `let` expressions are not supported here
}
if (let Some(a) = opt && (let Some(b) = a)) && true {
//~^ ERROR `let` expressions are not supported here
//~| ERROR `let` expressions are not supported here
}
if (let Some(a) = opt && (true)) && true {
//~^ ERROR `let` expressions are not supported here
}
if (true && (true)) && let Some(a) = opt {
}
if (true) && let Some(a) = opt {
}
if true && let Some(a) = opt {
}
let fun = || true;
if let true = (true && fun()) && (true) {
}
}

View file

@ -72,7 +72,6 @@ LL | let Some(n) = opt && let another = n else {
| ^^^^^^^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
= note: as well as when nested within `&&` and parentheses in those conditions
error[E0308]: mismatched types
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:9:19

View file

@ -11,35 +11,12 @@ use std::ops::Range;
fn _if() {
if let 0 = 1 {} // Stable!
if (let 0 = 1) {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
if (((let 0 = 1))) {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
if true && let 0 = 1 {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
if let 0 = 1 && true {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
if (let 0 = 1) && true {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
if true && (let 0 = 1) {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
if (let 0 = 1) && (let 0 = 1) {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
//~| ERROR `let` expressions in this position are unstable [E0658]
if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
//~| ERROR `let` expressions in this position are unstable [E0658]
//~| ERROR `let` expressions in this position are unstable [E0658]
//~| ERROR `let` expressions in this position are unstable [E0658]
//~| ERROR `let` expressions in this position are unstable [E0658]
if let Range { start: _, end: _ } = (true..true) && false {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
}
@ -47,35 +24,12 @@ fn _if() {
fn _while() {
while let 0 = 1 {} // Stable!
while (let 0 = 1) {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
while (((let 0 = 1))) {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
while true && let 0 = 1 {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
while let 0 = 1 && true {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
while (let 0 = 1) && true {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
while true && (let 0 = 1) {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
while (let 0 = 1) && (let 0 = 1) {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
//~| ERROR `let` expressions in this position are unstable [E0658]
while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
//~| ERROR `let` expressions in this position are unstable [E0658]
//~| ERROR `let` expressions in this position are unstable [E0658]
//~| ERROR `let` expressions in this position are unstable [E0658]
//~| ERROR `let` expressions in this position are unstable [E0658]
while let Range { start: _, end: _ } = (true..true) && false {}
//~^ ERROR `let` expressions in this position are unstable [E0658]
}
@ -92,10 +46,6 @@ fn _macros() {
while $e {}
}
}
use_expr!((let 0 = 1 && 0 == 0));
//~^ ERROR `let` expressions in this position are unstable [E0658]
use_expr!((let 0 = 1));
//~^ ERROR `let` expressions in this position are unstable [E0658]
#[cfg(FALSE)] (let 0 = 1);
//~^ ERROR `let` expressions in this position are unstable [E0658]
use_expr!(let 0 = 1);

View file

@ -1,5 +1,5 @@
error: no rules expected the token `let`
--> $DIR/feature-gate.rs:101:15
--> $DIR/feature-gate.rs:51:15
|
LL | macro_rules! use_expr {
| --------------------- when calling this macro
@ -8,25 +8,7 @@ LL | use_expr!(let 0 = 1);
| ^^^ no rules expected this token in macro call
error[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:14:9
|
LL | if (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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:17:11
|
LL | if (((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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:20:16
--> $DIR/feature-gate.rs:14:16
|
LL | if true && let 0 = 1 {}
| ^^^^^^^^^
@ -35,7 +17,7 @@ LL | if true && let 0 = 1 {}
= help: add `#![feature(let_chains)]` to the crate attributes to enable
error[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:23:8
--> $DIR/feature-gate.rs:17:8
|
LL | if let 0 = 1 && true {}
| ^^^^^^^^^
@ -44,88 +26,7 @@ LL | if let 0 = 1 && true {}
= help: add `#![feature(let_chains)]` to the crate attributes to enable
error[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:26:9
|
LL | if (let 0 = 1) && true {}
| ^^^^^^^^^
|
= 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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:29:17
|
LL | if true && (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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:32:9
|
LL | if (let 0 = 1) && (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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:32:24
|
LL | if (let 0 = 1) && (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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:36:8
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= 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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:36:21
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= 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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:36:35
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= 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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:36:48
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= 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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:36:61
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= 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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:43:8
--> $DIR/feature-gate.rs:20:8
|
LL | if let Range { start: _, end: _ } = (true..true) && false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -134,25 +35,7 @@ LL | if 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:50:12
|
LL | while (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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:53:14
|
LL | while (((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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:56:19
--> $DIR/feature-gate.rs:27:19
|
LL | while true && let 0 = 1 {}
| ^^^^^^^^^
@ -161,7 +44,7 @@ LL | while true && let 0 = 1 {}
= help: add `#![feature(let_chains)]` to the crate attributes to enable
error[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:59:11
--> $DIR/feature-gate.rs:30:11
|
LL | while let 0 = 1 && true {}
| ^^^^^^^^^
@ -170,88 +53,7 @@ LL | while let 0 = 1 && true {}
= help: add `#![feature(let_chains)]` to the crate attributes to enable
error[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:62:12
|
LL | while (let 0 = 1) && true {}
| ^^^^^^^^^
|
= 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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:65:20
|
LL | while true && (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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:68:12
|
LL | while (let 0 = 1) && (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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:68:27
|
LL | while (let 0 = 1) && (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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:72:11
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= 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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:72:24
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= 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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:72:38
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= 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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:72:51
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= 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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:72:64
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
|
= 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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:79:11
--> $DIR/feature-gate.rs:33:11
|
LL | while let Range { start: _, end: _ } = (true..true) && false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -260,7 +62,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:99:20
--> $DIR/feature-gate.rs:49:20
|
LL | #[cfg(FALSE)] (let 0 = 1);
| ^^^^^^^^^
@ -269,7 +71,7 @@ LL | #[cfg(FALSE)] (let 0 = 1);
= help: add `#![feature(let_chains)]` to the crate attributes to enable
error[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:86:17
--> $DIR/feature-gate.rs:40:17
|
LL | noop_expr!((let 0 = 1));
| ^^^^^^^^^
@ -277,24 +79,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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:95:16
|
LL | use_expr!((let 0 = 1 && 0 == 0));
| ^^^^^^^^^
|
= 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[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:97:16
|
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 33 previous errors
error: aborting due to 9 previous errors
For more information about this error, try `rustc --explain E0658`.