• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

trolldbois / ctypeslib / 4773580042

pending completion
4773580042

push

github

Loic Jaquemet
Merge branch 'pull-98'

1617 of 1929 relevant lines covered (83.83%)

50.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.89
/ctypeslib/codegen/cursorhandler.py
1
"""Handler for Cursor nodes from the clang AST tree."""
2

3
import logging
4
import re
5

6
from clang.cindex import CursorKind, LinkageKind, TypeKind, TokenKind
7

8
from ctypeslib.codegen import typedesc, util
9
from ctypeslib.codegen.handler import ClangHandler
10
from ctypeslib.codegen.handler import CursorKindException
11
from ctypeslib.codegen.handler import DuplicateDefinitionException
12
from ctypeslib.codegen.handler import InvalidDefinitionError
13
from ctypeslib.codegen.util import log_entity
14

15
log = logging.getLogger('cursorhandler')
16

17

18
class CursorHandler(ClangHandler):
19
    """
20
    Factory objects that handles Cursor Kind and transform them into typedesc.
×
21

22
    # clang.cindex.CursorKind
23
    # Declarations: 1-39
24
    # Reference: 40-49
25
    # Invalids: 70-73
26
    # Expressions: 100-143
27
    # Statements: 200-231
28
    # Root Translation unit: 300
29
    # Attributes: 400-403
30
    # Preprocessing: 500-503
31
    """
32

33
    def __init__(self, parser):
48✔
34
        ClangHandler.__init__(self, parser)
48✔
35

36
    def parse_cursor(self, cursor):
48✔
37
        mth = getattr(self, cursor.kind.name)
48✔
38
        return mth(cursor)
48✔
39

40
    ##########################################################################
41
    ##### CursorKind handlers#######
42
    ##########################################################################
43

44
    ###########################################
45
    # ATTRIBUTES
46

47
    # @log_entity
48
    # def UNEXPOSED_ATTR(self, cursor):
49
    # FIXME: do we do something with these ?
50
    # parent = cursor.semantic_parent
51
    # print 'parent is',parent.displayname, parent.location, parent.extent
52
    # TODO until attr is exposed by clang:
53
    # readlines()[extent] .split(' ') | grep {inline,packed}
54
    #    return
55

56
    # @log_entity
57
    # def PACKED_ATTR(self, cursor):
58
    # FIXME: do we do something with these ?
59
    # parent = cursor.semantic_parent
60
    # print 'parent is',parent.displayname, parent.location, parent.extent
61
    # TODO until attr is exposed by clang:
62
    # readlines()[extent] .split(' ') | grep {inline,packed}
63

64
    #    return
65

66
    ################################
67
    # EXPRESSIONS handlers
68

69
    # clang does not expose some types for some expression.
70
    # Example: the type of token group in a Char_s or char variable.
71
    # Counter example: The type of integer literal to a (int) variable.
72
    @log_entity
48✔
73
    def UNEXPOSED_EXPR(self, cursor):  # noqa
48✔
74
        ret = []
48✔
75
        for child in cursor.get_children():
48✔
76
            ret.append(self.parse_cursor(child))
48✔
77
        if len(ret) == 1:
48✔
78
            return ret[0]
48✔
79
        return ret
80

81
    @log_entity
48✔
82
    def DECL_REF_EXPR(self, cursor):  # noqa
48✔
83
        return cursor.displayname
84

85
    @log_entity
48✔
86
    def INIT_LIST_EXPR(self, cursor):  # noqa
48✔
87
        """Returns a list of literal values."""
88
        values = [self.parse_cursor(child)
12✔
89
                  for child in list(cursor.get_children())]
12✔
90
        return values
12✔
91

92
    ################################
93
    # STATEMENTS handlers
94

95
    # Do not traverse into function bodies and other compound statements
96
    # now fixed by TranslationUnit.PARSE_SKIP_FUNCTION_BODIES
97
    COMPOUND_STMT = ClangHandler._do_nothing
12✔
98

99
    @log_entity
12✔
100
    def NAMESPACE(self, cursor):  # noqa
101
        for child in cursor.get_children():
12✔
102
            self.parse_cursor(child)  # FIXME, where is the starElement
12✔
103

104
    ################################
105
    # TYPE REFERENCES handlers
106

107
    @log_entity
12✔
108
    def TYPE_REF(self, cursor):  # noqa
109
        name = self.get_unique_name(cursor)
110
        if self.is_registered(name):
111
            return self.get_registered(name)
112
        # log.warning('TYPE_REF with no saved decl in self.all')
113
        # return None
114
        # Should probably never get here.
115
        # I'm a field. ?
116
        _definition = cursor.get_definition()
117
        if _definition is None:
118
            # log.warning('no definition in this type_ref ?')
119
            # code.interact(local=locals())
120
            # raise IOError('I doubt this case is possible')
121
            _definition = cursor.type.get_declaration()
122
        return None  # self.parse_cursor(_definition)
123

124
    ################################
125
    # DECLARATIONS handlers
126
    #
127
    # UNEXPOSED_DECL are unexposed by clang. Go through the node's children.
128
    # VAR_DECL are Variable declarations. Initialisation value(s) are collected
129
    #          within _get_var_decl_init_value
130
    #
131

132
    NO_DECL_FOUND = ClangHandler._do_nothing
12✔
133

134
    UNEXPOSED_DECL = ClangHandler._pass_through_children
12✔
135
    """Undexposed declaration. Go and see children. """
136

137
    @log_entity
60✔
138
    def ENUM_CONSTANT_DECL(self, cursor):  # noqa
48✔
139
        """Gets the enumeration values"""
140
        name = cursor.displayname
141
        value = cursor.enum_value
142
        pname = self.get_unique_name(cursor.semantic_parent)
143
        parent = self.get_registered(pname)
144
        obj = typedesc.EnumValue(name, value, parent)
145
        parent.add_value(obj)
146
        return obj
147

148
    @log_entity
149
    def ENUM_DECL(self, cursor):  # noqa
150
        """Gets the enumeration declaration."""
151
        name = self.get_unique_name(cursor)
60✔
152
        if self.is_registered(name):
60✔
153
            return self.get_registered(name)
×
154
        align = cursor.type.get_align()
60✔
155
        size = cursor.type.get_size()
60✔
156
        obj = self.register(name, typedesc.Enumeration(name, size, align))
60✔
157
        self.set_location(obj, cursor)
60✔
158
        self.set_comment(obj, cursor)
60✔
159
        # parse all children
160
        for child in cursor.get_children():
60✔
161
            self.parse_cursor(child)  # FIXME, where is the starElement
60✔
162
        return obj
60✔
163

164
    @log_entity
60✔
165
    def FUNCTION_DECL(self, cursor):  # noqa
48✔
166
        """Handles function declaration"""
167
        # FIXME to UT
168
        name = self.get_unique_name(cursor)
169
        if self.is_registered(name):
170
            return self.get_registered(name)
171
        returns = self.parse_cursor_type(cursor.type.get_result())
172
        attributes = []
173
        extern = False
174
        obj = typedesc.Function(name, returns, attributes, extern)
175
        for arg in cursor.get_arguments():
176
            arg_obj = self.parse_cursor(arg)
177
            # if arg_obj is None:
