Skip to content

Commit 2ad701e

Browse files
authored
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`
2 parents 625e4dd + 6ee3c47 commit 2ad701e

File tree

6 files changed

+751
-473
lines changed

6 files changed

+751
-473
lines changed

Diff for: compiler/rustc_ast_passes/src/ast_validation.rs

+42-12
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,21 @@ impl<'a> AstValidator<'a> {
120120
let err = "`let` expressions are not supported here";
121121
let mut diag = sess.struct_span_err(expr.span, err);
122122
diag.note("only supported directly in conditions of `if` and `while` expressions");
123-
diag.note("as well as when nested within `&&` and parentheses in those conditions");
124-
if let ForbiddenLetReason::ForbiddenWithOr(span) = forbidden_let_reason {
125-
diag.span_note(
126-
span,
127-
"`||` operators are not currently supported in let chain expressions",
128-
);
123+
match forbidden_let_reason {
124+
ForbiddenLetReason::GenericForbidden => {}
125+
ForbiddenLetReason::NotSupportedOr(span) => {
126+
diag.span_note(
127+
span,
128+
"`||` operators are not supported in let chain expressions",
129+
);
130+
}
131+
ForbiddenLetReason::NotSupportedParentheses(span) => {
132+
diag.span_note(
133+
span,
134+
"`let`s wrapped in parentheses are not supported in a context with let \
135+
chains",
136+
);
137+
}
129138
}
130139
diag.emit();
131140
} else {
@@ -1009,9 +1018,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
10091018
self.with_let_management(Some(ForbiddenLetReason::GenericForbidden), |this, forbidden_let_reason| {
10101019
match &expr.kind {
10111020
ExprKind::Binary(Spanned { node: BinOpKind::Or, span }, lhs, rhs) => {
1012-
let forbidden_let_reason = Some(ForbiddenLetReason::ForbiddenWithOr(*span));
1013-
this.with_let_management(forbidden_let_reason, |this, _| this.visit_expr(lhs));
1014-
this.with_let_management(forbidden_let_reason, |this, _| this.visit_expr(rhs));
1021+
let local_reason = Some(ForbiddenLetReason::NotSupportedOr(*span));
1022+
this.with_let_management(local_reason, |this, _| this.visit_expr(lhs));
1023+
this.with_let_management(local_reason, |this, _| this.visit_expr(rhs));
10151024
}
10161025
ExprKind::If(cond, then, opt_else) => {
10171026
this.visit_block(then);
@@ -1036,7 +1045,23 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
10361045
}
10371046
}
10381047
}
1039-
ExprKind::Paren(_) | ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, ..) => {
1048+
ExprKind::Paren(local_expr) => {
1049+
fn has_let_expr(expr: &Expr) -> bool {
1050+
match expr.kind {
1051+
ExprKind::Binary(_, ref lhs, ref rhs) => has_let_expr(lhs) || has_let_expr(rhs),
1052+
ExprKind::Let(..) => true,
1053+
_ => false,
1054+
}
1055+
}
1056+
let local_reason = if has_let_expr(local_expr) {
1057+
Some(ForbiddenLetReason::NotSupportedParentheses(local_expr.span))
1058+
}
1059+
else {
1060+
forbidden_let_reason
1061+
};
1062+
this.with_let_management(local_reason, |this, _| this.visit_expr(local_expr));
1063+
}
1064+
ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, ..) => {
10401065
this.with_let_management(forbidden_let_reason, |this, _| visit::walk_expr(this, expr));
10411066
return;
10421067
}
@@ -1810,8 +1835,13 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) ->
18101835
/// Used to forbid `let` expressions in certain syntactic locations.
18111836
#[derive(Clone, Copy)]
18121837
enum ForbiddenLetReason {
1813-
/// A let chain with the `||` operator
1814-
ForbiddenWithOr(Span),
18151838
/// `let` is not valid and the source environment is not important
18161839
GenericForbidden,
1840+
/// A let chain with the `||` operator
1841+
NotSupportedOr(Span),
1842+
/// A let chain with invalid parentheses
1843+
///
1844+
/// For exemple, `let 1 = 1 && (expr && expr)` is allowed
1845+
/// but `(let 1 = 1 && (let 1 = 1 && (let 1 = 1))) && let a = 1` is not
1846+
NotSupportedParentheses(Span),
18171847
}

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

+102
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,67 @@ use std::ops::Range;
2525

2626
fn main() {}
2727

28+
fn _if() {
29+
if (let 0 = 1) {}
30+
//~^ ERROR `let` expressions are not supported here
31+
32+
if (((let 0 = 1))) {}
33+
//~^ ERROR `let` expressions are not supported here
34+
35+
if (let 0 = 1) && true {}
36+
//~^ ERROR `let` expressions are not supported here
37+
38+
if true && (let 0 = 1) {}
39+
//~^ ERROR `let` expressions are not supported here
40+
41+
if (let 0 = 1) && (let 0 = 1) {}
42+
//~^ ERROR `let` expressions are not supported here
43+
//~| ERROR `let` expressions are not supported here
44+
45+
if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
46+
//~^ ERROR `let` expressions are not supported here
47+
//~| ERROR `let` expressions are not supported here
48+
//~| ERROR `let` expressions are not supported here
49+
}
50+
51+
fn _while() {
52+
while (let 0 = 1) {}
53+
//~^ ERROR `let` expressions are not supported here
54+
55+
while (((let 0 = 1))) {}
56+
//~^ ERROR `let` expressions are not supported here
57+
58+
while (let 0 = 1) && true {}
59+
//~^ ERROR `let` expressions are not supported here
60+
61+
while true && (let 0 = 1) {}
62+
//~^ ERROR `let` expressions are not supported here
63+
64+
while (let 0 = 1) && (let 0 = 1) {}
65+
//~^ ERROR `let` expressions are not supported here
66+
//~| ERROR `let` expressions are not supported here
67+
68+
while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
69+
//~^ ERROR `let` expressions are not supported here
70+
//~| ERROR `let` expressions are not supported here
71+
//~| ERROR `let` expressions are not supported here
72+
}
73+
74+
fn _macros() {
75+
macro_rules! use_expr {
76+
($e:expr) => {
77+
if $e {}
78+
while $e {}
79+
}
80+
}
81+
use_expr!((let 0 = 1 && 0 == 0));
82+
//~^ ERROR `let` expressions are not supported here
83+
//~| ERROR `let` expressions are not supported here
84+
use_expr!((let 0 = 1));
85+
//~^ ERROR `let` expressions are not supported here
86+
//~| ERROR `let` expressions are not supported here
87+
}
88+
2889
fn nested_within_if_expr() {
2990
if &let 0 = 0 {} //~ ERROR `let` expressions are not supported here
3091
//~^ ERROR mismatched types
@@ -234,3 +295,44 @@ fn inside_const_generic_arguments() {
234295
//~| ERROR expressions must be enclosed in braces
235296
>::O == 5 {}
236297
}
298+
299+
fn with_parenthesis() {
300+
let opt = Some(Some(1i32));
301+
302+
if (let Some(a) = opt && true) {
303+
//~^ ERROR `let` expressions are not supported here
304+
}
305+
306+
if (let Some(a) = opt) && true {
307+
//~^ ERROR `let` expressions are not supported here
308+
}
309+
if (let Some(a) = opt) && (let Some(b) = a) {
310+
//~^ ERROR `let` expressions are not supported here
311+
//~| ERROR `let` expressions are not supported here
312+
}
313+
if let Some(a) = opt && (true && true) {
314+
}
315+
316+
if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
317+
//~^ ERROR `let` expressions are not supported here
318+
//~| ERROR `let` expressions are not supported here
319+
}
320+
if (let Some(a) = opt && (let Some(b) = a)) && true {
321+
//~^ ERROR `let` expressions are not supported here
322+
//~| ERROR `let` expressions are not supported here
323+
}
324+
if (let Some(a) = opt && (true)) && true {
325+
//~^ ERROR `let` expressions are not supported here
326+
}
327+
328+
if (true && (true)) && let Some(a) = opt {
329+
}
330+
if (true) && let Some(a) = opt {
331+
}
332+
if true && let Some(a) = opt {
333+
}
334+
335+
let fun = || true;
336+
if let true = (true && fun()) && (true) {
337+
}
338+
}

0 commit comments

Comments
 (0)