Skip to content

Commit 57e9024

Browse files
committed
Auto merge of rust-lang#15961 - ohno418:top-level-let-stmt, r=Veykril
Improve error handling for top-level `let` statements This commit addresses the issue of excessive and unrelated errors generated by top-level `let` statements. Now, only a single error is produced, indicating that `let` statements are invalid at the top level. --- Fixes rust-lang/rust-analyzer#14963. While I'm not really sure if handling a particular case in a special manner is appropriate, it would be good to suppress the excessive number of annoying and unrelated errors.
2 parents c9d189d + e076192 commit 57e9024

File tree

5 files changed

+84
-42
lines changed

5 files changed

+84
-42
lines changed

crates/parser/src/grammar.rs

+10
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,16 @@ fn error_block(p: &mut Parser<'_>, message: &str) {
376376
m.complete(p, ERROR);
377377
}
378378

379+
// test_err top_level_let
380+
// let ref foo: fn() = 1 + 3;
381+
fn error_let_stmt(p: &mut Parser<'_>, message: &str) {
382+
assert!(p.at(T![let]));
383+
let m = p.start();
384+
p.error(message);
385+
expressions::let_stmt(p, expressions::Semicolon::Optional);
386+
m.complete(p, ERROR);
387+
}
388+
379389
/// The `parser` passed this is required to at least consume one token if it returns `true`.
380390
/// If the `parser` returns false, parsing will stop.
381391
fn delimited(

crates/parser/src/grammar/expressions.rs

+42-42
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) {
5959
attributes::outer_attrs(p);
6060

6161
if p.at(T![let]) {
62-
let_stmt(p, m, semicolon);
62+
let_stmt(p, semicolon);
63+
m.complete(p, LET_STMT);
6364
return;
6465
}
6566

@@ -109,54 +110,53 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) {
109110
m.complete(p, EXPR_STMT);
110111
}
111112
}
113+
}
112114

113-
// test let_stmt
114-
// fn f() { let x: i32 = 92; }
115-
fn let_stmt(p: &mut Parser<'_>, m: Marker, with_semi: Semicolon) {
116-
p.bump(T![let]);
117-
patterns::pattern(p);
118-
if p.at(T![:]) {
119-
// test let_stmt_ascription
120-
// fn f() { let x: i32; }
121-
types::ascription(p);
122-
}
115+
// test let_stmt
116+
// fn f() { let x: i32 = 92; }
117+
pub(super) fn let_stmt(p: &mut Parser<'_>, with_semi: Semicolon) {
118+
p.bump(T![let]);
119+
patterns::pattern(p);
120+
if p.at(T![:]) {
121+
// test let_stmt_ascription
122+
// fn f() { let x: i32; }
123+
types::ascription(p);
124+
}
123125

124-
let mut expr_after_eq: Option<CompletedMarker> = None;
125-
if p.eat(T![=]) {
126-
// test let_stmt_init
127-
// fn f() { let x = 92; }
128-
expr_after_eq = expressions::expr(p);
129-
}
126+
let mut expr_after_eq: Option<CompletedMarker> = None;
127+
if p.eat(T![=]) {
128+
// test let_stmt_init
129+
// fn f() { let x = 92; }
130+
expr_after_eq = expressions::expr(p);
131+
}
130132

131-
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-
}
133+
if p.at(T![else]) {
134+
// test_err let_else_right_curly_brace
135+
// fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
136+
if let Some(expr) = expr_after_eq {
137+
if BlockLike::is_blocklike(expr.kind()) {
138+
p.error(
139+
"right curly brace `}` before `else` in a `let...else` statement not allowed",
140+
)
140141
}
141-
142-
// test let_else
143-
// fn f() { let Some(x) = opt else { return }; }
144-
let m = p.start();
145-
p.bump(T![else]);
146-
block_expr(p);
147-
m.complete(p, LET_ELSE);
148142
}
149143

150-
match with_semi {
151-
Semicolon::Forbidden => (),
152-
Semicolon::Optional => {
153-
p.eat(T![;]);
154-
}
155-
Semicolon::Required => {
156-
p.expect(T![;]);
157-
}
144+
// test let_else
145+
// fn f() { let Some(x) = opt else { return }; }
146+
let m = p.start();
147+
p.bump(T![else]);
148+
block_expr(p);
149+
m.complete(p, LET_ELSE);
150+
}
151+
152+
match with_semi {
153+
Semicolon::Forbidden => (),
154+
Semicolon::Optional => {
155+
p.eat(T![;]);
156+
}
157+
Semicolon::Required => {
158+
p.expect(T![;]);
158159
}
159-
m.complete(p, LET_STMT);
160160
}
161161
}
162162

crates/parser/src/grammar/items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ pub(super) fn item_or_macro(p: &mut Parser<'_>, stop_on_r_curly: bool) {
7979
e.complete(p, ERROR);
8080
}
8181
EOF | T!['}'] => p.error("expected an item"),
82+
T![let] => error_let_stmt(p, "expected an item"),
8283
_ => p.err_and_bump("expected an item"),
8384
}
8485
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
SOURCE_FILE
2+
ERROR
3+
LET_KW "let"
4+
WHITESPACE " "
5+
IDENT_PAT
6+
REF_KW "ref"
7+
WHITESPACE " "
8+
NAME
9+
IDENT "foo"
10+
COLON ":"
11+
WHITESPACE " "
12+
FN_PTR_TYPE
13+
FN_KW "fn"
14+
PARAM_LIST
15+
L_PAREN "("
16+
R_PAREN ")"
17+
WHITESPACE " "
18+
EQ "="
19+
WHITESPACE " "
20+
BIN_EXPR
21+
LITERAL
22+
INT_NUMBER "1"
23+
WHITESPACE " "
24+
PLUS "+"
25+
WHITESPACE " "
26+
LITERAL
27+
INT_NUMBER "3"
28+
SEMICOLON ";"
29+
WHITESPACE "\n"
30+
error 0: expected an item
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
let ref foo: fn() = 1 + 3;

0 commit comments

Comments
 (0)