178
            #    code.interact(local=locals())
179
            obj.add_argument(arg_obj)
180
        # code.interact(local=locals())
181
        self.register(name, obj)
182
        self.set_location(obj, cursor)
183
        self.set_comment(obj, cursor)
184
        return obj
185

186
    @log_entity
187
    def PARM_DECL(self, cursor):  # noqa
188
        """Handles parameter declarations."""
189
        # try and get the type. If unexposed, The canonical type will work.
190
        _type = cursor.type
60✔
191
        _name = cursor.spelling
60✔
192
        if (self.is_array_type(_type) or
60✔
193
                self.is_fundamental_type(_type) or
60✔
194
                self.is_pointer_type(_type) or
60✔
195
                self.is_unexposed_type(_type)):
60✔
196
            _argtype = self.parse_cursor_type(_type)
60✔
197
        else:  # FIXME: Which UT/case ? size_t in stdio.h for example.
×
198
            _argtype_decl = _type.get_declaration()
60✔
199
            _argtype_name = self.get_unique_name(_argtype_decl)
60✔
200
            if not self.is_registered(_argtype_name):
60✔
201
                log.info('This param type is not declared: %s', _argtype_name)
×
202
                _argtype = self.parse_cursor_type(_type)
×
203
            else:
×
204
                _argtype = self.get_registered(_argtype_name)
60✔
205
        obj = typedesc.Argument(_name, _argtype)
60✔
206
        self.set_location(obj, cursor)
60✔
207
        self.set_comment(obj, cursor)
60✔
208
        return obj
60✔
209

210
    @log_entity
60✔
211
    def TYPEDEF_DECL(self, cursor):  # noqa
48✔
212
        """
213
        Handles typedef statements.
214
        Gets Type from cache if we known it. Add it to cache otherwise.
215
        # typedef of an enum
216
        """
217
        name = self.get_unique_name(cursor)
60✔
218
        # if the typedef is known, get it from cache
219
        if self.is_registered(name):
60✔
220
            return self.get_registered(name)
60✔
221
        # use the canonical type directly.
222
        _type = cursor.type.get_canonical()
60✔
223
        log.debug("TYPEDEF_DECL: name:%s", name)
60✔
224
        log.debug("TYPEDEF_DECL: typ.kind.displayname:%s", _type.kind)
60✔
225

226
        # For all types (array, fundament, pointer, others), get the type
227
        p_type = self.parse_cursor_type(_type)
60✔
228
        if not isinstance(p_type, typedesc.T):
60✔
229
            log.error(
230
                'Bad TYPEREF parsing in TYPEDEF_DECL: %s',
231
                _type.spelling)
232
            # import code
233
            # code.interact(local=locals())
234
            raise TypeError(
235
                'Bad TYPEREF parsing in TYPEDEF_DECL: %s' %
236
                _type.spelling)
237
        # register the type
238
        obj = self.register(name, typedesc.Typedef(name, p_type))
60✔
239
        self.set_location(obj, cursor)
60✔
240
        self.set_comment(obj, cursor)
60✔
241
        return obj
60✔
242

243
    @log_entity
60✔
244
    def VAR_DECL(self, cursor):  # noqa
48✔
245
        """Handles Variable declaration."""
246
        # get the name
247
        name = self.get_unique_name(cursor)
248
        log.debug('VAR_DECL: name: %s', name)
249
        # Check for a previous declaration in the register
250
        if self.is_registered(name):
251
            return self.get_registered(name)
252
        # get the typedesc object
253
        _type, extern = self._VAR_DECL_type(cursor)
254
        # transform the ctypes values into ctypeslib
255
        init_value = self._VAR_DECL_value(cursor, _type)
256
        # finished
257
        log.debug('VAR_DECL: _type:%s', _type.name)
258
        log.debug('VAR_DECL: _init:%s', init_value)
259
        log.debug('VAR_DECL: location:%s', getattr(cursor, 'location'))
260
        obj = self.register(name, typedesc.Variable(name, _type, init_value, extern))
261
        self.set_location(obj, cursor)
262
        self.set_comment(obj, cursor)
263
        return True
264

265
    def _VAR_DECL_type(self, cursor):  # noqa
266
        """Generates a typedesc object from a Variable declaration."""
267
        # Get the type
268
        _ctype = cursor.type.get_canonical()
60✔
269
        extern = cursor.linkage in (LinkageKind.EXTERNAL, LinkageKind.UNIQUE_EXTERNAL)  # noqa
60✔
270
        log.debug('VAR_DECL: _ctype: %s ', _ctype.kind)
60✔
271
        # FIXME: Need working int128, long_double, etc.
272
        if self.is_fundamental_type(_ctype):
60✔
273
            ctypesname = self.get_ctypes_name(_ctype.kind)
60✔
274
            _type = typedesc.FundamentalType(ctypesname, 0, 0)
60✔
275
        elif self.is_unexposed_type(_ctype):
60✔
276
            st = 'PATCH NEEDED: %s type is not exposed by clang' % (
277
                self.get_unique_name(cursor))
278
            log.error(st)
×
279
            raise RuntimeError(st)
×
280
        elif self.is_array_type(_ctype) or _ctype.kind == TypeKind.RECORD:  # noqa
60✔
281
            _type = self.parse_cursor_type(_ctype)
60✔
282
        elif self.is_pointer_type(_ctype):
60✔
283
            # for example, extern Function pointer
284
            if self.is_unexposed_type(_ctype.get_pointee()):
60✔
285
                _type = self.parse_cursor_type(
286
                    _ctype.get_canonical().get_pointee())
287
            elif _ctype.get_pointee().kind == TypeKind.FUNCTIONPROTO:  # noqa
60✔
288
                # Function pointers
289
                # Arguments are handled in here
290
                _type = self.parse_cursor_type(_ctype.get_pointee())
60✔
291
            else:  # Pointer to Fundamental types, structs....
×
292
                _type = self.parse_cursor_type(_ctype)
60✔
293
        else:
×
294
            # What else ?
295
            raise NotImplementedError(
60✔
296
                'What other type of variable? %s' %
60✔
297
                _ctype.kind)
60✔
298
        log.debug('VAR_DECL: _type: %s ', _type)
60✔
299
        return _type, extern
60✔
300

301
    def _VAR_DECL_value(self, cursor, _type):  # noqa
60✔
302
        """Handles Variable value initialization."""
303
        # always expect list [(k,v)] as init value.from list(cursor.get_children())
304
        # get the init_value and special cases
305
        init_value = self._get_var_decl_init_value(cursor.type,
306
                                                   list(cursor.get_children()))
307
        _ctype = cursor.type.get_canonical()
308
        if self.is_unexposed_type(_ctype):
309
            # string are not exposed
310
            init_value = '%s # UNEXPOSED TYPE. PATCH NEEDED.' % init_value
311
        elif (self.is_pointer_type(_ctype) and
312
                      _ctype.get_pointee().kind == TypeKind.FUNCTIONPROTO):  # noqa
313
            # Function pointers argument are handled at type creation time
314
            # but we need to put a CFUNCTYPE as a value of the name variable
315
            init_value = _type
316
        elif self.is_array_type(_ctype):
317
            # an integer literal will be the size
318
            # a string literal will be the value
