Skip to content

Commit

Permalink
Avoid stringifying and reparsing a TokenStream
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Aaron1011 committed Sep 28, 2020
1 parent 3908896 commit d8ceeb8
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 9 deletions.
16 changes: 7 additions & 9 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{
parse_str, punctuated::Punctuated, spanned::Spanned, Attribute, Data, DeriveInput,
parse_quote, punctuated::Punctuated, spanned::Spanned, Attribute, Data, DeriveInput,
Error, Field, Fields, FieldsNamed, FieldsUnnamed, GenericParam, Generics, Ident,
ImplGenerics, Index, Meta, NestedMeta, Result, Token, Type, TypeGenerics,
TypeParamBound, Variant, WhereClause,
Expand Down Expand Up @@ -121,10 +121,9 @@ pub fn add_extra_type_param_bound_op_output<'a>(
let mut generics = generics.clone();
for type_param in &mut generics.type_params_mut() {
let type_ident = &type_param.ident;
let bound: TypeParamBound = parse_str(
&quote!(::core::ops::#trait_ident<Output=#type_ident>).to_string(),
)
.unwrap();
let bound: TypeParamBound = parse_quote! {
::core::ops::#trait_ident<Output=#type_ident>
};
type_param.bounds.push(bound)
}

Expand All @@ -143,7 +142,7 @@ pub fn add_extra_ty_param_bound<'a>(
bound: &'a TokenStream,
) -> Generics {
let mut generics = generics.clone();
let bound: TypeParamBound = parse_str(&bound.to_string()).unwrap();
let bound: TypeParamBound = parse_quote! { #bound };
for type_param in &mut generics.type_params_mut() {
type_param.bounds.push(bound.clone())
}
Expand Down Expand Up @@ -176,7 +175,7 @@ pub fn add_extra_generic_param(
generics: &Generics,
generic_param: TokenStream,
) -> Generics {
let generic_param: GenericParam = parse_str(&generic_param.to_string()).unwrap();
let generic_param: GenericParam = parse_quote! { #generic_param };
let mut generics = generics.clone();
generics.params.push(generic_param);

Expand All @@ -187,8 +186,7 @@ pub fn add_extra_where_clauses(
generics: &Generics,
type_where_clauses: TokenStream,
) -> Generics {
let mut type_where_clauses: WhereClause =
parse_str(&type_where_clauses.to_string()).unwrap();
let mut type_where_clauses: WhereClause = parse_quote! { #type_where_clauses };
let mut new_generics = generics.clone();
if let Some(old_where) = new_generics.where_clause {
type_where_clauses.predicates.extend(old_where.predicates)
Expand Down
14 changes: 14 additions & 0 deletions tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,20 @@ struct DoubleUIntStruct {
#[derive(From, Into, Constructor)]
struct Unit;

// Tests that we can forward to a path
// containing `$crate`
macro_rules! use_dollar_crate {
() => {
struct Foo;
#[derive(From)]
enum Bar {
First(#[from(forward)] $crate::Foo)
}
}
}

use_dollar_crate!();

#[test]
fn main() {
let mut myint: MyInt = 5.into();
Expand Down

0 comments on commit d8ceeb8

Please sign in to comment.