Skip to content

Commit a9d97b6

Browse files
committed
Auto merge of #14248 - XFFXFF:let_else_right_curly_brace, r=Veykril
fix: show diagnostic for } token followed by else in let else statement fix #14221 My thinking is to check if the `expr` after `=` is block like when parse `let ... lese` , and if so, emit error.
2 parents ecc32c2 + 6e97527 commit a9d97b6

16 files changed

+430
-12
lines changed

crates/parser/src/grammar.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,10 @@ impl BlockLike {
198198
fn is_block(self) -> bool {
199199
self == BlockLike::Block
200200
}
201+
202+
fn is_blocklike(kind: SyntaxKind) -> bool {
203+
matches!(kind, BLOCK_EXPR | IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR)
204+
}
201205
}
202206

203207
const VISIBILITY_FIRST: TokenSet = TokenSet::new(&[T![pub], T![crate]]);

crates/parser/src/grammar/attributes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub(super) fn meta(p: &mut Parser<'_>) {
4343
match p.current() {
4444
T![=] => {
4545
p.bump(T![=]);
46-
if !expressions::expr(p) {
46+
if expressions::expr(p).is_none() {
4747
p.error("expected expression");
4848
}
4949
}

crates/parser/src/grammar/expressions.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ pub(super) enum Semicolon {
1616

1717
const EXPR_FIRST: TokenSet = LHS_FIRST;
1818

19-
pub(super) fn expr(p: &mut Parser<'_>) -> bool {
19+
pub(super) fn expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
2020
let r = Restrictions { forbid_structs: false, prefer_stmt: false };
21-
expr_bp(p, None, r, 1).is_some()
21+
expr_bp(p, None, r, 1).map(|(m, _)| m)
2222
}
2323

2424
pub(super) fn expr_stmt(
@@ -120,16 +120,27 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) {
120120
// fn f() { let x: i32; }
121121
types::ascription(p);
122122
}
123+
124+
let mut expr_after_eq: Option<CompletedMarker> = None;
123125
if p.eat(T![=]) {
124126
// test let_stmt_init
125127
// fn f() { let x = 92; }
126-
expressions::expr(p);
128+
expr_after_eq = expressions::expr(p);
127129
}
128130

129131
if p.at(T![else]) {
132+
// test_err let_else_right_curly_brace
133+
// fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
134+
if let Some(expr) = expr_after_eq {
135+
if BlockLike::is_blocklike(expr.kind()) {
136+
p.error(
137+
"right curly brace `}` before `else` in a `let...else` statement not allowed",
138+
)
139+
}
140+
}
141+
130142
// test let_else
131143
// fn f() { let Some(x) = opt else { return }; }
132-
133144
let m = p.start();
134145
p.bump(T![else]);
135146
block_expr(p);
@@ -578,7 +589,14 @@ fn arg_list(p: &mut Parser<'_>) {
578589
// fn main() {
579590
// foo(#[attr] 92)
580591
// }
581-
delimited(p, T!['('], T![')'], T![,], EXPR_FIRST.union(ATTRIBUTE_FIRST), expr);
592+
delimited(
593+
p,
594+
T!['('],
595+
T![')'],
596+
T![,],
597+
EXPR_FIRST.union(ATTRIBUTE_FIRST),
598+
|p: &mut Parser<'_>| expr(p).is_some(),
599+
);
582600
m.complete(p, ARG_LIST);
583601
}
584602

crates/parser/src/grammar/expressions/atom.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,8 @@ pub(super) fn atom_expr(
163163
return None;
164164
}
165165
};
166-
let blocklike = match done.kind() {
167-
IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR => BlockLike::Block,
168-
_ => BlockLike::NotBlock,
169-
};
166+
let blocklike =
167+
if BlockLike::is_blocklike(done.kind()) { BlockLike::Block } else { BlockLike::NotBlock };
170168
Some((done, blocklike))
171169
}
172170

@@ -188,7 +186,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker {
188186

189187
// test tuple_attrs
190188
// const A: (i64, i64) = (1, #[cfg(test)] 2);
191-
if !expr(p) {
189+
if expr(p).is_none() {
192190
break;
193191
}
194192

@@ -221,7 +219,7 @@ fn array_expr(p: &mut Parser<'_>) -> CompletedMarker {
221219

222220
// test array_attrs
223221
// const A: &[i64] = &[1, #[cfg(test)] 2];
224-
if !expr(p) {
222+
if expr(p).is_none() {
225223
break;
226224
}
227225

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
SOURCE_FILE
2+
FN
3+
FN_KW "fn"
4+
WHITESPACE " "
5+
NAME
6+
IDENT "f"
7+
PARAM_LIST
8+
L_PAREN "("
9+
R_PAREN ")"
10+
WHITESPACE " "
11+
BLOCK_EXPR
12+
STMT_LIST
13+
L_CURLY "{"
14+
WHITESPACE "\n "
15+
LET_STMT
16+
LET_KW "let"
17+
WHITESPACE " "
18+
WILDCARD_PAT
19+
UNDERSCORE "_"
20+
WHITESPACE " "
21+
EQ "="
22+
WHITESPACE " "
23+
FOR_EXPR
24+
FOR_KW "for"
25+
WHITESPACE " "
26+
WILDCARD_PAT
27+
UNDERSCORE "_"
28+
WHITESPACE " "
29+
IN_KW "in"
30+
WHITESPACE " "
31+
RANGE_EXPR
32+
LITERAL
33+
INT_NUMBER "0"
34+
DOT2 ".."
35+
LITERAL
36+
INT_NUMBER "10"
37+
WHITESPACE " "
38+
BLOCK_EXPR
39+
STMT_LIST
40+
L_CURLY "{"
41+
WHITESPACE "\n "
42+
R_CURLY "}"
43+
WHITESPACE " "
44+
LET_ELSE
45+
ELSE_KW "else"
46+
WHITESPACE " "
47+
BLOCK_EXPR
48+
STMT_LIST
49+
L_CURLY "{"
50+
WHITESPACE "\n "
51+
RETURN_EXPR
52+
RETURN_KW "return"
53+
WHITESPACE "\n "
54+
R_CURLY "}"
55+
SEMICOLON ";"
56+
WHITESPACE "\n"
57+
R_CURLY "}"
58+
error 43: right curly brace `}` before `else` in a `let...else` statement not allowed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn f() {
2+
let _ = for _ in 0..10 {
3+
} else {
4+
return
5+
};
6+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
SOURCE_FILE
2+
FN
3+
FN_KW "fn"
4+
WHITESPACE " "
5+
NAME
6+
IDENT "f"
7+
PARAM_LIST
8+
L_PAREN "("
9+
R_PAREN ")"
10+
WHITESPACE " "
11+
BLOCK_EXPR
12+
STMT_LIST
13+
L_CURLY "{"
14+
WHITESPACE "\n "
15+
LET_STMT
16+
LET_KW "let"
17+
WHITESPACE " "
18+
WILDCARD_PAT
19+
UNDERSCORE "_"
20+
WHITESPACE " "
21+
EQ "="
22+
WHITESPACE " "
23+
LOOP_EXPR
24+
LOOP_KW "loop"
25+
WHITESPACE " "
26+
BLOCK_EXPR
27+
STMT_LIST
28+
L_CURLY "{"
29+
WHITESPACE "\n "
30+
R_CURLY "}"
31+
WHITESPACE " "
32+
LET_ELSE
33+
ELSE_KW "else"
34+
WHITESPACE " "
35+
BLOCK_EXPR
36+
STMT_LIST
37+
L_CURLY "{"
38+
WHITESPACE "\n "
39+
RETURN_EXPR
40+
RETURN_KW "return"
41+
WHITESPACE "\n "
42+
R_CURLY "}"
43+
SEMICOLON ";"
44+
WHITESPACE "\n"
45+
R_CURLY "}"
46+
error 33: right curly brace `}` before `else` in a `let...else` statement not allowed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn f() {
2+
let _ = loop {
3+
} else {
4+
return
5+
};
6+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
SOURCE_FILE
2+
FN
3+
FN_KW "fn"
4+
WHITESPACE " "
5+
NAME
6+
IDENT "f"
7+
PARAM_LIST
8+
L_PAREN "("
9+
R_PAREN ")"
10+
WHITESPACE " "
11+
BLOCK_EXPR
12+
STMT_LIST
13+
L_CURLY "{"
14+
WHITESPACE "\n "
15+
LET_STMT
16+
LET_KW "let"
17+
WHITESPACE " "
18+
WILDCARD_PAT
19+
UNDERSCORE "_"
20+
WHITESPACE " "
21+
EQ "="
22+
WHITESPACE " "
23+
MATCH_EXPR
24+
MATCH_KW "match"
25+
WHITESPACE " "
26+
CALL_EXPR
27+
PATH_EXPR
28+
PATH
29+
PATH_SEGMENT
30+
NAME_REF
31+
IDENT "Some"
32+
ARG_LIST
33+
L_PAREN "("
34+
LITERAL
35+
INT_NUMBER "1"
36+
R_PAREN ")"
37+
WHITESPACE " "
38+
MATCH_ARM_LIST
39+
L_CURLY "{"
40+
WHITESPACE "\n "
41+
MATCH_ARM
42+
TUPLE_STRUCT_PAT
43+
PATH
44+
PATH_SEGMENT
45+
NAME_REF
46+
IDENT "Some"
47+
L_PAREN "("
48+
WILDCARD_PAT
49+
UNDERSCORE "_"
50+
R_PAREN ")"
51+
WHITESPACE " "
52+
FAT_ARROW "=>"
53+
WHITESPACE " "
54+
LITERAL
55+
INT_NUMBER "1"
56+
COMMA ","
57+
WHITESPACE "\n "
58+
MATCH_ARM
59+
IDENT_PAT
60+
NAME
61+
IDENT "None"
62+
WHITESPACE " "
63+
FAT_ARROW "=>"
64+
WHITESPACE " "
65+
LITERAL
66+
INT_NUMBER "2"
67+
COMMA ","
68+
WHITESPACE "\n "
69+
R_CURLY "}"
70+
WHITESPACE " "
71+
LET_ELSE
72+
ELSE_KW "else"
73+
WHITESPACE " "
74+
BLOCK_EXPR
75+
STMT_LIST
76+
L_CURLY "{"
77+
WHITESPACE "\n "
78+
RETURN_EXPR
79+
RETURN_KW "return"
80+
WHITESPACE "\n "
81+
R_CURLY "}"
82+
SEMICOLON ";"
83+
WHITESPACE "\n"
84+
R_CURLY "}"
85+
error 83: right curly brace `}` before `else` in a `let...else` statement not allowed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn f() {
2+
let _ = match Some(1) {
3+
Some(_) => 1,
4+
None => 2,
5+
} else {
6+
return
7+
};
8+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
SOURCE_FILE
2+
FN
3+
FN_KW "fn"
4+
WHITESPACE " "
5+
NAME
6+
IDENT "f"
7+
PARAM_LIST
8+
L_PAREN "("
9+
R_PAREN ")"
10+
WHITESPACE " "
11+
BLOCK_EXPR
12+
STMT_LIST
13+
L_CURLY "{"
14+
WHITESPACE "\n "
15+
LET_STMT
16+
LET_KW "let"
17+
WHITESPACE " "
18+
WILDCARD_PAT
19+
UNDERSCORE "_"
20+
WHITESPACE " "
21+
EQ "="
22+
WHITESPACE " "
23+
WHILE_EXPR
24+
WHILE_KW "while"
25+
WHITESPACE " "
26+
LITERAL
27+
TRUE_KW "true"
28+
WHITESPACE " "
29+
BLOCK_EXPR
30+
STMT_LIST
31+
L_CURLY "{"
32+
WHITESPACE "\n "
33+
R_CURLY "}"
34+
WHITESPACE " "
35+
LET_ELSE
36+
ELSE_KW "else"
37+
WHITESPACE " "
38+
BLOCK_EXPR
39+
STMT_LIST
40+
L_CURLY "{"
41+
WHITESPACE "\n "
42+
RETURN_EXPR
43+
RETURN_KW "return"
44+
WHITESPACE "\n "
45+
R_CURLY "}"
46+
SEMICOLON ";"
47+
WHITESPACE "\n"
48+
R_CURLY "}"
49+
error 39: right curly brace `}` before `else` in a `let...else` statement not allowed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn f() {
2+
let _ = while true {
3+
} else {
4+
return
5+
};
6+
}

0 commit comments

Comments
 (0)