319
            # any list member will be children of a init_list_expr
320
            # FIXME Move that code into typedesc
321
            def countof(k, _value):
322
                return [item[0] for item in _value].count(k)
323

324
            if countof(CursorKind.INIT_LIST_EXPR, init_value) == 1:  # noqa
325
                init_value = dict(init_value)[CursorKind.INIT_LIST_EXPR]  # noqa
326
            elif countof(CursorKind.STRING_LITERAL, init_value) == 1:  # noqa
327
                # we have a initialised c_array
328
                init_value = dict(init_value)[CursorKind.STRING_LITERAL]  # noqa
329
            else:
330
                # ignore size alone
331
                init_value = []
332
            # check the array size versus elements.
333
            if _type.size < len(init_value):
334
                _type.size = len(init_value)
335
        elif init_value == []:
336
            # catch case.
337
            init_value = None
338
        else:
339
            log.debug('VAR_DECL: default init_value: %s', init_value)
340
            if len(init_value) > 0:
341
                init_value = init_value[0][1]
342
        return init_value
343

344
    def _get_var_decl_init_value(self, _ctype, children):
345
        """
346
        Gathers initialisation values by parsing children nodes of a VAR_DECL.
×
347
        """
×
348

349
        # FIXME TU for INIT_LIST_EXPR
350
        # FIXME: always return [(child.kind,child.value),...]
351
        # FIXME: simplify this redondant code.
352
        init_value = []
60✔
353
        children = list(children)  # weird requirement, list iterator error.
60✔
354
        log.debug('_get_var_decl_init_value: children #: %d', len(children))
60✔
355
        for child in children:
60✔
356
            # early stop cases.
357
            _tmp = None
60✔
358
            try:
60✔
359
                _tmp = self._get_var_decl_init_value_single(_ctype, child)
60✔
360
            except CursorKindException:
60✔
361
                log.debug(
60✔
362
                    '_get_var_decl_init_value: children init value skip on %s',
60✔
363
                    child.kind)
60✔
364
                continue
60✔
365
            if _tmp is not None:
60✔
366
                init_value.append(_tmp)
60✔
367
        return init_value
60✔
368

369
    def _get_var_decl_init_value_single(self, _ctype, child):
60✔
370
        """
371
        Handling of a single child for initialization value.
372
        Accepted types are expressions and declarations
373
        """
374
        init_value = None
60✔
375
        # FIXME: always return (child.kind, child.value)
376
        log.debug(
60✔
377
            '_get_var_decl_init_value_single: _ctype: %s Child.kind: %s',
60✔
378
            _ctype.kind,
60✔
379
            child.kind)
60✔
380
        # shorcuts.
381
        if not child.kind.is_expression() and not child.kind.is_declaration():
60✔
382
            raise CursorKindException(child.kind)
60✔
383
        if child.kind == CursorKind.CALL_EXPR:  # noqa
60✔
384
            raise CursorKindException(child.kind)
×
385
        # POD init values handling.
386
        # As of clang 3.3, int, double literals are exposed.
387
        # float, long double, char , char* are not exposed directly in level1.
388
        # but really it depends...
389
        if child.kind.is_unexposed():
60✔
390
            # recurse until we find a literal kind
391
            init_value = self._get_var_decl_init_value(_ctype, child.get_children())
60✔
392
            if len(init_value) == 0:
60✔
393
                init_value = None
×
394
            elif len(init_value) == 1:
60✔
395
                init_value = init_value[0]
60✔
396
            else:
×
397
                log.error('_get_var_decl_init_value_single: Unhandled case')
×
398
                assert len(init_value) <= 1
×
399
        # elif child.kind == CursorKind.STRING_LITERAL:
400
        #     _v = self._literal_handling(child)
401
        #     init_value = (child.kind, _v)
402
        else:  # literal or others
×
403
            _v = self.parse_cursor(child)
60✔
404
            if isinstance(_v, list) and len(_v) > 0 and child.kind not in [CursorKind.INIT_LIST_EXPR, CursorKind.STRING_LITERAL]:  # noqa
60✔
405
                log.warning('_get_var_decl_init_value_single: TOKENIZATION BUG CHECK: %s', _v)
×
406
                _v = _v[0]
×
407
            init_value = (child.kind, _v)
60✔
408
        log.debug('_get_var_decl_init_value_single: returns %s', str(init_value))
60✔
409
        return init_value
60✔
410

411
    def _clean_string_literal(self, cursor, value):
60✔
412
        # strip wchar_t type prefix for string/character
413
        # indicatively: u8 for utf-8, u for utf-16, U for utf32
414
        # assume that the source file is utf-8
415
        # utf-32 not supported in 2.7, lets keep all in utf8
416
        # string prefixes https://en.cppreference.com/w/cpp/language/string_literal
417
        # integer suffixes https://en.cppreference.com/w/cpp/language/integer_literal
418
        if cursor.kind in [CursorKind.CHARACTER_LITERAL, CursorKind.STRING_LITERAL]:  # noqa
60✔
419
            # clean prefix
420
            value = re.sub(r'''^(L|u8|u|U)(R|"|')''', r'\2', value)
60✔
421
            # R for raw strings
422
            # we need to remove the raw-char-sequence prefix,suffix
423
            if value[0] == 'R':
60✔
424
                s = value[1:]
60✔
425
                # if there is no '(' in the 17 first char, its not valid
426
                offset = s[:17].index('(')
60✔
427
                delimiter = s[1:offset] # we skip the "
60✔
428
                value = s[offset + 1:-offset - 1]
60✔
429
                return value
60✔
430
            # we strip string delimiters
431
            return value[1:-1]
60✔
432
        elif cursor.kind == CursorKind.MACRO_INSTANTIATION:  # noqa
60✔
433
            # prefix = value[:3].split('"')[0]
434
            return value
×
435
        elif cursor.kind == CursorKind.MACRO_DEFINITION:  # noqa
60✔
436
            c = value[-1]
60✔
437
            if c in ['"', "'"]:
60✔
438
                value = re.sub('''^L%s''' % c , c, value)
60✔
439
            else:
×
440
                # unsigned int / long int / unsigned long int / long long int / unsigned long long int
441
                # this works and doesn't eat STRING values because no '"' is before $ in the regexp.
442
                # FIXME combinaisons of u/U, l/L, ll/LL, and combined, plus z/Z combined with u/U
443
                value = re.sub("(u|U|l|L|ul|UL|ll|LL|ull|ULL|z|Z|zu|ZU)$", "", value)
60✔
444
            return value
60✔
445
        else:
×
446
            pass
×
447
        return value
×
448

449
    @log_entity
60✔
450
    def _literal_handling(self, cursor):
48✔
451
        """Parse all literal associated with this cursor.
452

453
        Literal handling is usually useful only for initialization values.
454

455
        We can't use a shortcut by getting tokens
456
            # init_value = ' '.join([t.spelling for t in children[0].get_tokens()
457
            # if t.spelling != ';'])
458
        because some literal might need cleaning."""
459
        # FIXME #77, internal integer literal like __clang_major__ are not working here.
460
        # tokens == [] , because ??? clang problem ? so there is no spelling available.
461
        tokens = list(cursor.get_tokens())
