Skip to content

Commit

Permalink
Parse safe and explicitly unsafe extern items
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Oct 23, 2024
1 parent dfc9372 commit 9ab57e1
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 35 deletions.
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

0 comments on commit 9ab57e1

Please sign in to comment.