11//! Builtin derives.
22
33use base_db:: { CrateOrigin , LangCrateOrigin } ;
4+ use std:: collections:: HashSet ;
45use tracing:: debug;
56
67use crate :: tt:: { self , TokenId } ;
78use syntax:: {
8- ast:: { self , AstNode , HasGenericParams , HasModuleItem , HasName } ,
9+ ast:: { self , AstNode , HasGenericParams , HasModuleItem , HasName , HasTypeBounds , PathType } ,
910 match_ast,
1011} ;
1112
@@ -60,8 +61,11 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
6061
6162struct BasicAdtInfo {
6263 name : tt:: Ident ,
63- /// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
64- param_types : Vec < Option < tt:: Subtree > > ,
64+ /// first field is the name, and
65+ /// second field is `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
66+ /// third fields is where bounds, if any
67+ param_types : Vec < ( tt:: Subtree , Option < tt:: Subtree > , Option < tt:: Subtree > ) > ,
68+ associated_types : Vec < tt:: Subtree > ,
6569}
6670
6771fn parse_adt ( tt : & tt:: Subtree ) -> Result < BasicAdtInfo , ExpandError > {
@@ -86,46 +90,126 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
8690 } ,
8791 }
8892 } ;
89- let name = name. ok_or_else ( || {
90- debug ! ( "parsed item has no name" ) ;
91- ExpandError :: Other ( "missing name" . into ( ) )
92- } ) ?;
93- let name_token_id =
94- token_map. token_by_range ( name. syntax ( ) . text_range ( ) ) . unwrap_or_else ( TokenId :: unspecified) ;
95- let name_token = tt:: Ident { span : name_token_id, text : name. text ( ) . into ( ) } ;
93+ let mut param_type_set: HashSet < String > = HashSet :: new ( ) ;
9694 let param_types = params
9795 . into_iter ( )
9896 . flat_map ( |param_list| param_list. type_or_const_params ( ) )
9997 . map ( |param| {
100- if let ast:: TypeOrConstParam :: Const ( param) = param {
98+ let name = {
99+ let this = param. name ( ) ;
100+ match this {
101+ Some ( x) => {
102+ param_type_set. insert ( x. to_string ( ) ) ;
103+ mbe:: syntax_node_to_token_tree ( x. syntax ( ) ) . 0
104+ }
105+ None => tt:: Subtree :: empty ( ) ,
106+ }
107+ } ;
108+ let bounds = match & param {
109+ ast:: TypeOrConstParam :: Type ( x) => {
110+ x. type_bound_list ( ) . map ( |x| mbe:: syntax_node_to_token_tree ( x. syntax ( ) ) . 0 )
111+ }
112+ ast:: TypeOrConstParam :: Const ( _) => None ,
113+ } ;
114+ let ty = if let ast:: TypeOrConstParam :: Const ( param) = param {
101115 let ty = param
102116 . ty ( )
103117 . map ( |ty| mbe:: syntax_node_to_token_tree ( ty. syntax ( ) ) . 0 )
104118 . unwrap_or_else ( tt:: Subtree :: empty) ;
105119 Some ( ty)
106120 } else {
107121 None
108- }
122+ } ;
123+ ( name, ty, bounds)
109124 } )
110125 . collect ( ) ;
111- Ok ( BasicAdtInfo { name : name_token, param_types } )
126+ let is_associated_type = |p : & PathType | {
127+ if let Some ( p) = p. path ( ) {
128+ if let Some ( parent) = p. qualifier ( ) {
129+ if let Some ( x) = parent. segment ( ) {
130+ if let Some ( x) = x. path_type ( ) {
131+ if let Some ( x) = x. path ( ) {
132+ if let Some ( pname) = x. as_single_name_ref ( ) {
133+ if param_type_set. contains ( & pname. to_string ( ) ) {
134+ // <T as Trait>::Assoc
135+ return true ;
136+ }
137+ }
138+ }
139+ }
140+ }
141+ if let Some ( pname) = parent. as_single_name_ref ( ) {
142+ if param_type_set. contains ( & pname. to_string ( ) ) {
143+ // T::Assoc
144+ return true ;
145+ }
146+ }
147+ }
148+ }
149+ false
150+ } ;
151+ let associated_types = node
152+ . descendants ( )
153+ . filter_map ( PathType :: cast)
154+ . filter ( is_associated_type)
155+ . map ( |x| mbe:: syntax_node_to_token_tree ( x. syntax ( ) ) . 0 )
156+ . collect :: < Vec < _ > > ( ) ;
157+ let name = name. ok_or_else ( || {
158+ debug ! ( "parsed item has no name" ) ;
159+ ExpandError :: Other ( "missing name" . into ( ) )
160+ } ) ?;
161+ let name_token_id =
162+ token_map. token_by_range ( name. syntax ( ) . text_range ( ) ) . unwrap_or_else ( TokenId :: unspecified) ;
163+ let name_token = tt:: Ident { span : name_token_id, text : name. text ( ) . into ( ) } ;
164+ Ok ( BasicAdtInfo { name : name_token, param_types, associated_types } )
112165}
113166
167+ /// Given that we are deriving a trait `DerivedTrait` for a type like:
168+ ///
169+ /// ```ignore (only-for-syntax-highlight)
170+ /// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait {
171+ /// a: A,
172+ /// b: B::Item,
173+ /// b1: <B as DeclaredTrait>::Item,
174+ /// c1: <C as WhereTrait>::Item,
175+ /// c2: Option<<C as WhereTrait>::Item>,
176+ /// ...
177+ /// }
178+ /// ```
179+ ///
180+ /// create an impl like:
181+ ///
182+ /// ```ignore (only-for-syntax-highlight)
183+ /// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where
184+ /// C: WhereTrait,
185+ /// A: DerivedTrait + B1 + ... + BN,
186+ /// B: DerivedTrait + B1 + ... + BN,
187+ /// C: DerivedTrait + B1 + ... + BN,
188+ /// B::Item: DerivedTrait + B1 + ... + BN,
189+ /// <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN,
190+ /// ...
191+ /// {
192+ /// ...
193+ /// }
194+ /// ```
195+ ///
196+ /// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and
197+ /// therefore does not get bound by the derived trait.
114198fn expand_simple_derive ( tt : & tt:: Subtree , trait_path : tt:: Subtree ) -> ExpandResult < tt:: Subtree > {
115199 let info = match parse_adt ( tt) {
116200 Ok ( info) => info,
117201 Err ( e) => return ExpandResult :: with_err ( tt:: Subtree :: empty ( ) , e) ,
118202 } ;
203+ let mut where_block = vec ! [ ] ;
119204 let ( params, args) : ( Vec < _ > , Vec < _ > ) = info
120205 . param_types
121206 . into_iter ( )
122- . enumerate ( )
123- . map ( |( idx, param_ty) | {
124- let ident = tt:: Leaf :: Ident ( tt:: Ident {
125- span : tt:: TokenId :: unspecified ( ) ,
126- text : format ! ( "T{idx}" ) . into ( ) ,
127- } ) ;
207+ . map ( |( ident, param_ty, bound) | {
128208 let ident_ = ident. clone ( ) ;
209+ if let Some ( b) = bound {
210+ let ident = ident. clone ( ) ;
211+ where_block. push ( quote ! { #ident : #b , } ) ;
212+ }
129213 if let Some ( ty) = param_ty {
130214 ( quote ! { const #ident : #ty , } , quote ! { #ident_ , } )
131215 } else {
@@ -134,9 +218,16 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
134218 }
135219 } )
136220 . unzip ( ) ;
221+
222+ where_block. extend ( info. associated_types . iter ( ) . map ( |x| {
223+ let x = x. clone ( ) ;
224+ let bound = trait_path. clone ( ) ;
225+ quote ! { #x : #bound , }
226+ } ) ) ;
227+
137228 let name = info. name ;
138229 let expanded = quote ! {
139- impl < ##params > #trait_path for #name < ##args > { }
230+ impl < ##params > #trait_path for #name < ##args > where ##where_block { }
140231 } ;
141232 ExpandResult :: ok ( expanded)
142233}
0 commit comments