60✔
462
        if cursor.kind == CursorKind.INTEGER_LITERAL and len(tokens) == 0:
60✔
463
            log.warning("INTEGER_LITERAL - clang provides no value - bug #77")
60✔
464
            # https://stackoverflow.com/questions/10692015/libclang-get-primitive-value
465
            # cursor.data[1] ?
466
            # import clang
467
            # # clang.cindex.conf.lib.clang_Cursor_Evaluate.restype = clang.cindex.conf.lib.CXEvalResult
468
            # evaluated = clang.cindex.conf.lib.clang_Cursor_Evaluate(cursor)
469
            # value = clang.cindex.conf.lib.clang_EvalResult_getAsInt(evaluated)
470
            # clang.cindex.conf.lib.clang_EvalResult_dispose(evaluated)
471

472
        log.debug('literal has %d tokens.[ %s ]', len(tokens), ' '.join([str(t.spelling) for t in tokens]))
60✔
473
        if len(tokens) == 1 and cursor.kind == CursorKind.STRING_LITERAL:  # noqa
60✔
474
            # use a shortcut that works for unicode
475
            value = tokens[0].spelling
60✔
476
            value = self._clean_string_literal(cursor, value)
60✔
477
            return value
60✔
478
        elif cursor.kind == CursorKind.STRING_LITERAL:  # noqa
60✔
479
            # use a shortcut - does not work on unicode var_decl
480
            value = cursor.displayname
60✔
481
            value = self._clean_string_literal(cursor, value)
60✔
482
            return value
60✔
483
        final_value = []
60✔
484
        # code.interact(local=locals())
485
        log.debug('cursor.type:%s', cursor.type.kind.name)
60✔
486
        for i, token in enumerate(tokens):
60✔
487
            value = token.spelling
60✔
488
            log.debug('token:%s tk.kd:%11s tk.cursor.kd:%15s cursor.kd:%15s',
60✔
489
                      token.spelling, token.kind.name, token.cursor.kind.name,
60✔
490
                      cursor.kind.name)
60✔
491
            # Punctuation is probably not part of the init_value,
492
            # but only in specific case: ';' endl, or part of list_expr
493
            if (token.kind == TokenKind.PUNCTUATION and  # noqa
60✔
494
                    (token.cursor.kind == CursorKind.INVALID_FILE or  # noqa
60✔
495
                             token.cursor.kind == CursorKind.INIT_LIST_EXPR)):  # noqa
60✔
496
                log.debug('IGNORE token %s', value)
×
497
                continue
×
498
            elif token.kind == TokenKind.COMMENT:  # noqa
60✔
499
                log.debug('Ignore comment %s', value)
×
500
                continue
×
501
            # elif token.cursor.kind == CursorKind.VAR_DECL:
502
            elif token.location not in cursor.extent:
60✔
503
                # log.debug('FIXME BUG: token.location not in cursor.extent %s', value)
504
                # 2021 clang 11, this seems fixed ?
505
                # there is most probably a BUG in clang or python-clang
506
                # when on #define with no value, a token is taken from
507
                # next line. Which break stuff.
508
                # example:
509
                #   #define A
510
                #   extern int i;
511
                # // this will give "extern" the last token of Macro("A")
512
                # Lexer is choking ?
513
                # FIXME BUG: token.location not in cursor.extent
514
                # code.interact(local=locals())
515
                continue
×
516
            # Cleanup specific c-lang or c++ prefix/suffix for POD types.
517
            if token.cursor.kind == CursorKind.INTEGER_LITERAL:  # noqa
60✔
518
                # strip type suffix for constants
519
                value = value.replace('L', '').replace('U', '')
60✔
520
                value = value.replace('l', '').replace('u', '')
60✔
521
                if value[:2] == '0x' or value[:2] == '0X':
60✔
522
                    value = '0x%s' % value[2:]  # "int(%s,16)"%(value)
60✔
523
                else:
×
524
                    value = int(value)
60✔
525
            elif token.cursor.kind == CursorKind.FLOATING_LITERAL:  # noqa
60✔
526
                # strip type suffix for constants
527
                value = value.replace('f', '').replace('F', '')
60✔
528
                value = float(value)
60✔
529
            elif (token.cursor.kind == CursorKind.CHARACTER_LITERAL or  # noqa
60✔
530
                          token.cursor.kind == CursorKind.STRING_LITERAL):  # noqa
60✔
531
                value = self._clean_string_literal(token.cursor, value)
60✔
532
            elif token.cursor.kind == CursorKind.MACRO_INSTANTIATION:  # noqa
60✔
533
                # get the macro value
534
                value = self.get_registered(value).body
×
535
                # already cleaned value = self._clean_string_literal(token.cursor, value)
536
            elif token.cursor.kind == CursorKind.MACRO_DEFINITION:  # noqa
60✔
537
                tk = token.kind
60✔
538
                if i == 0:
60✔
539
                    # ignore, macro name
540
                    pass
60✔
541
                elif token.kind == TokenKind.LITERAL:  # noqa
60✔
542
                    # and just clean it
543
                    value = self._clean_string_literal(token.cursor, value)
60✔
544
                elif token.kind == TokenKind.IDENTIFIER:  # noqa
60✔
545
                    # log.debug("Ignored MACRO_DEFINITION token identifier : %s", value)
546
                    # Identifier in Macro... Not sure what to do with that.
547
                    if self.is_registered(value):
60✔
548
                        # FIXME: if Macro is not a simple value replace, it should not be registered in the first place
549
                        # parse that, try to see if there is another Macro in there.
550
                        value = self.get_registered(value).body
60✔
551
                        log.debug("Found MACRO_DEFINITION token identifier : %s", value)
60✔
552
                    else:
×
553
                        value = typedesc.UndefinedIdentifier(value)
60✔
554
                        log.debug("Undefined MACRO_DEFINITION token identifier : %s", value)
60✔
555
                    pass
60✔
556
                elif token.kind == TokenKind.KEYWORD:  # noqa
60✔
557
                    log.debug("Got a MACRO_DEFINITION referencing a KEYWORD token.kind: %s", token.kind.name)
60✔
558
                    value = typedesc.UndefinedIdentifier(value)
60✔
559
                elif token.kind in [TokenKind.COMMENT, TokenKind.PUNCTUATION]:  # noqa
60✔
560
                    # log.debug("Ignored MACRO_DEFINITION token.kind: %s", token.kind.name)
561
                    pass
24✔
562

563
            # add token
564
            if value is not None:
60✔
565
                final_value.append(value)
60✔
566
        # return the EXPR
567
        # code.interact(local=locals())
568
        # FIXME, that will break. We need constant type return
569
        if len(final_value) == 1:
60✔
570
            return final_value[0]
60✔
571
        # Macro definition of a string using multiple macro
572
        if isinstance(final_value, list):
60✔
573
            if cursor.kind == CursorKind.STRING_LITERAL:  # noqa
60✔
574
                final_value = ''.join(final_value)
×
575
        log.debug('_literal_handling final_value: %s', final_value)
60✔
576
        return final_value
60✔
577

578
    INTEGER_LITERAL = _literal_handling
60✔
579
    FLOATING_LITERAL = _literal_handling
60✔
580
    IMAGINARY_LITERAL = _literal_handling
60✔
581
    STRING_LITERAL = _literal_handling
