about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar.rs3
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/items.rs13
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs4
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/safe_outside_of_extern.rast30
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/safe_outside_of_extern.rs1
7 files changed, 47 insertions, 8 deletions
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
index 7ae1e5f82e5..a5c031ad609 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
@@ -80,7 +80,8 @@ pub(crate) mod entry {
             paths::type_path(p);
         }
         pub(crate) fn item(p: &mut Parser<'_>) {
-            items::item_or_macro(p, true);
+            // We can set `is_in_extern=true`, because it only allows `safe fn`, and there is no ambiguity here.
+            items::item_or_macro(p, true, true);
         }
         // Parse a meta item , which excluded [], e.g : #[ MetaItem ]
         pub(crate) fn meta_item(p: &mut Parser<'_>) {
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
index 861fcedda2a..e565874a421 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
@@ -66,7 +66,7 @@ pub(super) fn stmt(p: &mut Parser<'_>, semicolon: Semicolon) {
 
     // test block_items
     // fn a() { fn b() {} }
-    let m = match items::opt_item(p, m) {
+    let m = match items::opt_item(p, m, false) {
         Ok(()) => return,
         Err(m) => m,
     };
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
index c4dce0daa5b..7d98499008d 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
@@ -20,7 +20,8 @@ use super::*;
 pub(super) fn mod_contents(p: &mut Parser<'_>, stop_on_r_curly: bool) {
     attributes::inner_attrs(p);
     while !(p.at(EOF) || (p.at(T!['}']) && stop_on_r_curly)) {
-        item_or_macro(p, stop_on_r_curly);
+        // We can set `is_in_extern=true`, because it only allows `safe fn`, and there is no ambiguity here.
+        item_or_macro(p, stop_on_r_curly, true);
     }
 }
 
@@ -41,11 +42,11 @@ pub(super) const ITEM_RECOVERY_SET: TokenSet = TokenSet::new(&[
     T![;],
 ]);
 
-pub(super) fn item_or_macro(p: &mut Parser<'_>, stop_on_r_curly: bool) {
+pub(super) fn item_or_macro(p: &mut Parser<'_>, stop_on_r_curly: bool, is_in_extern: bool) {
     let m = p.start();
     attributes::outer_attrs(p);
 
-    let m = match opt_item(p, m) {
+    let m = match opt_item(p, m, is_in_extern) {
         Ok(()) => {
             if p.at(T![;]) {
                 p.err_and_bump(
@@ -91,7 +92,7 @@ pub(super) fn item_or_macro(p: &mut Parser<'_>, stop_on_r_curly: bool) {
 }
 
 /// Try to parse an item, completing `m` in case of success.
-pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> {
+pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker, is_in_extern: bool) -> Result<(), Marker> {
     // test_err pub_expr
     // fn foo() { pub 92; }
     let has_visibility = opt_visibility(p, false);
@@ -135,7 +136,9 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> {
         has_mods = true;
     }
 
-    if p.at_contextual_kw(T![safe]) {
+    // test safe_outside_of_extern
+    // fn foo() { safe = true; }
+    if is_in_extern && p.at_contextual_kw(T![safe]) {
         p.eat_contextual_kw(T![safe]);
         has_mods = true;
     }
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs
index c215185d632..47f86ce8c6c 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs
@@ -94,7 +94,7 @@ pub(crate) fn assoc_item_list(p: &mut Parser<'_>) {
             error_block(p, "expected an item");
             continue;
         }
-        item_or_macro(p, true);
+        item_or_macro(p, true, false);
     }
     p.expect(T!['}']);
     m.complete(p, ASSOC_ITEM_LIST);
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 164d0f36f1b..5b41dd07adc 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
@@ -527,6 +527,10 @@ mod ok {
         run_and_expect_no_errors("test_data/parser/inline/ok/return_type_syntax_in_path.rs");
     }
     #[test]
+    fn safe_outside_of_extern() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/safe_outside_of_extern.rs");
+    }
+    #[test]
     fn self_param() { run_and_expect_no_errors("test_data/parser/inline/ok/self_param.rs"); }
     #[test]
     fn self_param_outer_attr() {
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/safe_outside_of_extern.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/safe_outside_of_extern.rast
new file mode 100644
index 00000000000..c9398eaa993
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/safe_outside_of_extern.rast
@@ -0,0 +1,30 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE " "
+        EXPR_STMT
+          BIN_EXPR
+            PATH_EXPR
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "safe"
+            WHITESPACE " "
+            EQ "="
+            WHITESPACE " "
+            LITERAL
+              TRUE_KW "true"
+          SEMICOLON ";"
+        WHITESPACE " "
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/safe_outside_of_extern.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/safe_outside_of_extern.rs
new file mode 100644
index 00000000000..a4d75e63ea7
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/safe_outside_of_extern.rs
@@ -0,0 +1 @@
+fn foo() { safe = true; }