Skip to content

gate let_chains in all places before the 2024 edition #140198

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 15 additions & 28 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2605,8 +2605,7 @@
let lo = self.prev_token.span;
// Scoping code checks the top level edition of the `if`; let's match it here.
// The `CondChecker` also checks the edition of the `let` itself, just to make sure.
let let_chains_policy = LetChainsPolicy::EditionDependent { current_edition: lo.edition() };
let cond = self.parse_expr_cond(let_chains_policy)?;
let cond = self.parse_expr_cond(lo.edition())?;
self.parse_if_after_cond(lo, cond)
}

Expand Down Expand Up @@ -2716,16 +2715,16 @@

/// Parses the condition of a `if` or `while` expression.
///
/// The specified `edition` in `let_chains_policy` should be that of the whole `if` construct,
/// i.e. the same span we use to later decide whether the drop behaviour should be that of
/// edition `..=2021` or that of `2024..`.
/// The specified `edition` should be that of the whole `if` construct, i.e. the same span
/// we use to later decide whether the drop behaviour should be that of edition `..=2021`
/// or that of `2024..`.
// Public because it is used in rustfmt forks such as https://github.com/tucant/rustfmt/blob/30c83df9e1db10007bdd16dafce8a86b404329b2/src/parse/macros/html.rs#L57 for custom if expressions.
pub fn parse_expr_cond(&mut self, let_chains_policy: LetChainsPolicy) -> PResult<'a, P<Expr>> {
pub fn parse_expr_cond(&mut self, edition: Edition) -> PResult<'a, P<Expr>> {
let attrs = self.parse_outer_attributes()?;
let (mut cond, _) =
self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?;

CondChecker::new(self, let_chains_policy).visit_expr(&mut cond);
CondChecker::new(self, edition).visit_expr(&mut cond);

Ok(cond)
}
Expand Down Expand Up @@ -3020,8 +3019,7 @@