60✔
582
    CHARACTER_LITERAL = _literal_handling
60✔
583

584
    @log_entity
60✔
585
    def _operator_handling(self, cursor):
48✔
586
        """Returns a string with the literal that are part of the operation."""
587
        values = self._literal_handling(cursor)
588
        retval = ''.join([str(val) for val in values])
589
        log.debug('cursor.type.kind:%s', cursor.type.kind.name)
590
        if cursor.kind == CursorKind.UNARY_OPERATOR:  # noqa
591
            if cursor.type.kind in [TypeKind.INT, TypeKind.LONG]:  # noqa
592
                if '0x' in retval:
593
                    retval = int(retval, 16)
594
                else:
595
                    try:
596
                        retval = int(retval)
597
                    except ValueError:
598
                        # fall back on pass through
599
                        pass
600
            elif cursor.type.kind in [TypeKind.FLOAT, TypeKind.DOUBLE]:  # noqa
601
                retval = float(retval)
602
        # Things we do not want to do:
603
        # elif cursor.kind == CursorKind.BINARY_OPERATOR:
604
        #     # cursor.kind == binary_operator, then need to make some additions
605
        #     retval = eval(retval)
606

607
        return retval
608

609
    UNARY_OPERATOR = _operator_handling
610
    BINARY_OPERATOR = _operator_handling
611

612
    @log_entity
613
    def STRUCT_DECL(self, cursor, num=None):
614
        """
615
        Handles Structure declaration.
×
616
        Its a wrapper to _record_decl.
×
617
        """
×
618
        return self._record_decl(cursor, typedesc.Structure, num)
60✔
619

620
    @log_entity
60✔
621
    def UNION_DECL(self, cursor, num=None):
60✔
622
        """
623
        Handles Union declaration.
624
        Its a wrapper to _record_decl.
625
        """
626
        return self._record_decl(cursor, typedesc.Union, num)
60✔
627

628
    def _record_decl(self, cursor, _output_type, num=None):
60✔
629
        """
630
        Handles record type declaration.
631
        Structure, Union...
632
        """
633
        name = self.get_unique_name(cursor)
60✔
634
        # FIXME, handling anonymous field by adding a child id.
635
        if num is not None:
60✔
636
            name = "%s_%d", name, num
×
637
        # TODO unittest: try redefinition.
638
        # Find if a record definition was already parsed and registered
639
        if (self.is_registered(name) and
60✔
640
                    self.get_registered(name).members is not None):
60✔
641
            log.debug(
60✔
642
                '_record_decl: %s is already registered with members',
60✔
643
                name)
60✔
644
            return self.get_registered(name)
60✔
645
        # CPP bases
646
        bases = []
60✔
647
        for c in cursor.get_children():
60✔
648
            if c.kind == CursorKind.CXX_BASE_SPECIFIER:  # noqa
60✔
649
                bases.append(self.get_registered(self.get_unique_name(c)))
60✔
650
                log.debug("got base class %s", c.displayname)
60✔
651
        size = cursor.type.get_size()
60✔
652
        align = cursor.type.get_align()
60✔
653
        if size == -2: #
60✔
654
            # CXTypeLayoutError_Incomplete = -2
655
            # produce an empty structure declaration
656
            size = align = 0
60✔
657
            log.debug('_record_decl: name: %s CXTypeLayoutError_Incomplete', name)
60✔
658
            obj = _output_type(name, align, None, bases, size, packed=False)
60✔
659
            self.set_location(obj, cursor)
60✔
660
            self.set_comment(obj, cursor)
60✔
661
            return self.register(name, obj)
60✔
662

663
        elif size < 0 or align < 0:
60✔
664
            # CXTypeLayoutError_Invalid = -1,
665
            # CXTypeLayoutError_Dependent = -3,
666
            # CXTypeLayoutError_NotConstantSize = -4,
667
            # CXTypeLayoutError_InvalidFieldName = -5
668
            errs = dict([(-1, "Invalid"), (-3, "Dependent"),
×
669
                         (-4, "NotConstantSize"), (-5, "InvalidFieldName")])
×
670
            loc = "%s:%s" % (cursor.location.file, cursor.location.line)
×
671
            log.error('Structure %s is %s %s align:%d size:%d',
×
672
                      name, errs[size], loc, align, size)
×
673
            raise InvalidDefinitionError('Structure %s is %s %s align:%d size:%d',
×
674
                                         name, errs[size], loc, align, size)
×
675
        else:
×
676
            log.debug('_record_decl: name: %s size:%d', name, size)
60✔
677
        # Declaration vs Definition point
678
        # when a struct decl happen before the definition, we have no members
679
        # in the first declaration instance.
680
        obj = None
60✔
681
        if not self.is_registered(name):
60✔
682
            if not cursor.is_definition():
60✔
683
                # just save the spot, don't look at members == None
684
                log.debug('cursor %s is not on a definition', name)
60✔
685
                obj = _output_type(name, align, None, bases, size, packed=False)
60✔
686
                return self.register(name, obj)
60✔
687
            else:
×
688
                log.debug('cursor %s is a definition', name)
60✔
689
                # save the type in the registry. Useful for not looping in case of
690
                # members with forward references
691
                obj = _output_type(name, align, None, bases, size, packed=False)
60✔
692
                self.register(name, obj)
60✔
693
                self.set_location(obj, cursor)
60✔
694
                self.set_comment(obj, cursor)
60✔
695
                declared_instance = True
60✔
696
        else:
×
697
            obj = self.get_registered(name)
60✔
698
            declared_instance = False
60✔
699
        # capture members declaration
700
        members = []
60✔
701
        # Go and recurse through fields
702
        fields = list(cursor.type.get_fields())
60✔
703
        decl_f = [f.type.get_declaration() for f in fields]
60✔
704
        log.debug('Fields: %s',
60✔
705
                  str(['%s/%s' % (f.kind.name, f.spelling) for f in fields]))
60✔
706
        for field in fields:
60✔
707
            log.debug('creating FIELD_DECL for %s/%s', field.kind.name, field.spelling)
60✔
708
            members.append(self.FIELD_DECL(field))
60✔
709
        # FIXME BUG clang: anonymous structure field with only one anonymous field
710
        # is not a FIELD_DECL. does not appear in get_fields() !!!
711
        #
712
        # check for other stuff
713
        for child in cursor.get_children():
60✔
714
            if child in fields:
60✔
715
                continue
60✔
716
            elif child in decl_f:
60✔
717
                continue
60✔
718
            elif child.kind == CursorKind.PACKED_ATTR:  # noqa
60✔
719
                obj.packed = True
60✔
720
                log.debug('PACKED record')
60✔
721
                continue  # dont mess with field calculations
60✔
722
            else:  # could be others.... struct_decl, etc...
×
723
                log.debug(
60✔
724
                    'Unhandled field %s in record %s',
60✔
725
                    child.kind, name)
60✔
726
                continue
60✔
727
        log.debug('_record_decl: %d members', len(members))
60✔
728
        # by now, the type is registered.
729
        if not declared_instance:
60✔
730
            log.debug('_record_decl: %s was previously registered', name)
60✔
731
        obj = self.get_registered(name)
60✔
732
        obj.members = members
