use quote::{ToTokens, TokenStreamExt};
use syn::{
braced, bracketed,
parse::{discouraged::Speculative, Parse, ParseStream},
punctuated::Punctuated,
token, Abi, AttrStyle, Attribute, Generics, Ident, ImplRestriction, Result,
Signature, Token, TraitItem, TypeParamBound, Visibility,
};
#[derive(Clone)]
pub(crate) struct ItemFnForSignature {
pub attrs: Vec<Attribute>,
pub vis: Visibility,
pub sig: Signature,
pub _block: proc_macro2::TokenStream,
}
impl Parse for ItemFnForSignature {
fn parse(input: ParseStream) -> syn::parse::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let vis: Visibility = input.parse()?;
let sig: Signature = input.parse()?;
let block = input.parse()?;
Ok(ItemFnForSignature { attrs, vis, sig, _block: block })
}
}
#[derive(Clone)]
pub(crate) struct ItemTraitPartParsed {
pub attrs: Vec<Attribute>,
pub vis: Visibility,
pub unsafety: Option<Token![unsafe]>,
pub auto_token: Option<Token![auto]>,
#[allow(dead_code)]
pub restriction: Option<ImplRestriction>,
pub trait_token: Token![trait],
pub ident: Ident,
pub generics: Generics,
pub colon_token: Option<Token![:]>,
pub supertraits: Punctuated<TypeParamBound, Token![+]>,
pub brace_token: token::Brace,
pub items: Vec<TraitItemPartParsed>,
}
impl Parse for ItemTraitPartParsed {
fn parse(input: ParseStream) -> syn::parse::Result<Self> {
let outer_attrs = input.call(Attribute::parse_outer)?;
let vis: Visibility = input.parse()?;
let unsafety: Option<Token![unsafe]> = input.parse()?;
let auto_token: Option<Token![auto]> = input.parse()?;
let trait_token: Token![trait] = input.parse()?;
let ident: Ident = input.parse()?;
let generics: Generics = input.parse()?;
parse_rest_of_trait(
input,
outer_attrs,
vis,
unsafety,
auto_token,
trait_token,
ident,
generics,
)
}
}
#[allow(clippy::too_many_arguments)]
fn parse_rest_of_trait(
input: ParseStream,
mut attrs: Vec<Attribute>,
vis: Visibility,
unsafety: Option<Token![unsafe]>,
auto_token: Option<Token![auto]>,
trait_token: Token![trait],
ident: Ident,
mut generics: Generics,
) -> Result<ItemTraitPartParsed> {
let colon_token: Option<Token![:]> = input.parse()?;
let mut supertraits = Punctuated::new();
if colon_token.is_some() {
loop {
if input.peek(Token![where]) || input.peek(token::Brace) {
break;
}
supertraits.push_value(input.parse()?);
if input.peek(Token![where]) || input.peek(token::Brace) {
break;
}
supertraits.push_punct(input.parse()?);
}
}
generics.where_clause = input.parse()?;
let content;
let brace_token = braced!(content in input);
parse_inner(&content, &mut attrs)?;
let mut items = Vec::new();
while !content.is_empty() {
items.push(content.parse()?);
}
Ok(ItemTraitPartParsed {
attrs,
vis,
unsafety,
auto_token,
restriction: None,
trait_token,
ident,
generics,
colon_token,
supertraits,
brace_token,
items,
})
}
impl ToTokens for ItemTraitPartParsed {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let Self {
attrs,
vis,
unsafety,
auto_token,
restriction: _,
trait_token,
ident,
generics,
colon_token,
supertraits,
brace_token,
items,
} = self;
tokens.append_all(attrs.outer());
vis.to_tokens(tokens);
unsafety.to_tokens(tokens);
auto_token.to_tokens(tokens);
trait_token.to_tokens(tokens);
ident.to_tokens(tokens);
generics.to_tokens(tokens);
if !supertraits.is_empty() {
TokensOrDefault(&colon_token).to_tokens(tokens);
supertraits.to_tokens(tokens);
}
generics.where_clause.to_tokens(tokens);
brace_token.surround(tokens, |tokens| {
tokens.append_all(attrs.inner());
tokens.append_all(items);
});
}
}
#[derive(Clone)]
pub(crate) enum TraitItemPartParsed {
Fn(TraitItemFnForSignature),
Other(TraitItem),
}
impl Parse for TraitItemPartParsed {
fn parse(input: ParseStream) -> syn::parse::Result<Self> {
let begin = input.fork();
let mut attrs = input.call(Attribute::parse_outer)?;
let vis: Visibility = input.parse()?;
let defaultness: Option<Token![default]> = input.parse()?;
let other: TraitItem = match (vis, defaultness) {
(Visibility::Inherited, None) => {
let ahead = input.fork();
let lookahead = ahead.lookahead1();
if lookahead.peek(Token![fn]) || peek_signature(&ahead) {
let mut fn_item: TraitItemFnForSignature = input.parse()?;
attrs.append(&mut fn_item.attrs);
fn_item.attrs = attrs;
return Ok(Self::Fn(fn_item));
} else {
begin.parse()?
}
}
_ => {
begin.parse()?
}
};
if let TraitItem::Fn(f) = &other {
return Err(syn::Error::new_spanned(
f.sig.fn_token,
"functions should have been handled separately, \
we should never get here",
));
}
input.advance_to(&begin);
Ok(Self::Other(other))
}
}
impl ToTokens for TraitItemPartParsed {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match self {
Self::Fn(f) => f.to_tokens(tokens),
Self::Other(o) => o.to_tokens(tokens),
}
}
}
#[derive(Clone)]
pub(crate) struct TraitItemFnForSignature {
pub attrs: Vec<Attribute>,
pub sig: Signature,
pub block: Option<UnparsedBlock>,
pub semi_token: Option<Token![;]>,
}
impl Parse for TraitItemFnForSignature {
fn parse(input: ParseStream) -> Result<Self> {
let mut attrs = input.call(Attribute::parse_outer)?;
let sig: Signature = input.parse()?;
let lookahead = input.lookahead1();
let (block, semi_token) = if lookahead.peek(token::Brace) {
let content;
let brace_token = braced!(content in input);
parse_inner(&content, &mut attrs)?;
let tokens = content.parse()?;
(Some(UnparsedBlock { brace_token, tokens }), None)
} else if lookahead.peek(Token![;]) {
let semi_token: Token![;] = input.parse()?;
(Default::default(), Some(semi_token))
} else {
return Err(lookahead.error());
};
Ok(Self { attrs, sig, block, semi_token })
}
}
impl ToTokens for TraitItemFnForSignature {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.append_all(self.attrs.outer());
self.sig.to_tokens(tokens);
match &self.block {
Some(block) => {
block.to_tokens(tokens);
}
None => {
TokensOrDefault(&self.semi_token).to_tokens(tokens);
}
}
}
}
#[derive(Clone)]
pub(crate) struct UnparsedBlock {
pub brace_token: token::Brace,
pub tokens: proc_macro2::TokenStream,
}
impl ToTokens for UnparsedBlock {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
self.brace_token.surround(tokens, |tokens| {
tokens.extend(self.tokens.clone());
});
}
}
fn peek_signature(input: ParseStream) -> 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()
&& fork.parse::<Option<Abi>>().is_ok()
&& fork.peek(Token![fn])
}
fn parse_inner(input: ParseStream, attrs: &mut Vec<Attribute>) -> Result<()> {
while input.peek(Token![#]) && input.peek2(Token![!]) {
attrs.push(input.call(single_parse_inner)?);
}
Ok(())
}
fn single_parse_inner(input: ParseStream) -> Result<Attribute> {
let content;
Ok(Attribute {
pound_token: input.parse()?,
style: AttrStyle::Inner(input.parse()?),
bracket_token: bracketed!(content in input),
meta: content.parse()?,
})
}
trait FilterAttrs<'a> {
type Ret: Iterator<Item = &'a Attribute>;
fn outer(self) -> Self::Ret;
fn inner(self) -> Self::Ret;
}
impl<'a> FilterAttrs<'a> for &'a [Attribute] {
type Ret = std::iter::Filter<
std::slice::Iter<'a, Attribute>,
fn(&&Attribute) -> bool,
>;
fn outer(self) -> Self::Ret {
fn is_outer(attr: &&Attribute) -> bool {
match attr.style {
AttrStyle::Outer => true,
AttrStyle::Inner(_) => false,
}
}
self.iter().filter(is_outer)
}
fn inner(self) -> Self::Ret {
fn is_inner(attr: &&Attribute) -> bool {
match attr.style {
AttrStyle::Inner(_) => true,
AttrStyle::Outer => false,
}
}
self.iter().filter(is_inner)
}
}
struct TokensOrDefault<'a, T: 'a>(&'a Option<T>);
impl<T> ToTokens for TokensOrDefault<'_, T>
where
T: ToTokens + Default,
{
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match self.0 {
Some(t) => t.to_tokens(tokens),
None => T::default().to_tokens(tokens),
}
}
}
#[cfg(test)]
mod tests {
use quote::quote;
use syn::{Signature, Visibility};
use crate::syn_parsing::ItemFnForSignature;
#[test]
fn test_busted_function() {
let f = quote! {
fn foo(parameter: u32) -> u32 {
(void) printf("%s", "no language C here!");
return (0);
}
};
let ast: ItemFnForSignature = syn::parse2(f).unwrap();
let sig: Signature = syn::parse2(quote! {
fn foo(parameter: u32) -> u32
})
.unwrap();
assert!(ast.attrs.is_empty());
assert_eq!(ast.vis, Visibility::Inherited);
assert_eq!(ast.sig, sig);
}
}