1+ use crate :: HashMap ;
2+ use crate :: HashSet ;
3+ use itertools:: Itertools ;
14use proc_macro2:: Span ;
25use quote:: { quote, ToTokens } ;
6+ use syn:: token:: Unsafe ;
7+ use syn:: Abi ;
38use 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 ,
612} ;
7- use itertools:: Itertools ;
8- use crate :: HashMap ;
9- use crate :: HashSet ;
1013
1114pub fn merge_cfg_attributes ( file : & mut File ) {
1215 let mut visitor = Visitor :: new ( ) ;
1316 visitor. visit_file ( file) ;
1417}
1518
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+
16124struct Visitor {
17- synthetic_mods : HashMap < Ident , ( AttributeSet , Vec < Item > ) > ,
125+ synthetic_mods : HashMap < Ident , SyntheticMod > ,
18126 new_items : Vec < Item > ,
19127}
20128
@@ -29,28 +137,39 @@ impl Visitor {
29137 fn visit_file ( & mut self , file : & mut File ) {
30138 self . visit_items ( & mut file. items ) ;
31139
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 ( ) {
36153 quote ! {
37- #( #items) *
154+ #( #cfg_attrs) *
155+ pub mod #ident {
156+ #( #cc_attrs) *
157+ #unsafety #abi {
158+ #( #items) *
159+ }
160+ }
38161 }
39162 } else {
40- // TODO: Don't swallow abi/unsafety :(
41163 quote ! {
42- #( #cc_attrs ) *
43- unsafe extern "C" {
164+ #( #cfg_attrs ) *
165+ pub mod #ident {
44166 #( #items) *
45167 }
46168 }
47169 } ;
48170
49171 self . new_items . push ( Item :: Verbatim ( quote ! {
50- #( #cfg_attrs) *
51- pub mod #ident {
52- #block
53- }
172+ #synthetic_mod
54173
55174 #( #cfg_attrs) *
56175 pub use #ident:: * ;
@@ -63,17 +182,17 @@ impl Visitor {
63182 fn visit_items ( & mut self , items : & mut Vec < Item > ) {
64183 for mut item in std:: mem:: take ( items) {
65184 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 ) ;
77196 * attrs = attr_set. other_attrs . iter ( ) . cloned ( ) . collect ( ) ;
78197 self . insert_item_into_mod ( attr_set, item) ;
79198 }
@@ -89,19 +208,23 @@ impl Visitor {
89208
90209 fn visit_foreign_mod ( & mut self , foreign_mod : & mut ItemForeignMod ) {
91210 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) ;
105228 * inner_attrs = attr_set. other_attrs . iter ( ) . cloned ( ) . collect ( ) ;
106229
107230 self . insert_item_into_mod (
@@ -113,70 +236,17 @@ impl Visitor {
113236
114237 fn insert_item_into_mod ( & mut self , attr_set : AttributeSet , item : Item ) {
115238 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;
120248 } else {
121249 self . new_items . push ( item) ;
122250 }
123251 }
124252}
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