about summary refs log tree commit diff
diff options
context:
space:
mode:
authorChayim Refael Friedman <chayimfr@gmail.com>2025-04-21 03:43:15 +0300
committerChayim Refael Friedman <chayimfr@gmail.com>2025-04-21 03:43:15 +0300
commit5e544ed9d4e1e04a864736221f4f7112fa71713f (patch)
treee26c4df504a4cbc04cbb64517542e2f4c2c6157a
parent42de0a525b7747bb8e901a39b90c2bf529883ffd (diff)
downloadrust-5e544ed9d4e1e04a864736221f4f7112fa71713f.tar.gz
rust-5e544ed9d4e1e04a864736221f4f7112fa71713f.zip
Allow wrapping `builtin#offset_of` fields argument in parentheses
This is necessary to correctly handle nested fields (`foo.bar`), see the comments in the code for explanation.
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs12
-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/offset_of_parens.rast42
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/offset_of_parens.rs3
4 files changed, 61 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
index 407320e1d08..c66afed91c5 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
@@ -258,6 +258,15 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
         p.expect(T!['(']);
         type_(p);
         p.expect(T![,]);
+        // Due to our incomplete handling of macro groups, especially
+        // those with empty delimiters, we wrap `expr` fragments in
+        // parentheses sometimes. Since `offset_of` is a macro, and takes
+        // `expr`, the field names could be wrapped in parentheses.
+        let wrapped_in_parens = p.eat(T!['(']);
+        // test offset_of_parens
+        // fn foo() {
+        //     builtin#offset_of(Foo, (bar.baz.0));
+        // }
         while !p.at(EOF) && !p.at(T![')']) {
             name_ref_mod_path_or_index(p);
             if !p.at(T![')']) {
@@ -265,6 +274,9 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
             }
         }
         p.expect(T![')']);
+        if wrapped_in_parens {
+            p.expect(T![')']);
+        }
         Some(m.complete(p, OFFSET_OF_EXPR))
     } else if p.at_contextual_kw(T![format_args]) {
         p.bump_remap(T![format_args]);
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 2ea29345edd..a8fe61e7af5 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
@@ -416,6 +416,10 @@ mod ok {
         run_and_expect_no_errors("test_data/parser/inline/ok/nocontentexpr_after_item.rs");
     }
     #[test]
+    fn offset_of_parens() {
+        run_and_expect_no_errors("test_data/parser/inline/ok/offset_of_parens.rs");
+    }
+    #[test]
     fn or_pattern() { run_and_expect_no_errors("test_data/parser/inline/ok/or_pattern.rs"); }
     #[test]
     fn param_list() { run_and_expect_no_errors("test_data/parser/inline/ok/param_list.rs"); }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/offset_of_parens.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/offset_of_parens.rast
new file mode 100644
index 00000000000..4e23455cfcc
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/offset_of_parens.rast
@@ -0,0 +1,42 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          OFFSET_OF_EXPR
+            BUILTIN_KW "builtin"
+            POUND "#"
+            OFFSET_OF_KW "offset_of"
+            L_PAREN "("
+            PATH_TYPE
+              PATH
+                PATH_SEGMENT
+                  NAME_REF
+                    IDENT "Foo"
+            COMMA ","
+            WHITESPACE " "
+            L_PAREN "("
+            NAME_REF
+              IDENT "bar"
+            DOT "."
+            NAME_REF
+              IDENT "baz"
+            DOT "."
+            NAME_REF
+              INT_NUMBER "0"
+            R_PAREN ")"
+            R_PAREN ")"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/offset_of_parens.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/offset_of_parens.rs
new file mode 100644
index 00000000000..a797d5c8206
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/offset_of_parens.rs
@@ -0,0 +1,3 @@
+fn foo() {
+    builtin#offset_of(Foo, (bar.baz.0));
+}