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) fn path(p: &mut Parser<'_>) { 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 @@ 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) fn mod_contents(p: &mut Parser<'_>, stop_on_r_curly: bool) { 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 @@ fn return_type_syntax_in_path() { 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; }