Skip to content
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

# [0.13.0] — 2026-07-22

- Update `syn` to 3.0 (#72)

# [0.12.0] — 2026-06-10

- Add cargo-deny check (#67)
Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ edition = "2024"

[package]
name = "impl-tools"
version = "0.12.0"
version = "0.13.0"
description = "Helper macros: autoimpl"
keywords = ["proc-macro", "macro", "derive", "trait", "procedural"]
categories = ["development-tools::procedural-macro-helpers"]
Expand All @@ -36,10 +36,10 @@ version = "3.0.2"
default-features = false

[dependencies.syn]
version = "2.0.0"
version = "3.0.2"

[dependencies.impl-tools-lib]
version = "0.12.0"
version = "0.13.0"
path = "lib"

[dev-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "impl-tools-lib"
version = "0.12.0"
version = "0.13.0"
description = "Helper macros: autoimpl"
keywords = ["derive", "trait"]
readme = "README.md"
Expand All @@ -24,7 +24,7 @@ version = "3.0.2"
default-features = false

[dependencies.syn]
version = "2.0.0"
version = "3.0.2"
# We need 'extra-traits' for equality testing
# We need 'full' for parsing macros within macro arguments
features = ["extra-traits", "full", "visit", "visit-mut"]
Expand Down
37 changes: 30 additions & 7 deletions lib/src/anon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

//! The `impl_anon!` macro

use crate::IdentFormatter;
use crate::fields::{Field, Fields, FieldsNamed, FieldsUnnamed, StructStyle};
use crate::scope::{Scope, ScopeItem};
use crate::{IdentFormatter, error_on_attrs};
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, TokenStreamExt, quote};
use syn::token::{Brace, Colon, Comma, Eq, Paren, Semi};
Expand Down Expand Up @@ -113,13 +113,20 @@ impl Anon {
};

let ty: Type = match field.ty {
Type::ImplTrait(syn::TypeImplTrait { impl_token, bounds }) => {
Type::ImplTrait(syn::TypeImplTrait {
attrs,
impl_token,
bounds,
}) => {
error_on_attrs(&attrs);

let span = quote! { #impl_token #bounds }.span();
let ty = Ident::new(&ty_name, span);

self.generics.params.push(parse_quote! { #ty: #bounds });

Type::Path(TypePath {
attrs: vec![],
qself: None,
path: ty.into(),
})
Expand All @@ -129,6 +136,7 @@ impl Anon {
self.generics.params.push(parse_quote! { #ty });

Type::Path(TypePath {
attrs: vec![],
qself: None,
path: ty.into(),
})
Expand All @@ -150,7 +158,12 @@ impl Anon {
impl<'a> syn::visit_mut::VisitMut for ReplaceInfers<'a> {
fn visit_type_mut(&mut self, node: &mut Type) {
let (span, bounds) = match node {
Type::ImplTrait(syn::TypeImplTrait { impl_token, bounds }) => {
Type::ImplTrait(syn::TypeImplTrait {
attrs,
impl_token,
bounds,
}) => {
error_on_attrs(attrs);
(impl_token.span, std::mem::take(bounds))
}
Type::Infer(infer) => (infer.span(), Punctuated::new()),
Expand All @@ -167,11 +180,11 @@ impl Anon {
ident: ident.clone(),
colon_token: Some(Default::default()),
bounds,
eq_token: None,
default: None,
}));

*node = Type::Path(TypePath {
attrs: vec![],
qself: None,
path: ident.into(),
});
Expand Down Expand Up @@ -331,8 +344,14 @@ mod parsing {
while let Type::Group(ty) = first_ty {
first_ty = *ty.elem;
}
if let Type::Path(TypePath { qself: None, path }) = first_ty {
trait_ = Some((None, path, for_token));
if let Type::Path(TypePath {
attrs,
qself: None,
path,
}) = first_ty
{
error_on_attrs(&attrs);
trait_ = Some((path, for_token));
} else {
unreachable!();
}
Expand All @@ -350,6 +369,7 @@ mod parsing {
if self_ty != parse_quote! { Self } {
if let Some(ident) = in_ident {
if !matches!(self_ty, Type::Path(TypePath {
attrs: _,
qself: None,
path: syn::Path {
leading_colon: None,
Expand Down Expand Up @@ -379,9 +399,12 @@ mod parsing {
items.push(content.parse()?);
}

let mut modifiers = syn::ImplModifiers::default();
modifiers.defaultness = defaultness;

Ok(ItemImpl {
attrs,
defaultness,
modifiers,
unsafety,
impl_token,
generics,
Expand Down
20 changes: 12 additions & 8 deletions lib/src/autoimpl/for_deref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ use crate::utils::propagate_attr_to_impl;
use proc_macro_error3::{emit_call_site_error, emit_call_site_warning, emit_error};
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, TokenStreamExt, quote};
use syn::parse_quote;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::token::{Comma, Eq, PathSep};
use syn::{FnArg, Ident, Item, Member, Pat, Token, TraitItem, Type, TypePath, parse_quote};
use syn::{FnArg, Ident, Item, Member, Pat, ReceiverKind, Token, TraitItem, Type, TypePath};

mod kw {
syn::custom_keyword!(using);
Expand Down Expand Up @@ -68,8 +69,11 @@ mod parsing {
if let WherePredicate::Type(pred) = pred {
for bound in &pred.bounds {
if matches!(bound, TypeParamBound::TraitSubst(_)) {
if let Type::Path(TypePath { qself: None, path }) =
&pred.bounded_ty
if let Type::Path(TypePath {
attrs: _,
qself: None,
path,
}) = &pred.bounded_ty
{
if let Some(ident) = path.get_ident() {
definitive = Some(ident.clone());
Expand Down Expand Up @@ -242,7 +246,7 @@ impl ForDeref {
if self.using.is_none() {
bound = bound.max(match item.sig.inputs.first() {
Some(FnArg::Receiver(rec)) => {
if rec.reference.is_some() {
if matches!(rec.kind, ReceiverKind::Reference(_, _, _)) {
Bound::Deref(rec.mutability.is_some())
} else {
emit_call_site_error!(
Expand Down Expand Up @@ -273,11 +277,11 @@ impl ForDeref {
}
}
if let Some(member) = self.using.as_ref() {
if let Some((r, _)) = arg.reference {
if let ReceiverKind::Reference(r, _, mutability) = arg.kind {
r.to_tokens(&mut toks);
}
if let Some(m) = arg.mutability {
m.to_tokens(&mut toks);
if let Some(m) = mutability {
m.to_tokens(&mut toks);
}
}
let self_ = &arg.self_token;
toks.append_all(quote! { #self_ . #member });
Expand Down
16 changes: 6 additions & 10 deletions lib/src/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,8 @@ pub struct TypeParam {
pub colon_token: Option<Token![:]>,
/// List of type bounds
pub bounds: Punctuated<TypeParamBound, Token![+]>,
/// `=`
pub eq_token: Option<Token![=]>,
/// Optional default value
pub default: Option<Type>,
pub default: Option<(Token![=], Type)>,
}

/// A trait or lifetime used as a bound on a type parameter.
Expand Down Expand Up @@ -169,7 +167,6 @@ mod parsing {
ident: input.call(Ident::parse_any)?,
colon_token: None,
bounds: Punctuated::new(),
eq_token: None,
default: None,
}));
} else {
Expand Down Expand Up @@ -217,8 +214,8 @@ mod parsing {
}

let eq_token: Option<Token![=]> = input.parse()?;
let default = if eq_token.is_some() {
Some(input.parse::<Type>()?)
let default = if let Some(tok) = eq_token {
Some((tok, input.parse::<Type>()?))
} else {
None
};
Expand All @@ -228,7 +225,6 @@ mod parsing {
ident,
colon_token,
bounds,
eq_token,
default,
})
}
Expand Down Expand Up @@ -278,6 +274,7 @@ mod parsing {
fn parse(input: ParseStream) -> Result<Self> {
if input.peek(Lifetime) && input.peek2(Token![:]) {
Ok(WherePredicate::Lifetime(PredicateLifetime {
attrs: vec![],
lifetime: input.parse()?,
colon_token: input.parse()?,
bounds: {
Expand Down Expand Up @@ -403,8 +400,8 @@ mod printing_subst {
self.colon_token.unwrap_or_default().to_tokens(tokens);
self.bounds.to_tokens_subst(tokens, subst);
}
if let Some(default) = &self.default {
self.eq_token.unwrap_or_default().to_tokens(tokens);
if let Some((tok, default)) = &self.default {
tok.to_tokens(tokens);
default.to_tokens(tokens);
}
}
Expand Down Expand Up @@ -477,7 +474,6 @@ fn map_generic_param(param: &syn::GenericParam) -> GenericParam {
.pairs()
.map(|pair| map_pair(pair, map_type_param_bound)),
),
eq_token: ty.eq_token,
default: ty.default.clone(),
}),
syn::GenericParam::Lifetime(lt) => GenericParam::Lifetime(lt.clone()),
Expand Down
9 changes: 9 additions & 0 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,12 @@ impl SimplePath {
}
}
}

// Attributes are supported in additional positions in syn v3.0.
// For now we reject attributes in these positions.
fn error_on_attrs(attrs: &[syn::Attribute]) {
use proc_macro_error3::emit_error;
for attr in attrs.iter() {
emit_error!(attr, "attributes are not (yet) supported in this context");
}
}
19 changes: 15 additions & 4 deletions lib/src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ impl Scope {

mod parsing {
use super::*;
use crate::error_on_attrs;
use crate::fields::parsing::data_struct;
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
Expand Down Expand Up @@ -388,8 +389,14 @@ mod parsing {
while let Type::Group(ty) = first_ty {
first_ty = *ty.elem;
}
if let Type::Path(TypePath { qself: None, path }) = first_ty {
trait_ = Some((None, path, for_token));
if let Type::Path(TypePath {
attrs,
qself: None,
path,
}) = first_ty
{
error_on_attrs(&attrs);
trait_ = Some((path, for_token));
} else {
unreachable!();
}
Expand All @@ -406,11 +413,12 @@ mod parsing {

if self_ty != parse_quote! { Self }
&& !matches!(self_ty, Type::Path(TypePath {
attrs: _,
qself: None,
path: Path {
leading_colon: None,
ref segments,
}
},
}) if segments.len() == 1 && segments.first().unwrap().ident == *in_ident)
{
return Err(Error::new(
Expand All @@ -431,9 +439,12 @@ mod parsing {
items.push(content.parse()?);
}

let mut modifiers = syn::ImplModifiers::default();
modifiers.defaultness = defaultness;

Ok(ItemImpl {
attrs,
defaultness,
modifiers,
unsafety,
impl_token,
generics,
Expand Down
10 changes: 5 additions & 5 deletions lib/src/split_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl SplitImpl {
items.push(ImplItem::Const(syn::ImplItemConst {
attrs: copy_non_doc_attrs(&item.attrs),
vis: syn::Visibility::Inherited,
defaultness: None,
modifiers: syn::ConstModifiers::default(),
const_token: item.const_token,
ident: item.ident.clone(),
generics: item.generics.clone(),
Expand All @@ -86,7 +86,7 @@ impl SplitImpl {
items.push(ImplItem::Fn(syn::ImplItemFn {
attrs: copy_non_doc_attrs(&item.attrs),
vis: syn::Visibility::Inherited,
defaultness: None,
modifiers: syn::FnModifiers::default(),
sig: item.sig.clone(),
block,
}));
Expand All @@ -102,7 +102,7 @@ impl SplitImpl {
items.push(ImplItem::Type(syn::ImplItemType {
attrs: copy_non_doc_attrs(&item.attrs),
vis: syn::Visibility::Inherited,
defaultness: None,
modifiers: syn::TypeModifiers::default(),
type_token: item.type_token,
ident: item.ident.clone(),
generics: item.generics.clone(),
Expand All @@ -124,11 +124,11 @@ impl SplitImpl {

let impl_ = syn::ItemImpl {
attrs,
defaultness: None,
modifiers: syn::ImplModifiers::default(),
unsafety: None,
impl_token: Default::default(),
generics,
trait_: Some((None, path, self.for_)),
trait_: Some((path, self.for_)),
self_ty: self.target,
brace_token: Default::default(),
items,
Expand Down
2 changes: 0 additions & 2 deletions lib/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,10 @@ pub fn extend_generics(generics: &mut Generics, gen2: &Generics) {
for param in &mut generics.params {
match param {
GenericParam::Type(p) => {
p.eq_token = None;
p.default = None;
}
GenericParam::Lifetime(_) => (),
GenericParam::Const(p) => {
p.eq_token = None;
p.default = None;
}
}
Expand Down
Loading