about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYutaro Ohno <yutaro.ono.418@gmail.com>2023-11-25 15:01:51 +0900
committerYutaro Ohno <yutaro.ono.418@gmail.com>2023-12-01 10:37:21 +0900
commite076192dd8d14dd20d8e187af9a9ffca92e21e88 (patch)
tree479f982ffe4af84544a29481bf5bf18d6b65a3ee
parentc9d189d1375e59a6c9b4d62fdede94ade001f6ee (diff)
downloadrust-e076192dd8d14dd20d8e187af9a9ffca92e21e88.tar.gz
rust-e076192dd8d14dd20d8e187af9a9ffca92e21e88.zip
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.
-rw-r--r--crates/parser/src/grammar.rs10
-rw-r--r--crates/parser/src/grammar/expressions.rs84
-rw-r--r--crates/parser/src/grammar/items.rs1
-rw-r--r--crates/parser/test_data/parser/inline/err/0024_top_level_let.rast30
-rw-r--r--crates/parser/test_data/parser/inline/err/0024_top_level_let.rs1
5 files changed, 84 insertions, 42 deletions
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 6a2a9adce15..19da297b58c 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -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(
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index 8542d6b86b1..e346ece2f94 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -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;
     }
 
@@ -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);
     }
 }
 
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index 4e850b1f74d..34fd3420f1c 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -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"),
     }
 }
diff --git a/crates/parser/test_data/parser/inline/err/0024_top_level_let.rast b/crates/parser/test_data/parser/inline/err/0024_top_level_let.rast
new file mode 100644
index 00000000000..5ddef5f3f03
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/0024_top_level_let.rast
@@ -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
diff --git a/crates/parser/test_data/parser/inline/err/0024_top_level_let.rs b/crates/parser/test_data/parser/inline/err/0024_top_level_let.rs
new file mode 100644
index 00000000000..3d3e7dd56c7
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/0024_top_level_let.rs
@@ -0,0 +1 @@
+let ref foo: fn() = 1 + 3;