Skip to content

Commit 491d5fb

Browse files
committed
Use custom param naming to prevent name clashes in generated code
1 parent abb343c commit 491d5fb

File tree

4 files changed

+74
-24
lines changed

4 files changed

+74
-24
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ path = "examples/use_matching_string.rs"
4040
[dependencies]
4141
quote = "1.0"
4242
proc-macro2 = "1.0"
43-
syn = { version = "2.0", features = ["extra-traits", "full"] }
43+
syn = { version = "2.0", features = ["extra-traits", "full", "visit", "visit-mut"] }
4444
itertools = "0.14.0"
4545
phf = { version = "0.11.2", features = ["macros"] }
4646

src/fields.rs

Lines changed: 71 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use itertools::Itertools;
22
use proc_macro2::{Ident, Span, TokenStream};
33
use quote::quote;
4-
use syn::{Expr, FieldsNamed, FieldsUnnamed, Result, Type};
4+
use std::collections::HashMap;
5+
use syn::punctuated::Punctuated;
6+
use syn::visit_mut::VisitMut;
7+
use syn::{Expr, FieldsNamed, FieldsUnnamed, Path, Result, Type};
58

69
pub enum Field {
710
Default { name: Ident, ty: Type },
@@ -95,29 +98,48 @@ impl Field {
9598
}
9699
}
97100

98-
pub fn generate_derived_expression(&self) -> Option<TokenStream> {
101+
pub fn generate_derived_expression(&self, fields: &Fields) -> Option<TokenStream> {
99102
match self {
100103
Field::Default { .. } => None,
101-
Field::Derived { name, expr, ty } => Some(quote! {
102-
let #name: #ty = #expr;
103-
}),
104+
Field::Derived { expr, ty, .. } => {
105+
let name = self.get_param_name();
106+
let mut expr = expr.clone();
107+
fields.rename_derive_expr(&mut expr);
108+
Some(quote! {
109+
let #name: #ty = #expr;
110+
})
111+
}
104112
}
105113
}
114+
115+
pub fn get_param_name(&self) -> Ident {
116+
Ident::new(&format!("param_{}", self.get_name()), Span::call_site())
117+
}
106118
}
107119

108120
impl Fields {
109-
pub fn get_all_names(&self) -> Vec<Ident> {
110-
self.fields
111-
.iter()
112-
.map(|field| field.get_name().clone())
113-
.collect()
121+
pub fn get_creation_names(&self) -> Vec<TokenStream> {
122+
let transform = if self.is_named {
123+
|field: &Field| {
124+
let name = field.get_name();
125+
let param_name = field.get_param_name();
126+
quote! { #name: #param_name }
127+
}
128+
} else {
129+
|field: &Field| {
130+
let name = field.get_param_name();
131+
quote! { #name }
132+
}
133+
};
134+
135+
self.fields.iter().map(transform).collect()
114136
}
115137

116138
pub fn get_expression_names(&self) -> Vec<Ident> {
117139
self.fields
118140
.iter()
119141
.filter(|field| !matches!(field, Field::Derived { .. }))
120-
.map(|field| field.get_name().clone())
142+
.map(Field::get_param_name)
121143
.collect()
122144
}
123145

@@ -132,31 +154,59 @@ impl Fields {
132154
pub fn get_derived_expressions(&self) -> Vec<TokenStream> {
133155
self.fields
134156
.iter()
135-
.filter_map(|field| field.generate_derived_expression())
157+
.filter_map(|field| field.generate_derived_expression(self))
136158
.collect()
137159
}
138160

139161
pub fn create_instance_expr(&self, variant_name: Option<&Ident>) -> TokenStream {
140-
let all_names = self.get_all_names();
162+
let creation_names = self.get_creation_names();
141163

142-
if all_names.is_empty() {
143-
if let Some(name) = variant_name {
144-
quote! { Self::#name }
164+
if creation_names.is_empty() {
165+
if let Some(variant) = variant_name {
166+
quote! { Self::#variant }
145167
} else {
146168
quote! { Self }
147169
}
148-
} else if let Some(name) = variant_name {
170+
} else if let Some(variant) = variant_name {
149171
if self.is_named {
150-
quote! { Self::#name { #(#all_names),* } }
172+
quote! { Self::#variant { #(#creation_names),* } }
151173
} else {
152-
quote! { Self::#name(#(#all_names),*) }
174+
quote! { Self::#variant(#(#creation_names),*) }
153175
}
154176
} else {
155177
if self.is_named {
156-
quote! { Self { #(#all_names),* } }
178+
quote! { Self { #(#creation_names),* } }
157179
} else {
158-
quote! { Self(#(#all_names),*) }
180+
quote! { Self(#(#creation_names),*) }
181+
}
182+
}
183+
}
184+
185+
fn rename_derive_expr(&self, expr: &mut Expr) {
186+
struct RenameDerived(HashMap<Ident, Ident>);
187+
impl VisitMut for RenameDerived {
188+
fn visit_path_mut(&mut self, path: &mut Path) {
189+
eprintln!("visit_path_mut: {:#?}", path);
190+
191+
for (source, target) in &self.0 {
192+
if path.is_ident(source) {
193+
path.segments = Punctuated::new();
194+
path.segments.push(syn::PathSegment {
195+
ident: target.clone(),
196+
arguments: Default::default(),
197+
});
198+
}
199+
}
159200
}
160201
}
202+
203+
let mapping = self
204+
.fields
205+
.iter()
206+
.filter(|field| !matches!(field, Field::Derived { .. }))
207+
.map(|field| (field.get_name().clone(), field.get_param_name()))
208+
.collect();
209+
210+
RenameDerived(mapping).visit_expr_mut(expr)
161211
}
162212
}

src/string_matching.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub fn parse_string_match(fields: &Fields, literal: LitStr) -> Result<Vec<TokenS
3030
}
3131

3232
for field in &fields.fields {
33-
if let Some(expr) = field.generate_derived_expression() {
33+
if let Some(expr) = field.generate_derived_expression(fields) {
3434
result.push(expr);
3535
}
3636
}

tests/enums.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,4 @@ fn test_dummy() {
5151
Ok::<_, Error<_>>(TestStruct::Dummy),
5252
TestStruct::parse_complete("dummy")
5353
);
54-
}
54+
}

0 commit comments

Comments
 (0)