60✔
733
        # obj.packed = packed
734
        # final fixup
735
        self._fixup_record(obj)
60✔
736
        return obj
60✔
737

738
    def _fixup_record_bitfields_type(self, s):
60✔
739
        """Fix the bitfield packing issue for python ctypes, by changing the
740
        bitfield type, and respecting compiler alignement rules.
741

742
        This method should be called AFTER padding to have a perfect continuous
743
        layout.
744

745
        There is one very special case:
746
            struct bytes3 {
747
                unsigned int b1:23; // 0-23
748
                // 1 bit padding
749
                char a2; // 24-32
750
            };
751

752
        where we would need to actually put a2 in the int32 bitfield.
753

754
        We also need to change the member type to the smallest type possible
755
        that can contains the number of bits.
756
        Otherwise ctypes has strange bitfield rules packing stuff to the biggest
757
        type possible.
758

759
        ** but at the same time, if a bitfield member is from type x, we need to
760
        respect that
761
        """
762
        # phase 1, make bitfield, relying upon padding.
763
        bitfields = []
60✔
764
        bitfield_members = []
60✔
765
        current_bits = 0
60✔
766
        for m in s.members:
60✔
767
            if m.is_bitfield:
60✔
768
                bitfield_members.append(m)
60✔
769
                if m.is_padding:
60✔
770
                    # compiler says this ends the bitfield
771
                    size = current_bits
772
                    bitfields.append((size, bitfield_members))
773
                    bitfield_members = []
774
                    current_bits = 0
775
                else:
776
                    # size of padding is not included
777
                    current_bits += m.bits
60✔
778
            elif len(bitfield_members) == 0:
60✔
779
                # no opened bitfield
780
                continue
60✔
781
            else:
782
                # we reach the end of the bitfield. Make calculations.
783
                size = current_bits
60✔
784
                bitfields.append((size, bitfield_members))
60✔
785
                bitfield_members = []
60✔
786
                current_bits = 0
60✔
787
        if current_bits != 0:
60✔
788
            size = current_bits
60✔
789
            bitfields.append((size, bitfield_members))
60✔
790

791
        # compilers tend to reduce the size of the bitfield
792
        # to the bf_size
793
        # set the proper type name for the bitfield.
794
        for bf_size, members in bitfields:
60✔
795
            name = members[0].type.name
60✔
796
            pad_bits = 0
60✔
797
            if bf_size <= 8:  # use 1 byte - type = char
60✔
798
                # prep the padding bitfield size
799
                pad_bits = 8 - bf_size
60✔
800
            elif bf_size <= 16:  # use 2 byte
60✔
801
                pad_bits = 16 - bf_size
60✔
802
            elif bf_size <= 32:  # use 2 byte
60✔
803
                pad_bits = 32 - bf_size
60✔
804
            elif bf_size <= 64:  # use 2 byte
60✔
805
                name = 'c_uint64'  # also the 3 bytes + char thing
60✔
806
                pad_bits = 64 - bf_size
60✔
807
            else:
808
                name = 'c_uint64'
60✔
809
                pad_bits = bf_size % 64 - bf_size
60✔
810
            # change the type to harmonise the bitfield
811
            log.debug('_fixup_record_bitfield_size: fix type to %s', name)
60✔
812
            # set the whole bitfield to the appropriate type size.
813
            for m in members:
60✔
814
                m.type.name = name
60✔
815
                if m.is_padding:
60✔
816
                    # this is the last field.
817
                    # reduce the size of this padding field to the
818
                    m.bits = pad_bits
819
            # and remove padding if the size is 0
820
            if members[-1].is_padding and members[-1].bits == 0:
60✔
821
                s.members.remove(members[-1])
822

823
        # phase 2 - integrate the special 3 Bytes + char fix
824
        for bf_size, members in bitfields:
60✔
825
            if True or bf_size == 24:
60✔
826
                # we need to check for a 3bytes + char corner case
827
                m = members[-1]
60✔
828
                i = s.members.index(m)
60✔
829
                if len(s.members) > i + 1:
60✔
830
                    # has to exists, no arch is aligned on 24 bits.
831
                    next_member = s.members[i + 1]
60✔
832
                    if next_member.bits == 8:
60✔
833
                        # next_member field is a char.
834
                        # it will be aggregated in a 32 bits space
835
                        # we need to make it a member of 32bit bitfield
836
                        next_member.is_bitfield = True
60✔
837
                        next_member.comment = "Promoted to bitfield member and type (was char)"
60✔
838
                        next_member.type = m.type
60✔
839
                        log.info("%s.%s promoted to bitfield member and type", s.name, next_member.name)
60✔
840
                        continue
60✔
841
        #
842
        return
60✔
843

844
    def _fixup_record(self, s):
60✔
845
        """Fixup padding on a record"""
846
        log.debug('FIXUP_STRUCT: %s %d bits', s.name, s.size * 8)
847
        if s.members is None:
848
            log.debug('FIXUP_STRUCT: no members')
849
            s.members = []
850
            return
851
        if s.size == 0:
852
            log.debug('FIXUP_STRUCT: struct has size %d', s.size)
853
            return
854
        # try to fix bitfields without padding first
855
        self._fixup_record_bitfields_type(s)
856
        # No need to lookup members in a global var.
857
        # Just fix the padding
858
        members = []
859
        member = None
860
        offset = 0
861
        padding_nb = 0
862
        member = None
863
        prev_member = None
864
        # create padding fields
865
        # DEBUG FIXME: why are s.members already typedesc objet ?
866
        # fields = self.fields[s.name]
867
        for m in s.members:  # s.members are strings - NOT
868
            # we need to check total size of bitfield, so to choose the right
869
            # bitfield type
870
            member = m
871
            log.debug('Fixup_struct: Member:%s offsetbits:%d->%d expecting offset:%d',
872
                      member.name, member.offset, member.offset + member.bits, offset)
873
            if member.offset < 0:
874
                # FIXME INCOMPLETEARRAY (clang bindings?)
875
                # All fields have offset == -2. No padding will be done.
876
                # But the fields are ordered and code will be produces with typed info.
877
                # so in most cases, it will work. if there is a structure with incompletearray
878
                # and padding or alignement issue, it will produce wrong results
879
                # just exit
880
                return
881
            if member.offset > offset:
882
                # create padding
883
                length = member.offset - offset
