@@ -8,6 +8,7 @@ use super::item::Item;
8
8
use super :: ty:: { FloatKind , TypeKind } ;
9
9
use crate :: callbacks:: MacroParsingBehavior ;
10
10
use crate :: clang;
11
+ use crate :: clang:: ClangToken ;
11
12
use crate :: parse:: {
12
13
ClangItemParser , ClangSubItemParser , ParseError , ParseResult ,
13
14
} ;
@@ -130,6 +131,75 @@ fn default_macro_constant_type(value: i64) -> IntKind {
130
131
}
131
132
}
132
133
134
+ /// Determines whether a set of tokens from a CXCursor_MacroDefinition
135
+ /// represent a function-like macro. If so, calls the func_macro callback
136
+ /// and returns `Err(ParseError::Continue)` to signal to skip further
137
+ /// processing. If conversion to UTF-8 fails (it is performed only where it
138
+ /// should be infallible), then `Err(ParseError::Continue)` is returned as well.
139
+ fn handle_function_macro (
140
+ cursor : & clang:: Cursor ,
141
+ tokens : & [ ClangToken ] ,
142
+ callbacks : & dyn crate :: callbacks:: ParseCallbacks ,
143
+ ) -> Result < ( ) , ParseError > {
144
+ fn is_abutting ( a : & ClangToken , b : & ClangToken ) -> bool {
145
+ unsafe {
146
+ clang_sys:: clang_equalLocations (
147
+ clang_sys:: clang_getRangeEnd ( a. extent ) ,
148
+ clang_sys:: clang_getRangeStart ( b. extent ) ,
149
+ ) != 0
150
+ }
151
+ }
152
+
153
+ let is_functional_macro =
154
+ // If we have libclang >= 3.9, we can use `is_macro_function_like()` and
155
+ // avoid checking for abutting tokens ourselves.
156
+ cursor. is_macro_function_like ( ) . unwrap_or_else ( || {
157
+ // If we cannot get a definitive answer from clang, we instead check
158
+ // for a parenthesis token immediately adjacent to (that is,
159
+ // abutting) the first token in the macro definition.
160
+ // TODO: Once we don't need the fallback check here, we can hoist
161
+ // the `is_macro_function_like` check into this function's caller,
162
+ // and thus avoid allocating the `tokens` vector for non-functional
163
+ // macros.
164
+ match tokens. get ( 0 ..2 ) {
165
+ Some ( [ a, b] ) => is_abutting ( & a, & b) && b. spelling ( ) == b"(" ,
166
+ _ => false ,
167
+ }
168
+ } ) ;
169
+
170
+ if !is_functional_macro {
171
+ return Ok ( ( ) ) ;
172
+ }
173
+
174
+ let is_closing_paren = |t : & ClangToken | {
175
+ // Test cheap token kind before comparing exact spellings.
176
+ t. kind == clang_sys:: CXToken_Punctuation && t. spelling ( ) == b")"
177
+ } ;
178
+ let boundary = tokens. iter ( ) . position ( is_closing_paren) ;
179
+
180
+ let mut spelled = tokens. iter ( ) . map ( ClangToken :: spelling) ;
181
+ // Add 1, to convert index to length.
182
+ let left = spelled
183
+ . by_ref ( )
184
+ . take ( boundary. ok_or ( ParseError :: Continue ) ? + 1 ) ;
185
+ let left = left. collect :: < Vec < _ > > ( ) . concat ( ) ;
186
+ let left = String :: from_utf8 ( left) . map_err ( |_| ParseError :: Continue ) ?;
187
+ let right = spelled;
188
+ // Drop last token with LLVM < 4.0, due to an LLVM bug.
189
+ //
190
+ // See:
191
+ // https://bugs.llvm.org//show_bug.cgi?id=9069
192
+ let len = match ( right. len ( ) , crate :: clang_version ( ) . parsed ) {
193
+ ( len, Some ( ( v, _) ) ) if len > 0 && v < 4 => len - 1 ,
194
+ ( len, _) => len,
195
+ } ;
196
+ let right: Vec < _ > = right. take ( len) . collect ( ) ;
197
+ callbacks. func_macro ( & left, & right) ;
198
+
199
+ // We handled the macro, skip future macro processing.
200
+ Err ( ParseError :: Continue )
201
+ }
202
+
133
203
impl ClangSubItemParser for Var {
134
204
fn parse (
135
205
cursor : clang:: Cursor ,
@@ -140,16 +210,20 @@ impl ClangSubItemParser for Var {
140
210
use clang_sys:: * ;
141
211
match cursor. kind ( ) {
142
212
CXCursor_MacroDefinition => {
213
+ let tokens: Vec < _ > = cursor. tokens ( ) . iter ( ) . collect ( ) ;
214
+
143
215
if let Some ( callbacks) = ctx. parse_callbacks ( ) {
144
216
match callbacks. will_parse_macro ( & cursor. spelling ( ) ) {
145
217
MacroParsingBehavior :: Ignore => {
146
218
return Err ( ParseError :: Continue ) ;
147
219
}
148
220
MacroParsingBehavior :: Default => { }
149
221
}
222
+
223
+ handle_function_macro ( & cursor, & tokens, callbacks) ?;
150
224
}
151
225
152
- let value = parse_macro ( ctx, & cursor ) ;
226
+ let value = parse_macro ( ctx, & tokens ) ;
153
227
154
228
let ( id, value) = match value {
155
229
Some ( v) => v,
@@ -316,11 +390,14 @@ impl ClangSubItemParser for Var {
316
390
/// Try and parse a macro using all the macros parsed until now.
317
391
fn parse_macro (
318
392
ctx : & BindgenContext ,
319
- cursor : & clang :: Cursor ,
393
+ tokens : & [ ClangToken ] ,
320
394
) -> Option < ( Vec < u8 > , cexpr:: expr:: EvalResult ) > {
321
395
use cexpr:: expr;
322
396
323
- let mut cexpr_tokens = cursor. cexpr_tokens ( ) ;
397
+ let mut cexpr_tokens: Vec < _ > = tokens
398
+ . iter ( )
399
+ . filter_map ( ClangToken :: as_cexpr_token)
400
+ . collect ( ) ;
324
401
325
402
let parser = expr:: IdentifierParser :: new ( ctx. parsed_macros ( ) ) ;
326
403
0 commit comments