1
+ use crate :: HashMap ;
2
+ use crate :: HashSet ;
3
+ use itertools:: Itertools ;
1
4
use proc_macro2:: Span ;
2
5
use quote:: { quote, ToTokens } ;
6
+ use syn:: token:: Unsafe ;
7
+ use syn:: Abi ;
3
8
use syn:: {
4
- Attribute , File , ForeignItem , Ident , Item , ItemConst , ItemEnum , ItemFn , ItemForeignMod ,
5
- ItemImpl , ItemMod , ItemStatic , ItemStruct , ItemType , ItemUnion , ItemUse ,
9
+ Attribute , File , ForeignItem , Ident , Item , ItemConst , ItemEnum , ItemFn ,
10
+ ItemForeignMod , ItemImpl , ItemMod , ItemStatic , ItemStruct , ItemType ,
11
+ ItemUnion , ItemUse ,
6
12
} ;
7
- use itertools:: Itertools ;
8
- use crate :: HashMap ;
9
- use crate :: HashSet ;
10
13
11
14
pub fn merge_cfg_attributes ( file : & mut File ) {
12
15
let mut visitor = Visitor :: new ( ) ;
13
16
visitor. visit_file ( file) ;
14
17
}
15
18
19
+ struct SyntheticMod {
20
+ attrs : AttributeSet ,
21
+ unsafety : Option < Unsafe > ,
22
+ abi : Option < Abi > ,
23
+ items : Vec < Item > ,
24
+ }
25
+
26
+ impl SyntheticMod {
27
+ pub fn new ( attrs : AttributeSet ) -> Self {
28
+ Self {
29
+ attrs,
30
+ unsafety : None ,
31
+ abi : None ,
32
+ items : vec ! [ ] ,
33
+ }
34
+ }
35
+ }
36
+
37
+ #[ derive( Default , Clone ) ]
38
+ struct AttributeSet {
39
+ cfg_attrs : HashSet < Attribute > ,
40
+ cc_attrs : HashSet < Attribute > ,
41
+ other_attrs : HashSet < Attribute > ,
42
+ unsafety : Option < Unsafe > ,
43
+ abi : Option < Abi > ,
44
+ }
45
+
46
+ impl AttributeSet {
47
+ fn new (
48
+ attrs : & [ Attribute ] ,
49
+ unsafety : Option < Unsafe > ,
50
+ abi : Option < Abi > ,
51
+ ) -> Self {
52
+ let mut attribute_set = AttributeSet :: default ( ) ;
53
+
54
+ for attr in attrs {
55
+ let target_set = if let Some ( ident) = attr. path ( ) . get_ident ( ) {
56
+ match ident. to_string ( ) . as_str ( ) {
57
+ "cfg" => & mut attribute_set. cfg_attrs ,
58
+ "link" => & mut attribute_set. cc_attrs ,
59
+ _ => & mut attribute_set. other_attrs ,
60
+ }
61
+ } else {
62
+ & mut attribute_set. other_attrs
63
+ } ;
64
+ target_set. insert ( attr. clone ( ) ) ;
65
+ }
66
+ attribute_set. unsafety = unsafety;
67
+ attribute_set. abi = abi;
68
+
69
+ attribute_set
70
+ }
71
+
72
+ fn extend (
73
+ & mut self ,
74
+ attrs : & [ Attribute ] ,
75
+ unsafety : Option < Unsafe > ,
76
+ abi : Option < Abi > ,
77
+ ) {
78
+ let other = AttributeSet :: new ( attrs, unsafety, abi) ;
79
+ self . other_attrs . extend ( other. other_attrs ) ;
80
+ self . cfg_attrs . extend ( other. cfg_attrs ) ;
81
+ self . cc_attrs . extend ( other. cc_attrs ) ;
82
+
83
+ self . unsafety = other. unsafety . or ( self . unsafety ) ;
84
+ self . abi = other. abi . or ( self . abi . clone ( ) ) ;
85
+ }
86
+
87
+ fn ident ( & self ) -> Ident {
88
+ Ident :: new (
89
+ Itertools :: intersperse (
90
+ self . unsafety
91
+ . map ( |r#unsafe| r#unsafe. to_token_stream ( ) . to_string ( ) )
92
+ . into_iter ( )
93
+ . chain (
94
+ self . abi
95
+ . as_ref ( )
96
+ . map ( |abi| abi. to_token_stream ( ) . to_string ( ) ) ,
97
+ )
98
+ . chain (
99
+ self . cfg_attrs
100
+ . iter ( )
101
+ . chain ( self . cc_attrs . iter ( ) )
102
+ . map ( |attr| attr. to_token_stream ( ) . to_string ( ) )
103
+ . sorted ( ) ,
104
+ ) ,
105
+ "_" . to_string ( ) ,
106
+ )
107
+ . collect :: < String > ( )
108
+ . replace ( |c| !c. is_alphanumeric ( ) , "_" )
109
+ . chars ( )
110
+ . coalesce ( |a, b| {
111
+ if a == '_' && b == '_' {
112
+ Ok ( a)
113
+ } else {
114
+ Err ( ( a, b) )
115
+ }
116
+ } )
117
+ . collect :: < String > ( )
118
+ . trim_matches ( '_' ) ,
119
+ Span :: call_site ( ) ,
120
+ )
121
+ }
122
+ }
123
+
16
124
struct Visitor {
17
- synthetic_mods : HashMap < Ident , ( AttributeSet , Vec < Item > ) > ,
125
+ synthetic_mods : HashMap < Ident , SyntheticMod > ,
18
126
new_items : Vec < Item > ,
19
127
}
20
128
@@ -29,28 +137,39 @@ impl Visitor {
29
137
fn visit_file ( & mut self , file : & mut File ) {
30
138
self . visit_items ( & mut file. items ) ;
31
139
32
- for ( ident, ( attr_set, items) ) in self . synthetic_mods . drain ( ) {
33
- let cfg_attrs: Vec < _ > = attr_set. cfg_attrs . iter ( ) . collect ( ) ;
34
- let cc_attrs: Vec < _ > = attr_set. cc_attrs . iter ( ) . collect ( ) ;
35
- let block = if cc_attrs. is_empty ( ) {
140
+ for (
141
+ ref mut ident,
142
+ SyntheticMod {
143
+ ref mut attrs,
144
+ ref mut unsafety,
145
+ ref mut abi,
146
+ ref mut items,
147
+ } ,
148
+ ) in self . synthetic_mods . drain ( )
149
+ {
150
+ let cfg_attrs = attrs. cfg_attrs . iter ( ) . collect :: < Vec < _ > > ( ) ;
151
+ let cc_attrs = attrs. cc_attrs . iter ( ) . collect :: < Vec < _ > > ( ) ;
152
+ let synthetic_mod = if abi. is_some ( ) {
36
153
quote ! {
37
- #( #items) *
154
+ #( #cfg_attrs) *
155
+ pub mod #ident {
156
+ #( #cc_attrs) *
157
+ #unsafety #abi {
158
+ #( #items) *
159
+ }
160
+ }
38
161
}
39
162
} else {
40
- // TODO: Don't swallow abi/unsafety :(
41
163
quote ! {
42
- #( #cc_attrs ) *
43
- unsafe extern "C" {
164
+ #( #cfg_attrs ) *
165
+ pub mod #ident {
44
166
#( #items) *
45
167
}
46
168
}
47
169
} ;
48
170
49
171
self . new_items . push ( Item :: Verbatim ( quote ! {
50
- #( #cfg_attrs) *
51
- pub mod #ident {
52
- #block
53
- }
172
+ #synthetic_mod
54
173
55
174
#( #cfg_attrs) *
56
175
pub use #ident:: * ;
@@ -63,17 +182,17 @@ impl Visitor {
63
182
fn visit_items ( & mut self , items : & mut Vec < Item > ) {
64
183
for mut item in std:: mem:: take ( items) {
65
184
match & mut item {
66
- Item :: Const ( ItemConst { ref mut attrs, .. } )
67
- | Item :: Struct ( ItemStruct { ref mut attrs, .. } )
68
- | Item :: Enum ( ItemEnum { ref mut attrs, .. } )
69
- | Item :: Fn ( ItemFn { ref mut attrs, .. } )
70
- | Item :: Union ( ItemUnion { ref mut attrs, .. } )
71
- | Item :: Type ( ItemType { ref mut attrs, .. } )
72
- | Item :: Impl ( ItemImpl { ref mut attrs, .. } )
73
- | Item :: Mod ( ItemMod { ref mut attrs, .. } )
74
- | Item :: Use ( ItemUse { ref mut attrs, .. } )
75
- | Item :: Static ( ItemStatic { ref mut attrs, .. } ) => {
76
- let attr_set = partition_attributes ( attrs) ;
185
+ Item :: Const ( ItemConst { ref mut attrs, .. } ) |
186
+ Item :: Struct ( ItemStruct { ref mut attrs, .. } ) |
187
+ Item :: Enum ( ItemEnum { ref mut attrs, .. } ) |
188
+ Item :: Union ( ItemUnion { ref mut attrs, .. } ) |
189
+ Item :: Type ( ItemType { ref mut attrs, .. } ) |
190
+ Item :: Use ( ItemUse { ref mut attrs, .. } ) |
191
+ Item :: Static ( ItemStatic { ref mut attrs, .. } ) |
192
+ Item :: Mod ( ItemMod { ref mut attrs, .. } ) |
193
+ Item :: Impl ( ItemImpl { ref mut attrs, .. } ) |
194
+ Item :: Fn ( ItemFn { ref mut attrs, .. } ) => {
195
+ let attr_set = AttributeSet :: new ( attrs, None , None ) ;
77
196
* attrs = attr_set. other_attrs . iter ( ) . cloned ( ) . collect ( ) ;
78
197
self . insert_item_into_mod ( attr_set, item) ;
79
198
}
@@ -89,19 +208,23 @@ impl Visitor {
89
208
90
209
fn visit_foreign_mod ( & mut self , foreign_mod : & mut ItemForeignMod ) {
91
210
for mut foreign_item in std:: mem:: take ( & mut foreign_mod. items ) {
92
- let mut attr_set = partition_attributes ( & foreign_mod. attrs ) ;
93
- let inner_attrs = match & mut foreign_item {
94
- ForeignItem :: Fn ( f) => & mut f. attrs ,
95
- ForeignItem :: Static ( s) => & mut s. attrs ,
96
- ForeignItem :: Type ( t) => & mut t. attrs ,
97
- ForeignItem :: Macro ( m) => & mut m. attrs ,
98
- _ => & mut Vec :: new ( ) ,
99
- } ;
100
-
101
- let inner_attr_set = partition_attributes ( inner_attrs) ;
102
- attr_set. other_attrs . extend ( inner_attr_set. other_attrs ) ;
103
- attr_set. cfg_attrs . extend ( inner_attr_set. cfg_attrs ) ;
104
- attr_set. cc_attrs . extend ( inner_attr_set. cc_attrs ) ;
211
+ let ( inner_attrs, inner_unsafety, inner_abi) =
212
+ match & mut foreign_item {
213
+ ForeignItem :: Fn ( f) => {
214
+ ( & mut f. attrs , f. sig . unsafety , f. sig . abi . clone ( ) )
215
+ }
216
+ ForeignItem :: Static ( s) => ( & mut s. attrs , None , None ) ,
217
+ ForeignItem :: Type ( t) => ( & mut t. attrs , None , None ) ,
218
+ ForeignItem :: Macro ( m) => ( & mut m. attrs , None , None ) ,
219
+ _ => ( & mut Vec :: new ( ) , None , None ) ,
220
+ } ;
221
+
222
+ let mut attr_set = AttributeSet :: new (
223
+ & foreign_mod. attrs ,
224
+ foreign_mod. unsafety ,
225
+ Some ( foreign_mod. abi . clone ( ) ) ,
226
+ ) ;
227
+ attr_set. extend ( inner_attrs, inner_unsafety, inner_abi) ;
105
228
* inner_attrs = attr_set. other_attrs . iter ( ) . cloned ( ) . collect ( ) ;
106
229
107
230
self . insert_item_into_mod (
@@ -113,70 +236,17 @@ impl Visitor {
113
236
114
237
fn insert_item_into_mod ( & mut self , attr_set : AttributeSet , item : Item ) {
115
238
if !attr_set. cfg_attrs . is_empty ( ) || !attr_set. cc_attrs . is_empty ( ) {
116
- let ( _, items) = self . synthetic_mods
117
- . entry ( attr_set. ident ( ) )
118
- . or_insert_with ( || ( attr_set, Vec :: new ( ) ) ) ;
119
- items. push ( item) ;
239
+ let ident = attr_set. ident ( ) ;
240
+ let synthetic_mod = self
241
+ . synthetic_mods
242
+ . entry ( ident)
243
+ . or_insert_with ( || SyntheticMod :: new ( attr_set. clone ( ) ) ) ;
244
+ synthetic_mod. items . push ( item) ;
245
+ synthetic_mod. unsafety = attr_set. unsafety ;
246
+ synthetic_mod. abi = attr_set. abi . clone ( ) ;
247
+ synthetic_mod. attrs = attr_set;
120
248
} else {
121
249
self . new_items . push ( item) ;
122
250
}
123
251
}
124
252
}
125
-
126
- #[ derive( Default ) ]
127
- struct AttributeSet {
128
- cfg_attrs : HashSet < Attribute > ,
129
- cc_attrs : HashSet < Attribute > ,
130
- other_attrs : HashSet < Attribute > ,
131
- }
132
-
133
- impl AttributeSet {
134
- fn ident ( & self ) -> Ident {
135
- assert ! ( !self . cfg_attrs. is_empty( ) || !self . cc_attrs. is_empty( ) ) ;
136
-
137
- Ident :: new (
138
- self . cfg_attrs
139
- . iter ( )
140
- . chain ( self . cc_attrs . iter ( ) )
141
- . map ( |attr| attr. to_token_stream ( ) . to_string ( ) )
142
- . sorted ( )
143
- . map ( |s| {
144
- s. replace ( '=' , "_eq_" ) . replace (
145
- |c : char | !c. is_alphanumeric ( ) && c != '_' ,
146
- "_" ,
147
- )
148
- } )
149
- . join ( "_" )
150
- . chars ( )
151
- . coalesce ( |a, b| {
152
- if a == '_' && b == '_' {
153
- Ok ( a)
154
- } else {
155
- Err ( ( a, b) )
156
- }
157
- } )
158
- . collect :: < String > ( )
159
- . trim_matches ( '_' ) ,
160
- Span :: call_site ( ) ,
161
- )
162
- }
163
- }
164
-
165
- fn partition_attributes ( attrs : & [ Attribute ] ) -> AttributeSet {
166
- let mut attribute_set = AttributeSet :: default ( ) ;
167
-
168
- for attr in attrs {
169
- let target_set = if let Some ( ident) = attr. path ( ) . get_ident ( ) {
170
- match ident. to_string ( ) . as_str ( ) {
171
- "cfg" => & mut attribute_set. cfg_attrs ,
172
- "link" => & mut attribute_set. cc_attrs ,
173
- _ => & mut attribute_set. other_attrs ,
174
- }
175
- } else {
176
- & mut attribute_set. other_attrs
177
- } ;
178
- target_set. insert ( attr. clone ( ) ) ;
179
- }
180
-
181
- attribute_set
182
- }
0 commit comments