From 5d3001795ac4482675473898588a67ce4ca1b13f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 5 Jun 2025 16:11:30 +0200 Subject: [PATCH] Better parser recovery for macro calls in type bound position --- crates/parser/src/grammar/generic_params.rs | 11 ++ crates/parser/src/grammar/paths.rs | 2 +- crates/parser/src/grammar/types.rs | 14 +-- crates/parser/test_data/generated/runner.rs | 4 + .../err/type_bounds_macro_call_recovery.rast | 112 ++++++++++++++++++ .../err/type_bounds_macro_call_recovery.rs | 1 + 6 files changed, 134 insertions(+), 10 deletions(-) create mode 100644 crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rast create mode 100644 crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rs diff --git a/crates/parser/src/grammar/generic_params.rs b/crates/parser/src/grammar/generic_params.rs index 9d4fdbfaf2..ea5a3bc859 100644 --- a/crates/parser/src/grammar/generic_params.rs +++ b/crates/parser/src/grammar/generic_params.rs @@ -201,6 +201,17 @@ fn type_bound(p: &mut Parser<'_>) -> bool { } if paths::is_use_path_start(p) { types::path_type_bounds(p, false); + // test_err type_bounds_macro_call_recovery + // fn foo() -> Box {} + if p.at(T![!]) { + let m = p.start(); + p.bump(T![!]); + p.error("unexpected `!` in type path, macro calls are not allowed here"); + if p.at_ts(TokenSet::new(&[T!['{'], T!['['], T!['(']])) { + items::token_tree(p); + } + m.complete(p, ERROR); + } } else { m.abandon(p); return false; diff --git a/crates/parser/src/grammar/paths.rs b/crates/parser/src/grammar/paths.rs index e628bcc056..dfe7cb57d2 100644 --- a/crates/parser/src/grammar/paths.rs +++ b/crates/parser/src/grammar/paths.rs @@ -98,7 +98,7 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) -> Option) { m.complete(p, DYN_TRAIT_TYPE); } -// test path_type -// type A = Foo; -// type B = ::Foo; -// type C = self::Foo; -// type D = super::Foo; -pub(super) fn path_type(p: &mut Parser<'_>) { - path_type_bounds(p, true); -} - // test macro_call_type // type A = foo!(); // type B = crate::foo!(); @@ -365,6 +356,11 @@ fn path_or_macro_type(p: &mut Parser<'_>, allow_bounds: bool) { } } +// test path_type +// type A = Foo; +// type B = ::Foo; +// type C = self::Foo; +// type D = super::Foo; pub(super) fn path_type_bounds(p: &mut Parser<'_>, allow_bounds: bool) { assert!(paths::is_path_start(p)); let m = p.start(); diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs index 537f6df05b..6ec4192830 100644 --- a/crates/parser/test_data/generated/runner.rs +++ b/crates/parser/test_data/generated/runner.rs @@ -876,6 +876,10 @@ mod err { run_and_expect_errors("test_data/parser/inline/err/tuple_pat_leading_comma.rs"); } #[test] + fn type_bounds_macro_call_recovery() { + run_and_expect_errors("test_data/parser/inline/err/type_bounds_macro_call_recovery.rs"); + } + #[test] fn type_in_array_recover() { run_and_expect_errors("test_data/parser/inline/err/type_in_array_recover.rs"); } diff --git a/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rast b/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rast new file mode 100644 index 0000000000..4722beb619 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rast @@ -0,0 +1,112 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + GENERIC_PARAM_LIST + L_ANGLE "<" + TYPE_PARAM + NAME + IDENT "T" + COLON ":" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "T" + ERROR + BANG "!" + TOKEN_TREE + L_BRACK "[" + R_BRACK "]" + COMMA "," + WHITESPACE " " + TYPE_PARAM + NAME + IDENT "T" + COLON ":" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "T" + ERROR + BANG "!" + COMMA "," + WHITESPACE " " + TYPE_PARAM + NAME + IDENT "T" + COLON ":" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "T" + ERROR + BANG "!" + TOKEN_TREE + L_CURLY "{" + R_CURLY "}" + R_ANGLE ">" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + RET_TYPE + THIN_ARROW "->" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Box" + GENERIC_ARG_LIST + L_ANGLE "<" + TYPE_ARG + DYN_TRAIT_TYPE + TYPE_BOUND_LIST + TYPE_BOUND + MACRO_TYPE + MACRO_CALL + PATH + PATH_SEGMENT + NAME_REF + IDENT "T" + BANG "!" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "T" + ERROR + BANG "!" + TOKEN_TREE + L_CURLY "{" + R_CURLY "}" + R_ANGLE ">" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" +error 12: unexpected `!` in type path, macro calls are not allowed here +error 21: unexpected `!` in type path, macro calls are not allowed here +error 28: unexpected `!` in type path, macro calls are not allowed here +error 43: expected `{`, `[`, `(` +error 48: unexpected `!` in type path, macro calls are not allowed here diff --git a/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rs b/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rs new file mode 100644 index 0000000000..517404fdb0 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/type_bounds_macro_call_recovery.rs @@ -0,0 +1 @@ +fn foo() -> Box {}