@@ -14,6 +14,7 @@ extern crate quote;
1414
1515use proc_macro2:: TokenStream ;
1616use quote:: ToTokens ;
17+ use syn:: spanned:: Spanned ;
1718
1819#[ proc_macro_attribute]
1920pub fn assert_instr (
@@ -67,40 +68,70 @@ pub fn assert_instr(
6768 ) ;
6869 let mut inputs = Vec :: new ( ) ;
6970 let mut input_vals = Vec :: new ( ) ;
70- let mut const_vals = Vec :: new ( ) ;
71+ let mut param_vals = Vec :: new ( ) ;
7172 let ret = & func. sig . output ;
7273 for arg in func. sig . inputs . iter ( ) {
7374 let capture = match * arg {
74- syn:: FnArg :: Typed ( ref c) => c,
75+ syn:: FnArg :: Typed ( ref c) => c. to_owned ( ) ,
7576 ref v => panic ! (
7677 "arguments must not have patterns: `{:?}`" ,
7778 v. clone( ) . into_token_stream( )
7879 ) ,
7980 } ;
80- let ident = match * capture. pat {
81- syn:: Pat :: Ident ( ref i) => & i. ident ,
81+ let ident = match capture. pat . as_ref ( ) {
82+ syn:: Pat :: Ident ( i) => & i. ident . to_owned ( ) ,
8283 _ => panic ! ( "must have bare arguments" ) ,
8384 } ;
84- if let Some ( ( _, tokens) ) = invoc. args . iter ( ) . find ( |a| * ident == a. 0 ) {
85+ if let Some ( & ( _, ref tokens) ) = invoc. args . iter ( ) . find ( |a| * ident == a. 0 ) {
8586 input_vals. push ( quote ! { #tokens } ) ;
8687 } else {
8788 inputs. push ( capture) ;
8889 input_vals. push ( quote ! { #ident } ) ;
8990 }
9091 }
9192 for arg in func. sig . generics . params . iter ( ) {
92- let c = match * arg {
93- syn:: GenericParam :: Const ( ref c) => c,
93+ match * arg {
94+ syn:: GenericParam :: Const ( ref c) => {
95+ if let Some ( ( _, tokens) ) = invoc. args . iter ( ) . find ( |a| c. ident == a. 0 ) {
96+ param_vals. push ( quote ! { #tokens } ) ;
97+ } else {
98+ panic ! ( "const generics must have a value for tests" ) ;
99+ }
100+ }
101+ syn:: GenericParam :: Type ( ref t) => {
102+ if let Some ( ( _, tokens) ) = invoc. args . iter ( ) . find ( |a| t. ident == a. 0 )
103+ && let syn:: Expr :: Path ( syn:: ExprPath { qself, path, .. } ) = tokens
104+ {
105+ param_vals. push ( syn:: Token ![ _] ( tokens. span ( ) ) . to_token_stream ( ) ) ;
106+
107+ let generic_ty_value = syn:: TypePath {
108+ qself : qself. clone ( ) ,
109+ path : path. clone ( ) ,
110+ } ;
111+
112+ // Replace any function arguments that use generic parameters with the
113+ // instantiation provided in the macro invocation.
114+ inputs. iter_mut ( ) . for_each ( |arg| {
115+ update_type_path ( arg. ty . as_mut ( ) , |type_path : & mut syn:: TypePath | {
116+ if let Some ( syn:: PathSegment {
117+ ident : last_ident, ..
118+ } ) = type_path. path . segments . last_mut ( )
119+ {
120+ if * last_ident == t. ident {
121+ * type_path = generic_ty_value. to_owned ( )
122+ }
123+ }
124+ } )
125+ } ) ;
126+ } else {
127+ panic ! ( "type generics must have a type for tests" ) ;
128+ }
129+ }
94130 ref v => panic ! (
95- "only const generics are allowed: `{:?}`" ,
131+ "only type and const generics are allowed: `{:?}`" ,
96132 v. clone( ) . into_token_stream( )
97133 ) ,
98134 } ;
99- if let Some ( ( _, tokens) ) = invoc. args . iter ( ) . find ( |a| c. ident == a. 0 ) {
100- const_vals. push ( quote ! { #tokens } ) ;
101- } else {
102- panic ! ( "const generics must have a value for tests" ) ;
103- }
104135 }
105136
106137 let attrs = func
@@ -138,7 +169,7 @@ pub fn assert_instr(
138169 #[ unsafe ( no_mangle) ]
139170 #[ inline( never) ]
140171 pub unsafe extern #abi fn #shim_name( #( #inputs) , * ) #ret {
141- #name:: <#( #const_vals ) , * >( #( #input_vals) , * )
172+ #name:: <#( #param_vals ) , * >( #( #input_vals) , * )
142173 }
143174 } ;
144175
@@ -222,3 +253,23 @@ where
222253 }
223254 }
224255}
256+
257+ /// Calls `update` on type paths so that type generics can be replaced with the instantiation from
258+ /// the attribute.
259+ fn update_type_path < F > ( ty : & mut syn:: Type , update : F )
260+ where
261+ F : Fn ( & mut syn:: TypePath ) ,
262+ {
263+ use syn:: Type :: * ;
264+ match ty {
265+ Array ( syn:: TypeArray { elem, .. } )
266+ | Group ( syn:: TypeGroup { elem, .. } )
267+ | Paren ( syn:: TypeParen { elem, .. } )
268+ | Ptr ( syn:: TypePtr { elem, .. } )
269+ | Reference ( syn:: TypeReference { elem, .. } )
270+ | Slice ( syn:: TypeSlice { elem, .. } ) => update_type_path ( elem. as_mut ( ) , update) ,
271+ Path ( path @ syn:: TypePath { .. } ) => update ( path) ,
272+ Tuple ( ..) => panic ! ( "tuples and generic types together are not yet supported" ) ,
273+ _ => { }
274+ }
275+ }
0 commit comments