1
1
//! Builtin derives.
2
2
3
3
use base_db:: { CrateOrigin , LangCrateOrigin } ;
4
+ use std:: collections:: HashSet ;
4
5
use tracing:: debug;
5
6
6
7
use crate :: tt:: { self , TokenId } ;
7
8
use syntax:: {
8
- ast:: { self , AstNode , HasGenericParams , HasModuleItem , HasName } ,
9
+ ast:: { self , AstNode , HasGenericParams , HasModuleItem , HasName , HasTypeBounds , PathType } ,
9
10
match_ast,
10
11
} ;
11
12
@@ -60,8 +61,11 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
60
61
61
62
struct BasicAdtInfo {
62
63
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 > ,
65
69
}
66
70
67
71
fn parse_adt ( tt : & tt:: Subtree ) -> Result < BasicAdtInfo , ExpandError > {
@@ -86,46 +90,126 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
86
90
} ,
87
91
}
88
92
} ;
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 ( ) ;
96
94
let param_types = params
97
95
. into_iter ( )
98
96
. flat_map ( |param_list| param_list. type_or_const_params ( ) )
99
97
. 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 {
101
115
let ty = param
102
116
. ty ( )
103
117
. map ( |ty| mbe:: syntax_node_to_token_tree ( ty. syntax ( ) ) . 0 )
104
118
. unwrap_or_else ( tt:: Subtree :: empty) ;
105
119
Some ( ty)
106
120
} else {
107
121
None
108
- }
122
+ } ;
123
+ ( name, ty, bounds)
109
124
} )
110
125
. 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 } )
112
165
}
113
166
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.
114
198
fn expand_simple_derive ( tt : & tt:: Subtree , trait_path : tt:: Subtree ) -> ExpandResult < tt:: Subtree > {
115
199
let info = match parse_adt ( tt) {
116
200
Ok ( info) => info,
117
201
Err ( e) => return ExpandResult :: with_err ( tt:: Subtree :: empty ( ) , e) ,
118
202
} ;
203
+ let mut where_block = vec ! [ ] ;
119
204
let ( params, args) : ( Vec < _ > , Vec < _ > ) = info
120
205
. param_types
121
206
. 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) | {
128
208
let ident_ = ident. clone ( ) ;
209
+ if let Some ( b) = bound {
210
+ let ident = ident. clone ( ) ;
211
+ where_block. push ( quote ! { #ident : #b , } ) ;
212
+ }
129
213
if let Some ( ty) = param_ty {
130
214
( quote ! { const #ident : #ty , } , quote ! { #ident_ , } )
131
215
} else {
@@ -134,9 +218,16 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
134
218
}
135
219
} )
136
220
. 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
+
137
228
let name = info. name ;
138
229
let expanded = quote ! {
139
- impl < ##params > #trait_path for #name < ##args > { }
230
+ impl < ##params > #trait_path for #name < ##args > where ##where_block { }
140
231
} ;
141
232
ExpandResult :: ok ( expanded)
142
233
}
0 commit comments