From 9ba7631819dd9d94673254de39ba66a2a4b82f4c Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 18 May 2026 09:18:00 +0800 Subject: [PATCH 1/2] minor: parse field expr keyword field to field-expr node This makes the completion more coherent and helpful for climbing trees Example --- ```rust const _=x.ref ``` **Before this PR** ``` CONST CONST_KW "const" WHITESPACE " " UNDERSCORE "_" EQ "=" FIELD_EXPR PATH_EXPR PATH PATH_SEGMENT NAME_REF IDENT "x" DOT "." ERROR REF_KW "ref" ERROR SEMICOLON ";" ``` **After this PR** ```rust CONST CONST_KW "const" WHITESPACE " " UNDERSCORE "_" EQ "=" FIELD_EXPR PATH_EXPR PATH PATH_SEGMENT NAME_REF IDENT "x" DOT "." ERROR REF_KW "ref" SEMICOLON ";" ``` --- crates/parser/src/grammar/expressions.rs | 8 +++ crates/parser/test_data/generated/runner.rs | 4 ++ .../inline/err/field_expr_keyword_field.rast | 70 +++++++++++++++++++ .../inline/err/field_expr_keyword_field.rs | 5 ++ 4 files changed, 87 insertions(+) create mode 100644 crates/parser/test_data/parser/inline/err/field_expr_keyword_field.rast create mode 100644 crates/parser/test_data/parser/inline/err/field_expr_keyword_field.rs diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs index 3f341c2ab846..abad1f07d683 100644 --- a/crates/parser/src/grammar/expressions.rs +++ b/crates/parser/src/grammar/expressions.rs @@ -597,6 +597,14 @@ fn field_expr( } (false, m) => Ok(m.complete(p, FIELD_EXPR)), }; + } else if !p.at(EOF) && p.current().is_keyword(p.current_edition()) { + // test_err field_expr_keyword_field + // fn foo() { + // x.ref; + // f(x.ref); + // f(2.ref); + // } + p.err_and_bump("expected field name or number"); } else { p.error("expected field name or number"); } diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs index ccf8b89be74a..5e3c94387248 100644 --- a/crates/parser/test_data/generated/runner.rs +++ b/crates/parser/test_data/generated/runner.rs @@ -827,6 +827,10 @@ mod err { #[test] fn empty_segment() { run_and_expect_errors("test_data/parser/inline/err/empty_segment.rs"); } #[test] + fn field_expr_keyword_field() { + run_and_expect_errors("test_data/parser/inline/err/field_expr_keyword_field.rs"); + } + #[test] fn fn_pointer_type_missing_fn() { run_and_expect_errors("test_data/parser/inline/err/fn_pointer_type_missing_fn.rs"); } diff --git a/crates/parser/test_data/parser/inline/err/field_expr_keyword_field.rast b/crates/parser/test_data/parser/inline/err/field_expr_keyword_field.rast new file mode 100644 index 000000000000..a55119d192e1 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/field_expr_keyword_field.rast @@ -0,0 +1,70 @@ +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 + FIELD_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + DOT "." + ERROR + REF_KW "ref" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "f" + ARG_LIST + L_PAREN "(" + FIELD_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + DOT "." + ERROR + REF_KW "ref" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "f" + ARG_LIST + L_PAREN "(" + FIELD_EXPR + LITERAL + INT_NUMBER "2" + DOT "." + ERROR + REF_KW "ref" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 17: expected field name or number +error 30: expected field name or number +error 44: expected field name or number diff --git a/crates/parser/test_data/parser/inline/err/field_expr_keyword_field.rs b/crates/parser/test_data/parser/inline/err/field_expr_keyword_field.rs new file mode 100644 index 000000000000..1064c03e2e54 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/field_expr_keyword_field.rs @@ -0,0 +1,5 @@ +fn foo() { + x.ref; + f(x.ref); + f(2.ref); +} From 39d001b4f2f538fcfde306fc8599763cd7b11133 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 18 May 2026 10:08:27 +0800 Subject: [PATCH 2/2] Add a expected type test for incomplete ast --- crates/ide-completion/src/context/tests.rs | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs index 94d904932ac5..e02dd7d43eec 100644 --- a/crates/ide-completion/src/context/tests.rs +++ b/crates/ide-completion/src/context/tests.rs @@ -810,3 +810,39 @@ fn foo() { expect![[r#"ty: bool, name: ?"#]], ); } + +#[test] +fn expected_type_some_incomplete_ast() { + check_expected_type_and_name( + r#" +fn foo(x: u32) { + foo(y.$0) +} +"#, + expect!["ty: u32, name: x"], + ); + check_expected_type_and_name( + r#" +fn foo(x: u32) { + foo(y.in$0) +} +"#, + expect!["ty: u32, name: x"], + ); + check_expected_type_and_name( + r#" +fn foo(x: u32) { + foo(y::$0) +} +"#, + expect!["ty: u32, name: x"], + ); + check_expected_type_and_name( + r#" +fn foo(x: u32) { + foo(crate::$0) +} +"#, + expect!["ty: u32, name: x"], + ); +}