Skip to content

Commit 055ab13

Browse files
committed
Use an explicit visitor to remove lifetimes from type
The previous code was the equivalent of a visitor using a recursive calling strategy. The new implementation uses the syn::visit_mut infrastructure to do the same thing in-place. It also no longer allocates discriminately. Signed-off-by: Marcel Müller <neikos@neikos.email>
1 parent 4b2408e commit 055ab13

File tree

2 files changed

+33
-41
lines changed

2 files changed

+33
-41
lines changed

crates/proc_macros/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ proc-macro = true
1616

1717
[dependencies]
1818
proc-macro2 = "1.0.36"
19-
syn = { version = "2.0.79", features = ["full"] }
19+
syn = { version = "2.0.79", features = ["full", "visit-mut"] }
2020
quote = "1.0.15"
2121
darling = "0.20.10"
2222
regex = "1.5"

crates/proc_macros/src/util.rs

Lines changed: 32 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use darling::ToTokens;
2-
use syn::punctuated::Punctuated;
2+
use syn::visit_mut::VisitMut;
33
use syn::{Attribute, Expr, GenericArgument, ItemImpl, Lit, Meta, PathArguments, Type, TypePath};
44

55
/// From a let of attributes to an item, extracts the ones that are documentation, as strings.
@@ -27,53 +27,45 @@ pub fn extract_doc_lines(attributes: &[Attribute]) -> Vec<String> {
2727
docs
2828
}
2929

30-
/// Ugly, incomplete function to purge `'a` from a `Generic<'a, T>`.
31-
pub fn purge_lifetimes_from_type(the_type: &Type) -> Type {
32-
let mut rval = the_type.clone();
33-
34-
match &mut rval {
35-
Type::Path(x) => {
36-
for p in &mut x.path.segments {
37-
let mut still_has_parameter = false;
38-
39-
match &mut p.arguments {
40-
PathArguments::None => {}
41-
PathArguments::AngleBracketed(angled_args) => {
42-
let mut p = Punctuated::new();
30+
struct LifetimeRemover;
4331

44-
for generic_arg in &mut angled_args.args {
45-
match generic_arg {
46-
GenericArgument::Lifetime(_) => {}
47-
GenericArgument::Type(x) => {
48-
let x = purge_lifetimes_from_type(x);
49-
p.push(GenericArgument::Type(x));
50-
}
51-
GenericArgument::Constraint(x) => p.push(GenericArgument::Constraint(x.clone())),
52-
GenericArgument::Const(x) => p.push(GenericArgument::Const(x.clone())),
53-
_ => {}
54-
}
32+
impl syn::visit_mut::VisitMut for LifetimeRemover {
33+
fn visit_path_segment_mut(&mut self, path_segment: &mut syn::PathSegment) {
34+
match &mut path_segment.arguments {
35+
PathArguments::None => {}
36+
PathArguments::AngleBracketed(angled_args) => {
37+
let punctuated = std::mem::take(&mut angled_args.args);
38+
angled_args.args = punctuated
39+
.into_iter()
40+
.filter_map(|mut arg| match arg {
41+
GenericArgument::Lifetime(_) => None,
42+
_ => {
43+
self.visit_generic_argument_mut(&mut arg);
44+
Some(arg)
5545
}
46+
})
47+
.collect();
5648

57-
still_has_parameter = !p.is_empty();
58-
angled_args.args = p;
59-
}
60-
PathArguments::Parenthesized(_) => {}
61-
}
62-
63-
if !still_has_parameter {
64-
p.arguments = PathArguments::None;
49+
if angled_args.args.empty_or_trailing() {
50+
path_segment.arguments = PathArguments::None;
6551
}
6652
}
53+
PathArguments::Parenthesized(_) => path_segment.arguments = PathArguments::None,
6754
}
68-
Type::Reference(x) => {
69-
x.lifetime = None;
70-
x.elem = Box::new(purge_lifetimes_from_type(&x.elem))
71-
}
72-
Type::Ptr(x) => x.elem = Box::new(purge_lifetimes_from_type(&x.elem)),
73-
Type::Group(x) => x.elem = Box::new(purge_lifetimes_from_type(&x.elem)),
74-
_ => {}
7555
}
7656

57+
fn visit_type_reference_mut(&mut self, type_reference: &mut syn::TypeReference) {
58+
type_reference.lifetime = None;
59+
self.visit_type_mut(&mut type_reference.elem);
60+
}
61+
}
62+
63+
/// Ugly, incomplete function to purge `'a` from a `Generic<'a, T>`.
64+
pub fn purge_lifetimes_from_type(the_type: &Type) -> Type {
65+
let mut rval = the_type.clone();
66+
67+
LifetimeRemover.visit_type_mut(&mut rval);
68+
7769
rval
7870
}
7971

0 commit comments

Comments
 (0)