Skip to content

Commit

Permalink
Auto merge of #15961 - ohno418:top-level-let-stmt, r=Veykril
Browse files Browse the repository at this point in the history
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 #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.
  • Loading branch information
bors committed Dec 1, 2023
2 parents c9d189d + e076192 commit 57e9024
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 42 deletions.
10 changes: 10 additions & 0 deletions crates/parser/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,16 @@ fn error_block(p: &mut Parser<'_>, message: &str) {
m.complete(p, ERROR);
}

// test_err top_level_let
// let ref foo: fn() = 1 + 3;
fn error_let_stmt(p: &mut Parser<'_>, message: &str) {
assert!(p.at(T![let]));
let m = p.start();
p.error(message);
expressions::let_stmt(p, expressions::Semicolon::Optional);
m.complete(p, ERROR);
}

/// The `parser` passed this is required to at least consume one token if it returns `true`.
/// If the `parser` returns false, parsing will stop.
fn delimited(
Expand Down
84 changes: 42 additions & 42 deletions crates/parser/src/grammar/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) {
attributes::outer_attrs(p);

if p.at(T![let]) {
let_stmt(p, m, semicolon);
let_stmt(p, semicolon);
m.complete(p, LET_STMT);
return;
}

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

// test let_stmt
// fn f() { let x: i32 = 92; }
fn let_stmt(p: &mut Parser<'_>, m: Marker, with_semi: Semicolon) {
p.bump(T![let]);
patterns::pattern(p);
if p.at(T![:]) {
// test let_stmt_ascription
// fn f() { let x: i32; }
types::ascription(p);
}
// test let_stmt
// fn f() { let x: i32 = 92; }
pub(super) fn let_stmt(p: &mut Parser<'_>, with_semi: Semicolon) {
p.bump(T![let]);
patterns::pattern(p);
if p.at(T![:]) {
// test let_stmt_ascription
// fn f() { let x: i32; }
types::ascription(p);
}

let mut expr_after_eq: Option<CompletedMarker> = None;
if p.eat(T![=]) {
// test let_stmt_init
// fn f() { let x = 92; }
expr_after_eq = expressions::expr(p);
}
let mut expr_after_eq: Option<CompletedMarker> = None;
if p.eat(T![=]) {
// test let_stmt_init
// fn f() { let x = 92; }
expr_after_eq = expressions::expr(p);
}

if p.at(T![else]) {
// test_err let_else_right_curly_brace
// fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
if let Some(expr) = expr_after_eq {
if BlockLike::is_blocklike(expr.kind()) {
p.error(
"right curly brace `}` before `else` in a `let...else` statement not allowed",
)
}
if p.at(T![else]) {
// test_err let_else_right_curly_brace
// fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
if let Some(expr) = expr_after_eq {
if BlockLike::is_blocklike(expr.kind()) {
p.error(
"right curly brace `}` before `else` in a `let...else` statement not allowed",
)
}

// test let_else
// fn f() { let Some(x) = opt else { return }; }
let m = p.start();
p.bump(T![else]);
block_expr(p);
m.complete(p, LET_ELSE);
}

match with_semi {
Semicolon::Forbidden => (),
Semicolon::Optional => {
p.eat(T![;]);
}
Semicolon::Required => {
p.expect(T![;]);
}
// test let_else
// fn f() { let Some(x) = opt else { return }; }
let m = p.start();
p.bump(T![else]);
block_expr(p);
m.complete(p, LET_ELSE);
}

match with_semi {
Semicolon::Forbidden => (),
Semicolon::Optional => {
p.eat(T![;]);
}
Semicolon::Required => {
p.expect(T![;]);
}
m.complete(p, LET_STMT);
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/parser/src/grammar/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ pub(super) fn item_or_macro(p: &mut Parser<'_>, stop_on_r_curly: bool) {
e.complete(p, ERROR);
}
EOF | T!['}'] => p.error("expected an item"),
T![let] => error_let_stmt(p, "expected an item"),
_ => p.err_and_bump("expected an item"),
}
}
Expand Down
30 changes: 30 additions & 0 deletions crates/parser/test_data/parser/inline/err/0024_top_level_let.rast
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
SOURCE_FILE
ERROR
LET_KW "let"
WHITESPACE " "
IDENT_PAT
REF_KW "ref"
WHITESPACE " "
NAME
IDENT "foo"
COLON ":"
WHITESPACE " "
FN_PTR_TYPE
FN_KW "fn"
PARAM_LIST
L_PAREN "("
R_PAREN ")"
WHITESPACE " "
EQ "="
WHITESPACE " "
BIN_EXPR
LITERAL
INT_NUMBER "1"
WHITESPACE " "
PLUS "+"
WHITESPACE " "
LITERAL
INT_NUMBER "3"
SEMICOLON ";"
WHITESPACE "\n"
error 0: expected an item
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let ref foo: fn() = 1 + 3;

0 comments on commit 57e9024

Please sign in to comment.