Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse safe and explicitly unsafe extern items #1768

Merged
merged 1 commit into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 70 additions & 29 deletions src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -952,7 +952,8 @@ pub(crate) mod parsing {
let vis: Visibility = ahead.parse()?;

let lookahead = ahead.lookahead1();
let mut item = if lookahead.peek(Token![fn]) || peek_signature(&ahead) {
let allow_safe = false;
let mut item = if lookahead.peek(Token![fn]) || peek_signature(&ahead, allow_safe) {
let vis: Visibility = input.parse()?;
let sig: Signature = input.parse()?;
if input.peek(Token![;]) {
Expand Down Expand Up @@ -1493,34 +1494,52 @@ pub(crate) mod parsing {
}
}

fn peek_signature(input: ParseStream) -> bool {
fn peek_signature(input: ParseStream, allow_safe: bool) -> bool {
let fork = input.fork();
fork.parse::<Option<Token![const]>>().is_ok()
&& fork.parse::<Option<Token![async]>>().is_ok()
&& fork.parse::<Option<Token![unsafe]>>().is_ok()
&& ((allow_safe
&& token::parsing::peek_keyword(fork.cursor(), "safe")
&& token::parsing::keyword(&fork, "safe").is_ok())
|| fork.parse::<Option<Token![unsafe]>>().is_ok())
&& fork.parse::<Option<Abi>>().is_ok()
&& fork.peek(Token![fn])
}

#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
impl Parse for Signature {
fn parse(input: ParseStream) -> Result<Self> {
let constness: Option<Token![const]> = input.parse()?;
let asyncness: Option<Token![async]> = input.parse()?;
let unsafety: Option<Token![unsafe]> = input.parse()?;
let abi: Option<Abi> = input.parse()?;
let fn_token: Token![fn] = input.parse()?;
let ident: Ident = input.parse()?;
let mut generics: Generics = input.parse()?;
let allow_safe = false;
parse_signature(input, allow_safe).map(Option::unwrap)
}
}

let content;
let paren_token = parenthesized!(content in input);
let (inputs, variadic) = parse_fn_args(&content)?;
fn parse_signature(input: ParseStream, allow_safe: bool) -> Result<Option<Signature>> {
let constness: Option<Token![const]> = input.parse()?;
let asyncness: Option<Token![async]> = input.parse()?;
let unsafety: Option<Token![unsafe]> = input.parse()?;
let safe = allow_safe
&& unsafety.is_none()
&& token::parsing::peek_keyword(input.cursor(), "safe");
if safe {
token::parsing::keyword(input, "safe")?;
}
let abi: Option<Abi> = input.parse()?;
let fn_token: Token![fn] = input.parse()?;
let ident: Ident = input.parse()?;
let mut generics: Generics = input.parse()?;

let output: ReturnType = input.parse()?;
generics.where_clause = input.parse()?;
let content;
let paren_token = parenthesized!(content in input);
let (inputs, variadic) = parse_fn_args(&content)?;

Ok(Signature {
let output: ReturnType = input.parse()?;
generics.where_clause = input.parse()?;

Ok(if safe {
None
} else {
Some(Signature {
constness,
asyncness,
unsafety,
Expand All @@ -1533,7 +1552,7 @@ pub(crate) mod parsing {
variadic,
output,
})
}
})
}

#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
Expand Down Expand Up @@ -1829,35 +1848,55 @@ pub(crate) mod parsing {
let vis: Visibility = ahead.parse()?;

let lookahead = ahead.lookahead1();
let mut item = if lookahead.peek(Token![fn]) || peek_signature(&ahead) {
let allow_safe = true;
let mut item = if lookahead.peek(Token![fn]) || peek_signature(&ahead, allow_safe) {
let vis: Visibility = input.parse()?;
let sig: Signature = input.parse()?;
if input.peek(token::Brace) {
let sig = parse_signature(input, allow_safe)?;
let has_safe = sig.is_none();
let has_body = input.peek(token::Brace);
let semi_token: Option<Token![;]> = if has_body {
let content;
braced!(content in input);
content.call(Attribute::parse_inner)?;
content.call(Block::parse_within)?;

None
} else {
Some(input.parse()?)
};
if has_safe || has_body {
Ok(ForeignItem::Verbatim(verbatim::between(&begin, input)))
} else {
Ok(ForeignItem::Fn(ForeignItemFn {
attrs: Vec::new(),
vis,
sig,
semi_token: input.parse()?,
sig: sig.unwrap(),
semi_token: semi_token.unwrap(),
}))
}
} else if lookahead.peek(Token![static]) {
} else if lookahead.peek(Token![static])
|| ((ahead.peek(Token![unsafe])
|| token::parsing::peek_keyword(ahead.cursor(), "safe"))
&& ahead.peek2(Token![static]))
{
let vis = input.parse()?;
let unsafety: Option<Token![unsafe]> = input.parse()?;
let safe =
unsafety.is_none() && token::parsing::peek_keyword(input.cursor(), "safe");
if safe {
token::parsing::keyword(input, "safe")?;
}
let static_token = input.parse()?;
let mutability = input.parse()?;
let ident = input.parse()?;
let colon_token = input.parse()?;
let ty = input.parse()?;
if input.peek(Token![=]) {
let has_value = input.peek(Token![=]);
if has_value {
input.parse::<Token![=]>()?;
input.parse::<Expr>()?;
input.parse::<Token![;]>()?;
}
let semi_token: Token![;] = input.parse()?;
if safe || has_value {
Ok(ForeignItem::Verbatim(verbatim::between(&begin, input)))
} else {
Ok(ForeignItem::Static(ForeignItemStatic {
Expand All @@ -1868,7 +1907,7 @@ pub(crate) mod parsing {
ident,
colon_token,
ty,
semi_token: input.parse()?,
semi_token,
}))
}
} else if lookahead.peek(Token![type]) {
Expand Down Expand Up @@ -2300,7 +2339,8 @@ pub(crate) mod parsing {
let ahead = input.fork();

let lookahead = ahead.lookahead1();
let mut item = if lookahead.peek(Token![fn]) || peek_signature(&ahead) {
let allow_safe = false;
let mut item = if lookahead.peek(Token![fn]) || peek_signature(&ahead, allow_safe) {
input.parse().map(TraitItem::Fn)
} else if lookahead.peek(Token![const]) {
let const_token: Token![const] = ahead.parse()?;
Expand Down Expand Up @@ -2645,7 +2685,8 @@ pub(crate) mod parsing {
None
};

let mut item = if lookahead.peek(Token![fn]) || peek_signature(&ahead) {
let allow_safe = false;
let mut item = if lookahead.peek(Token![fn]) || peek_signature(&ahead, allow_safe) {
let allow_omitted_body = true;
if let Some(item) = parse_impl_item_fn(input, allow_omitted_body)? {
Ok(ImplItem::Fn(item))
Expand Down
7 changes: 1 addition & 6 deletions tests/repo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@ const REVISION: &str = "5069856495870486134dd2ca0b0e2516308c5c2a";

#[rustfmt::skip]
static EXCLUDE_FILES: &[&str] = &[
// TODO: `unsafe static`, `safe fn`
// https://github.com/dtolnay/syn/issues/1675
"src/tools/rustfmt/tests/target/unsafe_extern_blocks.rs",
"tests/rustdoc/unsafe-extern-blocks.rs",
"tests/ui/rust-2024/unsafe-extern-blocks/safe-items.rs",

// TODO: non-lifetime binders: `where for<'a, T> &'a Struct<T>: Trait`
// https://github.com/dtolnay/syn/issues/1435
"src/tools/rustfmt/tests/source/issue_5721.rs",
Expand Down Expand Up @@ -301,6 +295,7 @@ static EXCLUDE_FILES: &[&str] = &[
"src/tools/rustfmt/tests/target/configs/spaces_around_ranges/false.rs",
"src/tools/rustfmt/tests/target/configs/spaces_around_ranges/true.rs",
"src/tools/rustfmt/tests/target/type.rs",
"src/tools/rustfmt/tests/target/unsafe_extern_blocks.rs",
"tests/run-make/translation/test.rs",
"tests/ui/generics/issue-94432-garbage-ice.rs",

Expand Down
Loading