884
                log.debug(
885
                    'Fixup_struct: create padding for %d bits %d bytes',
886
                    length, length // 8)
887
                padding_nb = self._make_padding(
888
                    members,
889
                    padding_nb,
890
                    offset,
891
                    length,
892
                    prev_member)
893
            if member.type is None:
894
                log.error('FIXUP_STRUCT: %s.type is None', member.name)
895
            members.append(member)
896
            offset = member.offset + member.bits
897
            prev_member = member
898
        # tail padding if necessary
899
        if s.size * 8 != offset:
900
            length = s.size * 8 - offset
901
            log.debug(
902
                'Fixup_struct: s:%d create tail padding for %d bits %d bytes',
903
                s.size, length, length // 8)
904
            padding_nb = self._make_padding(
905
                members,
906
                padding_nb,
907
                offset,
908
                length,
909
                prev_member)
910
        if len(members) > 0:
911
            offset = members[-1].offset + members[-1].bits
912
        # go
913
        s.members = members
914
        log.debug("FIXUP_STRUCT: size:%d offset:%d", s.size * 8, offset)
915
        # if member and not member.is_bitfield:
916
        ## self._fixup_record_bitfields_type(s)
917
        # , assert that the last field stop at the size limit
918
        assert offset == s.size * 8
919
        return
920

921
    _fixup_Structure = _fixup_record
922
    _fixup_Union = _fixup_record
923

924
    def _make_padding(
925
            self, members, padding_nb, offset, length, prev_member=None):
926
        """Make padding Fields for a specifed size."""
927
        name = 'PADDING_%d' % padding_nb
60✔
928
        padding_nb += 1
60✔
929
        log.debug("_make_padding: for %d bits", length)
60✔
930
        if (length % 8) != 0 or (prev_member is not None and prev_member.is_bitfield):
60✔
931
            if length > 32:
60✔
932
                typename = "c_uint64"
60✔
933
            elif length > 16:
60✔
934
                typename = "c_uint32"
60✔
935
            elif length > 8:
60✔
936
                typename = "c_uint16"
60✔
937
            else:
938
                typename = "c_uint8"
60✔
939
            padding = typedesc.Field(name,
60✔
940
                                     typedesc.FundamentalType(typename, 1, 1),
60✔
941
                                     offset, length, is_bitfield=True, is_padding=True)
60✔
942
            members.append(padding)
60✔
943
            return padding_nb
60✔
944
        elif length > 8:
60✔
945
            pad_bytes = length // 8
60✔
946
            padding = typedesc.Field(name,
60✔
947
                                     typedesc.ArrayType(
60✔
948
                                         typedesc.FundamentalType(
60✔
949
                                             self.get_ctypes_name(TypeKind.CHAR_U), length, 1),  # noqa
60✔
950
                                         pad_bytes),
60✔
951
                                     offset, length, is_padding=True)
60✔
952
            members.append(padding)
60✔
953
            return padding_nb
60✔
954
        # simple char padding
955
        padding = typedesc.Field(name,
60✔
956
                                 typedesc.FundamentalType(
60✔
957
                                     self.get_ctypes_name(
60✔
958
                                         TypeKind.CHAR_U),  # noqa
60✔
959
                                     1,
60✔
960
                                     1),
60✔
961
                                 offset, length, is_padding=True)
60✔
962
        members.append(padding)
60✔
963
        return padding_nb
60✔
964

965
    # FIXME
966
    CLASS_DECL = STRUCT_DECL
60✔
967
    _fixup_Class = _fixup_record
60✔
968

969
    # @log_entity DEBUG
970
    def FIELD_DECL(self, cursor):
60✔
971
        """
972
        Handles Field declarations.
973
        Some specific treatment for a bitfield.
974
        """
975
        # name, type
976
        parent = cursor.semantic_parent
60✔
977
        # field name:
978
        # either its cursor.spelling or it is an anonymous bitfield
979
        # we do NOT rely on get_unique_name for a bitfield name.
980
        # Anonymous Field:
981
        #    We have to create a name
982
        #    it will be the indice of the field (_0,_1,...)
983
        # offset of field:
984
        #    we will need it late. get the offset of the field in the record
985
        # Note: cursor.is_anonymous seems to be unreliable/inconsistent across
986
        # libclang versions, and we will consider the field as anonymous if
987
        # cursor.spelling is empty
988
        name = cursor.spelling
60✔
989
        offset = parent.type.get_offset(name)
60✔
990
        if not name and cursor.is_anonymous() and not cursor.is_bitfield():
60✔
991
            # anonymous type, that is not a bitfield field case:
992
            offset = cursor.get_field_offsetof()
60✔
993
            # name = self.get_unique_name(cursor)
994
            # we want to keep name empty if the field is unnamed.
995
        elif not name:
60✔
996
            # anonymous bitfield case:
997
            # get offset by iterating all fields of parent
998
            # corner case for anonymous fields
999
            # if offset == -5: use field.get_offset_of()
1000
            fieldnum = 0
60✔
1001
            offset = cursor.get_field_offsetof()
60✔
1002
            for i, _f in enumerate(parent.type.get_fields()):
60✔
1003
                if _f == cursor:
60✔
1004
                    fieldnum = i
60✔
1005
                    break
60✔
1006
            # make a name
1007
            if fieldnum == -1:
60✔
1008
                raise ValueError("Anonymous field was not found in get_fields()")
1009
            name = "_%d" % fieldnum
60✔
1010
            log.debug("FIELD_DECL: anonymous field renamed to %s", name)
60✔
1011
        # some debug
1012
        if offset < 0:
60✔
1013
            log.error('FIELD_DECL: BAD RECORD, Bad offset: %d for %s', offset, name)
1014
            # incomplete record definition, gives us an error here on fields.
1015
            # BUG clang bindings ?
1016
        # FIXME if c++ class ?
1017
        log.debug('FIELD_DECL: field offset is %d', offset)
60✔
1018

1019
        # bitfield checks
1020
        bits = None
60✔
1021
        if cursor.is_bitfield():
60✔
1022
            log.debug('FIELD_DECL: field is part of a bitfield')
60✔
1023
            bits = cursor.get_bitfield_width()
60✔
1024
        else:
1025
            bits = cursor.type.get_size() * 8
60✔
1026
            if bits < 0:
60✔
1027
                log.warning('Bad source code, bitsize == %d <0 on %s', bits, name)
60✔
1028
                bits = 0
60✔
1029
        log.debug('FIELD_DECL: field is %d bits', bits)
60✔
1030
        # try to get a representation of the type
1031
        # _canonical_type = cursor.type.get_canonical()
1032
        # t-t-t-t-
1033
        _type = None
60✔
1034
        _canonical_type = cursor.type.get_canonical()
60✔
1035
        _decl = cursor.type.get_declaration()
60✔
1036
        if (self.is_array_type(_canonical_type) or
60✔
1037
                self.is_fundamental_type(_canonical_type) or
60✔
1038
                self.is_pointer_type(_canonical_type)):
60✔
1039
            _type = self.parse_cursor_type(_canonical_type)
60✔
1040
        else:
1041
            children = list(cursor.get_children())
60✔
1042
            log.debug('FIELD_DECL: we now look for the declaration name.'
60✔
1043
                      'kind %s', _decl.kind)
60✔
1044
            if len(children) > 0 and _decl.kind == CursorKind.NO_DECL_FOUND:  # noqa
60✔
1045
                # constantarray of typedef of pointer , and other cases ?
1046
                _decl_name = self.get_unique_name(
1047
                    list(
1048
                        cursor.get_children())[0])
1049
            else:
1050
                # pass a field name to get a better name for an anonymous record, that is a named field
1051
                _decl_name = self.get_unique_name(_decl, field_name=name)
60✔
1052
            log.debug('FIELD_DECL: the declaration name %s', _decl_name)
60✔
1053
            # rename anonymous field type name
1054
            # 2015-06-26 handled in get_name
1055
            # if cursor.is_anonymous():
1056
            #    _decl_name += name
1057
            #    log.debug('FIELD_DECL: IS_ANONYMOUS the declaration name %s',_decl_name)
1058
            if self.is_registered(_decl_name):
60✔
1059
                log.debug(
60✔
1060
                    'FIELD_DECL: used type from cache: %s',
60✔
1061
                    _decl_name)
60✔
1062
                _type = self.get_registered(_decl_name)
60✔
1063
                # then we shortcut
1064
            else:
1065
                # is it always the case ?
1066
                log.debug("FIELD_DECL: name:'%s'", _decl_name)
60✔
1067
                log.debug("FIELD_DECL: %s: nb children:%s", cursor.type.kind,
60✔
1068
                          len(children))
60✔
1069
                # recurse into the right function
1070
                _type = self.parse_cursor_type(_canonical_type)
60✔
1071
                if _type is None:
60✔
1072
                    log.warning("Field %s is an %s type - ignoring field type",
×
1073
                                name, _canonical_type.kind.name)
×
1074
                    return None
×
1075
        if cursor.is_anonymous():
60✔
1076
            # we have to unregister the _type and register an alternate named
1077
            # type.
1078
            self.parser.remove_registered(_type.name)
60✔
1079
            _type.name = _decl_name
60✔
1080
            self.register(_decl_name, _type)
60✔
1081
        return typedesc.Field(name, _type, offset, bits,
60✔
1082
                              is_bitfield=cursor.is_bitfield(),
60✔
1083
                              is_anonymous=cursor.is_anonymous())
60✔
1084

1085
    #############################
1086
    # PREPROCESSING
1087

1088
    @log_entity
60✔
1089
    def MACRO_DEFINITION(self, cursor):
48✔
1090
        """
1091
        Parse MACRO_DEFINITION, only present if the TranslationUnit is
1092
        used with TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD.
1093
        By default, macro are not parsed. requires -k m || parser.activate_macros_parsing()
1094
        """
1095
        # macro parsing takes a LOT of time.
1096
        # ignore system macro
1097
        if (not hasattr(cursor, 'location') or cursor.location is None or
60✔
1098
                cursor.location.file is None):
60✔
1099
            # keep track of sizes from clang directly
1100
            # but we already did that elsewhere in clangparser.py make_ctypes_convertor
1101
            # if cursor.displayname.startswith('__SIZEOF_'):
1102
            #     typ = cursor.displayname[len('__SIZEOF_'):-2]
1103
            #     self.__sizeof[typ] = list(cursor.get_tokens())[1].spelling
1104
            return False
60✔
1105
        name = self.get_unique_name(cursor)
60✔
1106
        # MACRO_DEFINITION are a list of Tokens
1107
        # .kind = {IDENTIFIER, KEYWORD, LITERAL, PUNCTUATION, COMMENT ? }
1108
        comment = None
60✔
1109
        tokens = self._literal_handling(cursor)
60✔
1110
        # Macro name is tokens[0]
1111
        # get Macro value(s)
1112
        value = True
60✔
1113
        # args should be filled when () are in tokens,
1114
        args = None
60✔
1115
        if isinstance(tokens, list):
60✔
1116
            # TODO, if there is an UndefinedIdentifier, we need to scrap the whole thing to comments.
1117
            # unknowns = [_ for _ in tokens if isinstance(_, typedesc.UndefinedIdentifier)]
1118
            # if len(unknowns) > 0:
1119
            #     value = tokens
1120
            # elif len(tokens) == 2:
1121
            if len(tokens) == 2:
60✔
1122
                # #define key value
1123
                value = tokens[1]
60✔
1124
            elif len(tokens) == 3 and tokens[1] == '-':
60✔
1125
                value = ''.join(tokens[1:])
60✔
1126
            elif tokens[1] == '(':
60✔
1127
                # #107, differentiate between function-like macro and expression in ()
1128
                # valid tokens for us are are '()[0-9],.e' and terminating LluU
1129
                if any(filter(lambda x: isinstance(x, typedesc.UndefinedIdentifier), tokens)):
60✔
1130
                    # function macro or an expression.
1131
                    str_tokens = [str(_) for _ in tokens[1:tokens.index(')')+1]]
60✔
1132
                    args = ''.join(str_tokens).replace(',', ', ')
60✔
1133
                    str_tokens = [str(_) for _ in tokens[tokens.index(')')+1:]]
60✔
1134
                    value = ''.join(str_tokens)
60✔
1135
                else:
×
1136
                    value = ''.join((str(_) for _ in tokens[1:tokens.index(')') + 1]))
60✔
1137
            elif len(tokens) > 2:
60✔
1138
                # #define key a b c
1139
                value = list(tokens[1:])
60✔
1140
            else:
×
1141
                # FIXME no reach ?!
1142
                # just merge the list of tokens
1143
                value = ' '.join(tokens[1:])
×
1144
        elif isinstance(tokens, str):
60✔
1145
            # #define only
1146
            value = True
60✔
1147
        # macro comment maybe in tokens. Not in cursor.raw_comment
1148
        for t in cursor.get_tokens():
60✔
1149
            if t.kind == TokenKind.COMMENT:  ## noqa
60✔
1150
                comment = t.spelling
×
1151
        # special case. internal __null or __thread
1152
        # FIXME, there are probable a lot of others.
1153
        # why not Cursor.kind GNU_NULL_EXPR child instead of a token ?
1154
        if name in ['NULL', '__thread'] or value in ['__null', '__thread']:
60✔
1155
            value = None
60✔
1156
        log.debug('MACRO: #define %s%s %s', name, args or '', value)
60✔
1157
        obj = typedesc.Macro(name, args, value)
60✔
1158
        try:
60✔
1159
            self.register(name, obj)
60✔
1160
        except DuplicateDefinitionException:
60✔
1161
            log.info('Redefinition of %s %s->%s', name, self.parser.all[name].args, value)
60✔
1162
            # HACK
1163
            self.parser.all[name] = obj
60✔
1164
        self.set_location(obj, cursor)
60✔
1165
        # set the comment in the obj
1166
        obj.comment = comment
60✔
1167
        return True
60✔
1168

1169
    @log_entity
60✔
1170
    def MACRO_INSTANTIATION(self, cursor):
48✔
1171
        """We could use this to count instantiations
1172
        so we now, if we need to generate python code or comment for this macro ? """
1173
        log.debug('cursor.spelling: %s', cursor.spelling)
60✔
1174
        # log.debug('cursor.kind: %s', cursor.kind.name)
1175
        # log.debug('cursor.type.kind: %s', cursor.type.kind.name)
1176
        # # no children ?
1177
        # for child in cursor.get_children():
1178
        #     log.debug('child.spelling: %s', child.spelling)
1179
        #     log.debug('child.kind: %s', child.kind.name)
1180
        #     log.debug('child.type.kind: %s', child.type.kind.name)
1181
        #
1182
        # for token in cursor.get_tokens():
1183
        #     log.debug('token.spelling: %s', token.spelling)
1184
        #     log.debug('token.kind: %s', token.kind.name)
1185
        #     #log.debug('token.type.kind: %s', token.type.kind.name)
1186

1187
        # ret.append(self.parse_cursor(child))
1188
        # log.debug('cursor.type:%s', cursor.type.kind.name)
1189
        # self.set_location(obj, cursor)
1190
        # set the comment in the obj
1191
        # obj.comment = comment
1192
        return True
60✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc