about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2024-12-04 06:27:43 +0100
committerLukas Wirth <lukastw97@gmail.com>2024-12-04 06:45:59 +0100
commit05aac8cd69ff15c6353dab1c27ad47e9def2d96f (patch)
tree5cefbdc47dc6ece010bba5165d70b2ee9fda451d
parentb747197a8278c90f0ff198ccf8a17335f53f9022 (diff)
downloadrust-05aac8cd69ff15c6353dab1c27ad47e9def2d96f.tar.gz
rust-05aac8cd69ff15c6353dab1c27ad47e9def2d96f.zip
Better parser recovery for incomplete attributes
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs10
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs19
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/parser.rs11
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0004_use_path_bad_segment.rast5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0048_double_fish.rast10
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/meta_recovery.rast86
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/meta_recovery.rs6
8 files changed, 130 insertions, 19 deletions
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
index 82e4d661488..ccb556b2cca 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
@@ -36,6 +36,14 @@ fn attr(p: &mut Parser<'_>, inner: bool) {
     attr.complete(p, ATTR);
 }
 
+// test_err meta_recovery
+// #![]
+// #![p = ]
+// #![p::]
+// #![p:: =]
+// #![unsafe]
+// #![unsafe =]
+
 // test metas
 // #![simple_ident]
 // #![simple::path]
