Skip to content

Commit 6c6ec74

Browse files
committed
Avoid stringifying and reparsing a TokenStream
This PR changes removes uses of the pattern `syn::parse_str(tokens.to_string())`, which loses `Span` information. In addition to making error messages less precise, it can break code that depends on the hygiene information carried by `Span`. Instead, `syn::parse_quote` is used to parse a `TokenStream` into the desired AST struct. I've added a test which demonstrates one of the problems with the previous code - using `$crate` would previously cause a panic, but now compiles successfully.
1 parent 3908896 commit 6c6ec74

File tree

2 files changed

+24
-12
lines changed

2 files changed

+24
-12
lines changed

src/utils.rs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
use proc_macro2::{Span, TokenStream};
44
use quote::{quote, ToTokens};
55
use syn::{
6-
parse_str, punctuated::Punctuated, spanned::Spanned, Attribute, Data, DeriveInput,
7-
Error, Field, Fields, FieldsNamed, FieldsUnnamed, GenericParam, Generics, Ident,
8-
ImplGenerics, Index, Meta, NestedMeta, Result, Token, Type, TypeGenerics,
9-
TypeParamBound, Variant, WhereClause,
6+
parse_quote, punctuated::Punctuated, spanned::Spanned, Attribute, Data,
7+
DeriveInput, Error, Field, Fields, FieldsNamed, FieldsUnnamed, GenericParam,
8+
Generics, Ident, ImplGenerics, Index, Meta, NestedMeta, Result, Token, Type,
9+
TypeGenerics, TypeParamBound, Variant, WhereClause,
1010
};
1111

1212
#[derive(Clone, Copy, Default)]
@@ -121,10 +121,9 @@ pub fn add_extra_type_param_bound_op_output<'a>(
121121
let mut generics = generics.clone();
122122
for type_param in &mut generics.type_params_mut() {
123123
let type_ident = &type_param.ident;
124-
let bound: TypeParamBound = parse_str(
125-
&quote!(::core::ops::#trait_ident<Output=#type_ident>).to_string(),
126-
)
127-
.unwrap();
124+
let bound: TypeParamBound = parse_quote! {
125+
::core::ops::#trait_ident<Output=#type_ident>
126+
};
128127
type_param.bounds.push(bound)
129128
}
130129

@@ -143,7 +142,7 @@ pub fn add_extra_ty_param_bound<'a>(
143142
bound: &'a TokenStream,
144143
) -> Generics {
145144
let mut generics = generics.clone();
146-
let bound: TypeParamBound = parse_str(&bound.to_string()).unwrap();
145+
let bound: TypeParamBound = parse_quote! { #bound };
147146
for type_param in &mut generics.type_params_mut() {
148147
type_param.bounds.push(bound.clone())
149148
}
@@ -176,7 +175,7 @@ pub fn add_extra_generic_param(
176175
generics: &Generics,
177176
generic_param: TokenStream,
178177
) -> Generics {
179-
let generic_param: GenericParam = parse_str(&generic_param.to_string()).unwrap();
178+
let generic_param: GenericParam = parse_quote! { #generic_param };
180179
let mut generics = generics.clone();
181180
generics.params.push(generic_param);
182181

@@ -187,8 +186,7 @@ pub fn add_extra_where_clauses(
187186
generics: &Generics,
188187
type_where_clauses: TokenStream,
189188
) -> Generics {
190-
let mut type_where_clauses: WhereClause =
191-
parse_str(&type_where_clauses.to_string()).unwrap();
189+
let mut type_where_clauses: WhereClause = parse_quote! { #type_where_clauses };
192190
let mut new_generics = generics.clone();
193191
if let Some(old_where) = new_generics.where_clause {
194192
type_where_clauses.predicates.extend(old_where.predicates)

tests/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,20 @@ struct DoubleUIntStruct {
153153
#[derive(From, Into, Constructor)]
154154
struct Unit;
155155

156+
// Tests that we can forward to a path
157+
// containing `$crate`
158+
macro_rules! use_dollar_crate {
159+
() => {
160+
struct Foo;
161+
#[derive(From)]
162+
enum Bar {
163+
First(#[from(forward)] $crate::Foo),
164+
}
165+
};
166+
}
167+
168+
use_dollar_crate!();
169+
156170
#[test]
157171
fn main() {
158172
let mut myint: MyInt = 5.into();

0 commit comments

Comments
 (0)