diff --git a/src/attr.rs b/src/attr.rs index 579452bbf..2bdf96ee7 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -653,6 +653,7 @@ pub(crate) mod parsing { use crate::parse::{Parse, ParseStream}; use crate::path::Path; use crate::{mac, token}; + use proc_macro2::Ident; use std::fmt::{self, Display}; pub(crate) fn parse_inner(input: ParseStream, attrs: &mut Vec) -> Result<()> { @@ -685,7 +686,7 @@ pub(crate) mod parsing { #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for Meta { fn parse(input: ParseStream) -> Result { - let path = input.call(Path::parse_mod_style)?; + let path = parse_outermost_meta_path(input)?; parse_meta_after_path(path, input) } } @@ -693,7 +694,7 @@ pub(crate) mod parsing { #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for MetaList { fn parse(input: ParseStream) -> Result { - let path = input.call(Path::parse_mod_style)?; + let path = parse_outermost_meta_path(input)?; parse_meta_list_after_path(path, input) } } @@ -701,11 +702,22 @@ pub(crate) mod parsing { #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] impl Parse for MetaNameValue { fn parse(input: ParseStream) -> Result { - let path = input.call(Path::parse_mod_style)?; + let path = parse_outermost_meta_path(input)?; parse_meta_name_value_after_path(path, input) } } + // Unlike meta::parse_meta_path which accepts arbitrary keywords in the path, + // only the `unsafe` keyword is accepted as an attribute's outermost path. + fn parse_outermost_meta_path(input: ParseStream) -> Result { + if input.peek(Token![unsafe]) { + let unsafe_token: Token![unsafe] = input.parse()?; + Ok(Path::from(Ident::new("unsafe", unsafe_token.span))) + } else { + Path::parse_mod_style(input) + } + } + pub(crate) fn parse_meta_after_path(path: Path, input: ParseStream) -> Result { if input.peek(token::Paren) || input.peek(token::Bracket) || input.peek(token::Brace) { parse_meta_list_after_path(path, input).map(Meta::List) diff --git a/tests/repo/mod.rs b/tests/repo/mod.rs index 6c367c944..9f8a418ab 100644 --- a/tests/repo/mod.rs +++ b/tests/repo/mod.rs @@ -25,13 +25,6 @@ static EXCLUDE_FILES: &[&str] = &[ "tests/rustdoc/unsafe-extern-blocks.rs", "tests/ui/rust-2024/unsafe-extern-blocks/safe-items.rs", - // TODO: unsafe attributes: `#[unsafe(path::to)]` - // https://github.com/dtolnay/syn/issues/1710 - "src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0213_metas.rs", - "src/tools/rustfmt/tests/target/unsafe_attributes.rs", - "tests/ui/attributes/unsafe/unsafe-attributes.rs", - "tests/ui/rust-2024/unsafe-attributes/unsafe-attribute-marked.rs", - // TODO: non-lifetime binders: `where for<'a, T> &'a Struct: Trait` // https://github.com/dtolnay/syn/issues/1435 "src/tools/rustfmt/tests/source/issue_5721.rs",