@@ -63,7 +71,7 @@ pub(super) fn meta(p: &mut Parser<'_>) {
     if is_unsafe {
         p.expect(T!['(']);
     }
-    paths::use_path(p);
+    paths::attr_path(p);
 
     match p.current() {
         T![=] => {
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
index 09db921803f..057a691ef03 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
@@ -19,6 +19,10 @@ pub(super) fn use_path(p: &mut Parser<'_>) {
     path(p, Mode::Use);
 }
 
+pub(super) fn attr_path(p: &mut Parser<'_>) {
+    path(p, Mode::Attr);
+}
+
 pub(crate) fn type_path(p: &mut Parser<'_>) {
     path(p, Mode::Type);
 }
@@ -37,6 +41,7 @@ pub(crate) fn type_path_for_qualifier(
 #[derive(Clone, Copy, Eq, PartialEq)]
 enum Mode {
     Use,
+    Attr,
     Type,
     Expr,
 }
@@ -93,12 +98,7 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
             p.error("expected `::`");
         }
     } else {
-        let empty = if first {
-            p.eat(T![::]);
-            false
-        } else {
-            true
-        };
+        let mut empty = if first { !p.eat(T![::]) } else { true };
         match p.current() {
             IDENT => {
                 name_ref(p);
@@ -114,10 +114,13 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
             _ => {
                 let recover_set = match mode {
                     Mode::Use => items::ITEM_RECOVERY_SET,
+                    Mode::Attr => {
+                        items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![']'], T![=], T![#]]))
+                    }
                     Mode::Type => TYPE_PATH_SEGMENT_RECOVERY_SET,
                     Mode::Expr => EXPR_PATH_SEGMENT_RECOVERY_SET,
                 };
-                p.err_recover("expected identifier", recover_set);
+                empty &= p.err_recover("expected identifier", recover_set);
                 if empty {
                     // test_err empty_segment
                     // use crate::;
@@ -132,7 +135,7 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
 
 fn opt_path_type_args(p: &mut Parser<'_>, mode: Mode) {
     match mode {
-        Mode::Use => {}
+        Mode::Use | Mode::Attr => {}
         Mode::Type => {
             // test typepathfn_with_coloncolon
             // type F = Start::(Middle) -> (Middle)::End;
diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs
index f6b3783d1ca..8078532e0bb 100644
--- a/src/tools/rust-analyzer/crates/parser/src/parser.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs
@@ -258,22 +258,25 @@ impl<'t> Parser<'t> {
         self.err_recover(message, TokenSet::EMPTY);
     }
 
-    /// Create an error node and consume the next token.
-    pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) {
+    /// Create an error node and consume the next token unless it is in the recovery set.
+    ///
+    /// Returns true if recovery kicked in.
+    pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) -> bool {
         if matches!(self.current(), T!['{'] | T!['}']) {
             self.error(message);
-            return;
+            return true;
         }
 
         if self.at_ts(recovery) {
             self.error(message);
-            return;
+            return true;
         }
 
         let m = self.start();
         self.error(message);
         self.bump_any();
         m.complete(self, ERROR);
+        false
     }
 
     fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
index 62b381b6688..3db8b51a4ba 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
@@ -772,6 +772,8 @@ mod err {
         run_and_expect_errors("test_data/parser/inline/err/match_arms_recovery.rs");
     }
     #[test]
+    fn meta_recovery() { run_and_expect_errors("test_data/parser/inline/err/meta_recovery.rs"); }
+    #[test]
     fn method_call_missing_argument_list() {
         run_and_expect_errors("test_data/parser/inline/err/method_call_missing_argument_list.rs");
     }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0004_use_path_bad_segment.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0004_use_path_bad_segment.rast
index 44e192a5fcb..cf455934e91 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0004_use_path_bad_segment.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0004_use_path_bad_segment.rast
@@ -9,7 +9,8 @@ SOURCE_FILE
             NAME_REF
               IDENT "foo"
         COLON2 "::"
-        ERROR
-          INT_NUMBER "92"
+        PATH_SEGMENT
+          ERROR
+            INT_NUMBER "92"
     SEMICOLON ";"
 error 9: expected identifier
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0048_double_fish.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0048_double_fish.rast
index 207a5c24dff..7ef1eb98fcd 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0048_double_fish.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0048_double_fish.rast
@@ -39,8 +39,9 @@ SOURCE_FILE
                                         IDENT "lol"
                               R_ANGLE ">"
                         COLON2 "::"
-                        ERROR
-                          L_ANGLE "<"
+                        PATH_SEGMENT
+                          ERROR
+                            L_ANGLE "<"
                   TYPE_ARG
                     PATH_TYPE
                       PATH
@@ -91,8 +92,9 @@ SOURCE_FILE
                               IDENT "lol"
                     R_ANGLE ">"
               COLON2 "::"
-              ERROR
-                L_ANGLE "<"
+              PATH_SEGMENT
+                ERROR
+                  L_ANGLE "<"
         EXPR_STMT
           BIN_EXPR
             PATH_EXPR
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/meta_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/meta_recovery.rast
new file mode 100644
index 00000000000..c4bec849e7b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/meta_recovery.rast
@@ -0,0 +1,86 @@
+SOURCE_FILE
+  ATTR
+    POUND "#"
+    BANG "!"
+    L_BRACK "["
+    META
+      PATH
+    R_BRACK "]"
+  WHITESPACE "\n"
+  ATTR
+    POUND "#"
+    BANG "!"
+    L_BRACK "["
+    META
+      PATH
+        PATH_SEGMENT
+          NAME_REF
+            IDENT "p"
+      WHITESPACE " "
+      EQ "="
+    WHITESPACE " "
+    R_BRACK "]"
+  WHITESPACE "\n"
+  ATTR
+    POUND "#"
+    BANG "!"
+    L_BRACK "["
+    META
+      PATH
+        PATH
+          PATH_SEGMENT
+            NAME_REF
+              IDENT "p"
+        COLON2 "::"
+    R_BRACK "]"
+  WHITESPACE "\n"
+  ATTR
+    POUND "#"
+    BANG "!"
+    L_BRACK "["
+    META
+      PATH
+        PATH
+          PATH_SEGMENT
+            NAME_REF
+              IDENT "p"
+        COLON2 "::"
+      WHITESPACE " "
+      EQ "="
+    R_BRACK "]"
+  WHITESPACE "\n"
+  ATTR
+    POUND "#"
+    BANG "!"
+    L_BRACK "["
+    META
+      UNSAFE_KW "unsafe"
+      PATH
+    R_BRACK "]"
+  WHITESPACE "\n"
+  ATTR
+    POUND "#"
+    BANG "!"
+    L_BRACK "["
+    META
+      UNSAFE_KW "unsafe"
+      WHITESPACE " "
+      PATH
+      EQ "="
+    R_BRACK "]"
+  WHITESPACE "\n"
+error 3: expected identifier
+error 11: expected expression
+error 11: expected expression
+error 20: expected identifier
+error 28: expected identifier
+error 30: expected expression
+error 30: expected expression
+error 41: expected L_PAREN
+error 41: expected identifier
+error 41: expected R_PAREN
+error 52: expected L_PAREN
+error 53: expected identifier
+error 54: expected expression
+error 54: expected expression
+error 54: expected R_PAREN
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/meta_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/meta_recovery.rs
new file mode 100644
index 00000000000..51d30adf8b4
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/meta_recovery.rs
@@ -0,0 +1,6 @@
+#![]
+#![p = ]
+#![p::]
+#![p:: =]
+#![unsafe]
+#![unsafe =]