From 922aad6b6d22ced38024fc5d2506f2b3884f643f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 22 Oct 2025 14:37:12 +0800 Subject: [PATCH] Improve parsing of missing name in MethodCallExpr Usually, this occurs when preparing to input a method name However, once an identifier is entered, it is not reasonable for the parsing result to change from `CallExpr(FieldExpr())` to `MethodCallExpr()` Example --- ```rust fn foo() { x. () } ``` **Before this PR**: ```text 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 " CALL_EXPR FIELD_EXPR PATH_EXPR PATH PATH_SEGMENT NAME_REF IDENT "x" DOT "." WHITESPACE "\n " ARG_LIST L_PAREN "(" R_PAREN ")" WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" error 17: expected field name or number ``` **After this PR**: ```text 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 " METHOD_CALL_EXPR PATH_EXPR PATH PATH_SEGMENT NAME_REF IDENT "x" DOT "." WHITESPACE "\n " ARG_LIST L_PAREN "(" R_PAREN ")" WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" error 17: expected method name, field name or number ``` --- .../crates/parser/src/grammar/expressions.rs | 25 ++++++++++++---- .../parser/test_data/generated/runner.rs | 4 +++ .../err/postfix_dot_expr_ambiguity.rast | 29 +++++++++++++++++++ .../inline/err/postfix_dot_expr_ambiguity.rs | 4 +++ 4 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.rs 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 41fd72d8d5a2..76d26c1ecdfc 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -430,6 +430,11 @@ fn postfix_expr( // } T!['('] if allow_calls => call_expr(p, lhs), T!['['] if allow_calls => index_expr(p, lhs), + // test_err postfix_dot_expr_ambiguity + // fn foo() { + // x. + // () + // } T![.] => match postfix_dot_expr::(p, lhs) { Ok(it) => it, Err(it) => { @@ -458,6 +463,7 @@ fn postfix_dot_expr( if PATH_NAME_REF_KINDS.contains(p.nth(nth1)) && (p.nth(nth2) == T!['('] || p.nth_at(nth2, T![::])) + || p.nth(nth1) == T!['('] { return Ok(method_call_expr::(p, lhs)); } @@ -526,19 +532,26 @@ fn method_call_expr( lhs: CompletedMarker, ) -> CompletedMarker { if FLOAT_RECOVERY { - assert!(p.at_ts(PATH_NAME_REF_KINDS) && (p.nth(1) == T!['('] || p.nth_at(1, T![::]))); - } else { assert!( - p.at(T![.]) - && PATH_NAME_REF_KINDS.contains(p.nth(1)) - && (p.nth(2) == T!['('] || p.nth_at(2, T![::])) + p.at_ts(PATH_NAME_REF_KINDS) && (p.nth(1) == T!['('] || p.nth_at(1, T![::])) + || p.current() == T!['('] + ); + } else { + assert!(p.at(T![.])); + assert!( + PATH_NAME_REF_KINDS.contains(p.nth(1)) && (p.nth(2) == T!['('] || p.nth_at(2, T![::])) + || p.nth(1) == T!['('] ); } let m = lhs.precede(p); if !FLOAT_RECOVERY { p.bump(T![.]); } - name_ref_mod_path(p); + if p.at_ts(PATH_NAME_REF_KINDS) { + name_ref_mod_path(p); + } else { + p.error("expected method name, field name or number"); + } generic_args::opt_generic_arg_list_expr(p); if p.at(T!['(']) { arg_list(p); 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 cd6d433d0efa..9bdbe5633033 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 @@ -844,6 +844,10 @@ mod err { run_and_expect_errors("test_data/parser/inline/err/pointer_type_no_mutability.rs"); } #[test] + fn postfix_dot_expr_ambiguity() { + run_and_expect_errors("test_data/parser/inline/err/postfix_dot_expr_ambiguity.rs"); + } + #[test] fn precise_capturing_invalid() { run_and_expect_errors("test_data/parser/inline/err/precise_capturing_invalid.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.rast new file mode 100644 index 000000000000..4ee318de2515 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.rast @@ -0,0 +1,29 @@ +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 " + METHOD_CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + DOT "." + WHITESPACE "\n " + ARG_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 17: expected method name, field name or number diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.rs new file mode 100644 index 000000000000..c1aed3034288 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/postfix_dot_expr_ambiguity.rs @@ -0,0 +1,4 @@ +fn foo() { + x. + () +}