/// Parses a `while` or `while let` expression (`while` token already eaten).
fn parse_expr_while(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
let policy = LetChainsPolicy::EditionDependent { current_edition: lo.edition() };
let cond = self.parse_expr_cond(policy).map_err(|mut err| {
let cond = self.parse_expr_cond(lo.edition()).map_err(|mut err| {
err.span_label(lo, "while parsing the condition of this `while` expression");
err
})?;
Expand Down Expand Up @@ -3426,7 +3424,8 @@
let if_span = self.prev_token.span;
let mut cond = self.parse_match_guard_condition()?;

CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond);
// TODO explain

Check failure on line 3427 in compiler/rustc_parse/src/parser/expr.rs

View workflow job for this annotation

GitHub Actions / PR - mingw-check-tidy

TODO is used for tasks that should be done before merging a PR; If you want to leave a message in the codebase use FIXME
CondChecker::new(self, if_span.edition()).visit_expr(&mut cond);

if has_let_expr(&cond) {
let span = if_span.to(cond.span);
Expand Down Expand Up @@ -3455,7 +3454,7 @@
unreachable!()
};
self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span);
CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond);
CondChecker::new(self, span.edition()).visit_expr(&mut cond);
let right = self.prev_token.span;
self.dcx().emit_err(errors::ParenthesesInMatchPat {
span: vec![left, right],
Expand Down Expand Up @@ -4026,13 +4025,6 @@
NotSupportedParentheses(#[primary_span] Span),
}

/// Whether let chains are allowed on all editions, or it's edition dependent (allowed only on
/// 2024 and later). In case of edition dependence, specify the currently present edition.
pub enum LetChainsPolicy {
AlwaysAllowed,
EditionDependent { current_edition: Edition },
}

/// Visitor to check for invalid use of `ExprKind::Let` that can't
/// easily be caught in parsing. For example:
///
Expand All @@ -4044,21 +4036,21 @@
/// ```
struct CondChecker<'a> {
parser: &'a Parser<'a>,
let_chains_policy: LetChainsPolicy,
current_edition: Edition,
depth: u32,
forbid_let_reason: Option<ForbiddenLetReason>,
missing_let: Option<errors::MaybeMissingLet>,
comparison: Option<errors::MaybeComparison>,
}

impl<'a> CondChecker<'a> {
fn new(parser: &'a Parser<'a>, let_chains_policy: LetChainsPolicy) -> Self {
fn new(parser: &'a Parser<'a>, current_edition: Edition) -> Self {
CondChecker {
parser,
forbid_let_reason: None,
missing_let: None,
comparison: None,
let_chains_policy,
current_edition,
depth: 0,
}
}
Expand All @@ -4083,13 +4075,8 @@
));
} else if self.depth > 1 {
// Top level `let` is always allowed; only gate chains
match self.let_chains_policy {
LetChainsPolicy::AlwaysAllowed => (),
LetChainsPolicy::EditionDependent { current_edition } => {
if !current_edition.at_least_rust_2024() || !span.at_least_rust_2024() {
self.parser.psess.gated_spans.gate(sym::let_chains, span);
}
}
if !self.current_edition.at_least_rust_2024() || !span.at_least_rust_2024() {
self.parser.psess.gated_spans.gate(sym::let_chains, span);
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ fn _if_let_guard() {

() if true && let 0 = 1 => {}
//~^ ERROR `if let` guards are experimental
//~| ERROR `let` expressions in this position are unstable

() if let 0 = 1 && true => {}
//~^ ERROR `if let` guards are experimental
//~| ERROR `let` expressions in this position are unstable

() if (let 0 = 1) && true => {}
//~^ ERROR expected expression, found `let` statement
Expand All @@ -31,13 +33,16 @@ fn _if_let_guard() {

() if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
//~^ ERROR `if let` guards are experimental
//~| ERROR `let` expressions in this position are unstable
//~| ERROR `let` expressions in this position are unstable
//~| ERROR expected expression, found `let` statement
//~| ERROR expected expression, found `let` statement
//~| ERROR expected expression, found `let` statement


() if let Range { start: _, end: _ } = (true..true) && false => {}
//~^ ERROR `if let` guards are experimental
//~| ERROR `let` expressions in this position are unstable

_ => {}
}
Expand Down
100 changes: 75 additions & 25 deletions tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -25,106 +25,106 @@ LL | () if (((let 0 = 1))) => {}
| ^^^^^^^^^

error: expected expression, found `let` statement
--> $DIR/feature-gate.rs:22:16
--> $DIR/feature-gate.rs:24:16
|
LL | () if (let 0 = 1) && true => {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
--> $DIR/feature-gate.rs:22:16
--> $DIR/feature-gate.rs:24:16
|
LL | () if (let 0 = 1) && true => {}
| ^^^^^^^^^

error: expected expression, found `let` statement
--> $DIR/feature-gate.rs:25:24
--> $DIR/feature-gate.rs:27:24
|
LL | () if true && (let 0 = 1) => {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
--> $DIR/feature-gate.rs:25:24
--> $DIR/feature-gate.rs:27:24
|
LL | () if true && (let 0 = 1) => {}
| ^^^^^^^^^

error: expected expression, found `let` statement
--> $DIR/feature-gate.rs:28:16
--> $DIR/feature-gate.rs:30:16
|
LL | () if (let 0 = 1) && (let 0 = 1) => {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
--> $DIR/feature-gate.rs:28:16
--> $DIR/feature-gate.rs:30:16
|
LL | () if (let 0 = 1) && (let 0 = 1) => {}
| ^^^^^^^^^

error: expected expression, found `let` statement
--> $DIR/feature-gate.rs:28:31
--> $DIR/feature-gate.rs:30:31
|
LL | () if (let 0 = 1) && (let 0 = 1) => {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
--> $DIR/feature-gate.rs:28:31
--> $DIR/feature-gate.rs:30:31
|
LL | () if (let 0 = 1) && (let 0 = 1) => {}
| ^^^^^^^^^

error: expected expression, found `let` statement
--> $DIR/feature-gate.rs:32:42
--> $DIR/feature-gate.rs:34:42
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
--> $DIR/feature-gate.rs:32:42
--> $DIR/feature-gate.rs:34:42
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: expected expression, found `let` statement
--> $DIR/feature-gate.rs:32:55
--> $DIR/feature-gate.rs:34:55
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
--> $DIR/feature-gate.rs:32:42
--> $DIR/feature-gate.rs:34:42
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: expected expression, found `let` statement
--> $DIR/feature-gate.rs:32:68
--> $DIR/feature-gate.rs:34:68
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^^^^^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
note: `let`s wrapped in parentheses are not supported in a context with let chains
--> $DIR/feature-gate.rs:32:42
--> $DIR/feature-gate.rs:34:42
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: expected expression, found `let` statement
--> $DIR/feature-gate.rs:55:16
--> $DIR/feature-gate.rs:60:16
|
LL | use_expr!((let 0 = 1 && 0 == 0));
| ^^^
|
= note: only supported directly in conditions of `if` and `while` expressions

error: expected expression, found `let` statement
--> $DIR/feature-gate.rs:55:16
--> $DIR/feature-gate.rs:60:16
|
LL | use_expr!((let 0 = 1 && 0 == 0));
| ^^^
Expand All @@ -133,15 +133,15 @@ LL | use_expr!((let 0 = 1 && 0 == 0));
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: expected expression, found `let` statement
--> $DIR/feature-gate.rs:58:16
--> $DIR/feature-gate.rs:63:16
|
LL | use_expr!((let 0 = 1));
| ^^^
|
= note: only supported directly in conditions of `if` and `while` expressions

error: expected expression, found `let` statement
--> $DIR/feature-gate.rs:58:16
--> $DIR/feature-gate.rs:63:16
|
LL | use_expr!((let 0 = 1));
| ^^^
Expand All @@ -150,7 +150,7 @@ LL | use_expr!((let 0 = 1));
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: no rules expected keyword `let`
--> $DIR/feature-gate.rs:67:15
--> $DIR/feature-gate.rs:72:15
|
LL | macro_rules! use_expr {
| --------------------- when calling this macro
Expand All @@ -159,7 +159,7 @@ LL | use_expr!(let 0 = 1);
| ^^^ no rules expected this token in macro call
|
note: while trying to match meta-variable `$e:expr`
--> $DIR/feature-gate.rs:48:10
--> $DIR/feature-gate.rs:53:10
|
LL | ($e:expr) => {
| ^^^^^^^
Expand Down Expand Up @@ -187,7 +187,7 @@ LL | () if true && let 0 = 1 => {}
= 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:19:12
--> $DIR/feature-gate.rs:20:12
|
LL | () if let 0 = 1 && true => {}
| ^^^^^^^^^^^^^^^^^^^^
Expand All @@ -198,7 +198,7 @@ LL | () if let 0 = 1 && true => {}
= 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:32:12
--> $DIR/feature-gate.rs:34:12
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -209,7 +209,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 =
= 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:39:12
--> $DIR/feature-gate.rs:43:12
|
LL | () if let Range { start: _, end: _ } = (true..true) && false => {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -220,7 +220,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:63:12
--> $DIR/feature-gate.rs:68:12
|
LL | () if let 0 = 1 => {}
| ^^^^^^^^^^^^
Expand All @@ -230,6 +230,56 @@ LL | () if let 0 = 1 => {}
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`

error: aborting due to 20 previous errors
error[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:16:23
|
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
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:20:15
|
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
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:34:15
|
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
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error[E0658]: `let` expressions in this position are unstable
--> $DIR/feature-gate.rs:34:28
|
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
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error[E0658]: `let` expressions in this position are unstable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this fine in the 2024 edition? If so it should say that rather than it being unstable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's due to the way #132833 was done: it just exempted 2024 usages from the feature gate instead of stabilizing the feature gate and adding an edition dependent error. I left that for future work, as I didn't know when it would stabilize. Plus, I saw that a lot of compiler crates were using let chains and I didn't want to play catch up with all the #![feature(let_chains)] changes happening that occur as the compiler does changes naturally (knowing that the stabilization would likely take months).

--> $DIR/feature-gate.rs:43:15
|
LL | () if let Range { start: _, end: _ } = (true..true) && false => {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= 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
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error: aborting due to 25 previous errors

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