Skip to content

Commit

Permalink
Switch to syn 2
Browse files Browse the repository at this point in the history
  • Loading branch information
glandium committed Dec 25, 2024
1 parent c7fda90 commit 0f1b32f
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 31 deletions.
2 changes: 1 addition & 1 deletion ambassador/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ readme = "README.md"
proc-macro = true

[dependencies]
syn = { version = "1.0.25", features = ["full", "extra-traits"] }
syn = { version = "2", features = ["full", "extra-traits"] }
quote = "1.0.2"
proc-macro2 = "1.0.6"
itertools = "0.10.3"
Expand Down
4 changes: 2 additions & 2 deletions ambassador/src/delegate_shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ pub(super) fn delegate_macro<I>(
// Parse the input tokens into a syntax tree
let mut delegate_attributes = attrs
.into_iter()
.filter(|attr| attr.path.is_ident("delegate"))
.map(|attr| attr.tokens)
.filter(|attr| attr.path().is_ident("delegate"))
.map(|attr| attr.meta.to_token_stream().into_iter().skip(1).collect())
.peekable();
if delegate_attributes.peek().is_none() {
return error!(
Expand Down
48 changes: 35 additions & 13 deletions ambassador/src/delegate_to_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::util;
use crate::util::{error, try_option, ReceiverType};
use itertools::Itertools;
use proc_macro::TokenStream;
use proc_macro2::{Ident, TokenStream as TokenStream2};
use proc_macro2::{Ident, TokenStream as TokenStream2, TokenTree};
use quote::{quote, ToTokens};
use std::cell::Cell;
use std::convert::{TryFrom, TryInto};
Expand All @@ -30,19 +30,18 @@ struct MethodInfo {
used: Cell<bool>, // modified when a usage is found
}

impl TryFrom<syn::ImplItemMethod> for MethodInfo {
impl TryFrom<syn::ImplItemFn> for MethodInfo {
type Error = (Ident, syn::Error);

fn try_from(method: syn::ImplItemMethod) -> std::result::Result<Self, (Ident, syn::Error)> {
fn try_from(method: syn::ImplItemFn) -> std::result::Result<Self, (Ident, syn::Error)> {
let receiver_or_err = util::receiver_type(&method.sig);
let return_span = method.sig.paren_token.span;
let mut ident = Some(method.sig.ident);
let mut add_ident = |err| (ident.take().unwrap(), err);
let receiver = receiver_or_err.map_err(&mut add_ident)?;
let ret = match method.sig.output {
ReturnType::Default => {
error!(return_span, "delegated to methods must return").map_err(&mut add_ident)?
}
ReturnType::Default => error!(return_span.open(), "delegated to methods must return")
.map_err(&mut add_ident)?,
ReturnType::Type(_, t) => *t,
};
let ret = match (ret, receiver) {
Expand Down Expand Up @@ -188,20 +187,37 @@ impl DelegateTarget {
}
}

fn replace_semi_with_block(input: TokenStream2) -> TokenStream2 {
input
.into_iter()
.map(|token| match token {
TokenTree::Punct(p) if p.as_char() == ';' => TokenTree::Group(proc_macro2::Group::new(
proc_macro2::Delimiter::Brace,
TokenStream2::new(),
)),
token => token,
})
.collect()
}

// Checks that:
// - all the items in an impl are methods
// - all the methods are _empty_ (no body, just the signature)
fn check_for_method_impls_and_extras(impl_items: &[syn::ImplItem]) -> Result<()> {
let iter = impl_items.iter().filter_map(|i| {
let mut i = i;
// We're looking for *only* empty methods (no block).
if let syn::ImplItem::Method(m) = i {
let mut _item: Option<syn::ImplItem> = None;
// syn 2 doesn't parse functions with omitted blocks as ImplItemFn.
if let syn::ImplItem::Verbatim(v) = i {
_item = Some(syn::parse2::<syn::ImplItem>(replace_semi_with_block(v.clone())).ok()?);
i = _item.as_ref().unwrap();
}
if let syn::ImplItem::Fn(m) = i {
let block = &m.block;
let empty_block = syn::parse2::<syn::ImplItemMethod>(quote! { fn foo(); })
.unwrap()
.block;

// We'll accept `{}` blocks and omitted blocks (i.e. `fn foo();`):
if block.stmts.is_empty() || block == &empty_block {
if block.stmts.is_empty() {
None
} else {
Some(syn::Error::new(
Expand Down Expand Up @@ -257,7 +273,7 @@ pub fn delegate_macro(input: TokenStream, keep_impl_block: bool) -> TokenStream
let mut input = parse_macro_input!(input as ItemImpl);
let attrs = std::mem::take(&mut input.attrs);
assert!(
attrs.iter().all(|attr| attr.path.is_ident("delegate")),
attrs.iter().all(|attr| attr.path().is_ident("delegate")),
"All attributes must be \"delegate\""
);
let keep_info = if keep_impl_block {
Expand All @@ -269,7 +285,13 @@ pub fn delegate_macro(input: TokenStream, keep_impl_block: bool) -> TokenStream
.items
.into_iter()
.filter_map(|item| match item {
syn::ImplItem::Method(method) => Some(method.try_into()),
syn::ImplItem::Fn(method) => Some(method.try_into()),
syn::ImplItem::Verbatim(v) => {
// syn 2 doesn't parse functions with omitted blocks as ImplItemFn.
syn::parse2::<syn::ImplItemFn>(replace_semi_with_block(v.clone()))
.ok()
.map(MethodInfo::try_from)
}
_ => None,
})
.partition_result();
Expand Down
22 changes: 11 additions & 11 deletions ambassador/src/register.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::util::{error, process_results, receiver_type, ReceiverType};
use itertools::Itertools;
use proc_macro2::{Delimiter, Ident, TokenStream, TokenTree};
use proc_macro2::{Ident, TokenStream, TokenTree};
use quote::{quote, ToTokens, TokenStreamExt};
use syn::spanned::Spanned;
use syn::{
AttrStyle, Attribute, ConstParam, GenericParam, ItemTrait, LifetimeDef, TraitItem,
AttrStyle, Attribute, ConstParam, GenericParam, ItemTrait, LifetimeParam, TraitItem,
TraitItemConst, TraitItemType, TypeParam, Visibility,
};

Expand Down Expand Up @@ -116,15 +116,15 @@ pub fn build_register_trait(original_item: &ItemTrait) -> TokenStream {
fn param_to_ident(param: &GenericParam) -> &Ident {
match param {
GenericParam::Type(TypeParam { ident, .. }) => ident,
GenericParam::Lifetime(LifetimeDef { lifetime, .. }) => &lifetime.ident,
GenericParam::Lifetime(LifetimeParam { lifetime, .. }) => &lifetime.ident,
GenericParam::Const(ConstParam { ident, .. }) => ident,
}
}

fn param_to_matcher(param: &GenericParam) -> TokenStream {
match param {
GenericParam::Type(TypeParam { ident, .. }) => quote!($ #ident : ty,),
GenericParam::Lifetime(LifetimeDef { lifetime, .. }) => {
GenericParam::Lifetime(LifetimeParam { lifetime, .. }) => {
let ident = &lifetime.ident;
quote!($ #ident : lifetime,)
}
Expand All @@ -135,7 +135,7 @@ fn param_to_matcher(param: &GenericParam) -> TokenStream {
fn param_to_tokens(param: &GenericParam) -> TokenStream {
match param {
GenericParam::Type(TypeParam { ident, .. }) => quote!(#ident,),
GenericParam::Lifetime(LifetimeDef { lifetime, .. }) => quote!(#lifetime,),
GenericParam::Lifetime(LifetimeParam { lifetime, .. }) => quote!(#lifetime,),
GenericParam::Const(ConstParam { ident, .. }) => quote!(#ident,),
}
}
Expand Down Expand Up @@ -219,9 +219,9 @@ fn build_method(
fn extract_cfg(attrs: &[Attribute]) -> Option<TokenStream> {
let mut iter = attrs
.iter()
.filter(|attr| attr.style == AttrStyle::Outer && attr.path.is_ident("cfg"))
.filter_map(|attr| match attr.tokens.clone().into_iter().next()? {
TokenTree::Group(x) if x.delimiter() == Delimiter::Parenthesis => Some(x.stream()),
.filter(|attr| attr.style == AttrStyle::Outer && attr.path().is_ident("cfg"))
.filter_map(|attr| match &attr.meta {
syn::Meta::List(meta_list) => Some(meta_list.tokens.clone()),
_ => None,
});
let e0 = iter.next()?;
Expand Down Expand Up @@ -267,7 +267,7 @@ fn build_trait_items(
quote! {compile_error!("trg=\"self\" is not allowed with associated types")},
)
}
TraitItem::Method(original_method) => {
TraitItem::Fn(original_method) => {
let method_sig = original_method.sig.to_token_stream();
let method_sig = replace_gen_idents(method_sig, gen_idents);
(
Expand Down Expand Up @@ -317,7 +317,7 @@ fn build_trait_items(
let attrs: &[Attribute] = match original_item {
TraitItem::Const(c) => &c.attrs,
TraitItem::Type(t) => &t.attrs,
TraitItem::Method(m) => &m.attrs,
TraitItem::Fn(m) => &m.attrs,
_ => &[],
};
if let Some(pred) = extract_cfg(attrs) {
Expand All @@ -329,7 +329,7 @@ fn build_trait_items(
}

fn build_method_invocation(
original_method: &syn::TraitItemMethod,
original_method: &syn::TraitItemFn,
field_ident: &TokenStream,
) -> TokenStream {
let method_sig = &original_method.sig;
Expand Down
8 changes: 4 additions & 4 deletions ambassador/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,11 @@ fn receiver_type_inner(r: &Receiver) -> ReceiverType {

pub(crate) fn receiver_type(sig: &syn::Signature) -> Result<ReceiverType> {
match sig.receiver() {
Some(syn::FnArg::Receiver(r)) => Ok(receiver_type_inner(r)),
Some(syn::FnArg::Typed(t)) => error!(
t.span(),
Some(r) if r.colon_token.is_none() => Ok(receiver_type_inner(r)),
Some(r) => error!(
r.span(),
"method's receiver type is not supported (must one of self, &self, or &mut self)"
),
None => error!(sig.paren_token.span, "method must have a receiver"),
None => error!(sig.paren_token.span.open(), "method must have a receiver"),
}
}

0 comments on commit 0f1b32f

Please sign in to comment.