summaryrefslogtreecommitdiffstats
path: root/Tools/c-analyzer/c_parser/match.py
blob: 3b5068fd11b685dff188f6d0f369fe20bb1f6d59 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import re

from . import info as _info
from .parser._regexes import SIMPLE_TYPE


_KIND = _info.KIND


def match_storage(decl, expected):
    default = _info.get_default_storage(decl)
    #assert default
    if expected is None:
        expected = {default}
    elif isinstance(expected, str):
        expected = {expected or default}
    elif not expected:
        expected = _info.STORAGE
    else:
        expected = {v or default for v in expected}
    storage = _info.get_effective_storage(decl, default=default)
    return storage in expected


##################################
# decl matchers

def is_type_decl(item):
    return _KIND.is_type_decl(item.kind)


def is_decl(item):
    return _KIND.is_decl(item.kind)


def is_pots(typespec, *,
            _regex=re.compile(rf'^{SIMPLE_TYPE}$', re.VERBOSE),
            ):

    if not typespec:
        return None
    if type(typespec) is not str:
        _, _, _, typespec, _ = _info.get_parsed_vartype(typespec)
    return _regex.match(typespec) is not None


def is_funcptr(vartype):
    if not vartype:
        return None
    _, _, _, _, abstract = _info.get_parsed_vartype(vartype)
    return _is_funcptr(abstract)


def _is_funcptr(declstr):
    if not declstr:
        return None
    # XXX Support "(<name>*)(".
    return '(*)(' in declstr.replace(' ', '')


def is_forward_decl(decl):
    if decl.kind is _KIND.TYPEDEF:
        return False
    elif is_type_decl(decl):
        return not decl.data
    elif decl.kind is _KIND.FUNCTION:
        # XXX This doesn't work with ParsedItem.
        return decl.signature.isforward
    elif decl.kind is _KIND.VARIABLE:
        # No var decls are considered forward (or all are...).
        return False
    else:
        raise NotImplementedError(decl)


def can_have_symbol(decl):
    return decl.kind in (_KIND.VARIABLE, _KIND.FUNCTION)


def has_external_symbol(decl):
    if not can_have_symbol(decl):
        return False
    if _info.get_effective_storage(decl) != 'extern':
        return False
    if decl.kind is _KIND.FUNCTION:
        return not decl.signature.isforward
    else:
        # It must be a variable, which can only be implicitly extern here.
        return decl.storage != 'extern'


def has_internal_symbol(decl):
    if not can_have_symbol(decl):
        return False
    return _info.get_actual_storage(decl) == 'static'


def is_external_reference(decl):
    if not can_have_symbol(decl):
        return False
    # We have to check the declared storage rather tnan the effective.
    if decl.storage != 'extern':
        return False
    if decl.kind is _KIND.FUNCTION:
        return decl.signature.isforward
    # Otherwise it's a variable.
    return True


def is_local_var(decl):
    if not decl.kind is _KIND.VARIABLE:
        return False
    return True if decl.parent else False


def is_global_var(decl):
    if not decl.kind is _KIND.VARIABLE:
        return False
    return False if decl.parent else True


##################################
# filtering with matchers

def filter_by_kind(items, kind):
    if kind == 'type':
        kinds = _KIND._TYPE_DECLS
    elif kind == 'decl':
        kinds = _KIND._TYPE_DECLS
    try:
        okay = kind in _KIND
    except TypeError:
        kinds = set(kind)
    else:
        kinds = {kind} if okay else set(kind)
    for item in items:
        if item.kind in kinds:
            yield item


##################################
# grouping with matchers

def group_by_category(decls, categories, *, ignore_non_match=True):
    collated = {}
    for decl in decls:
        # Matchers should be mutually exclusive.  (First match wins.)
        for category, match in categories.items():
            if match(decl):
                if category not in collated:
                    collated[category] = [decl]
                else:
                    collated[category].append(decl)
                break
        else:
            if not ignore_non_match:
                raise Exception(f'no match for {decl!r}')
    return collated


def group_by_kind(items):
    collated = {kind: [] for kind in _KIND}
    for item in items:
        try:
            collated[item.kind].append(item)
        except KeyError:
            raise ValueError(f'unsupported kind in {item!r}')
    return collated


def group_by_kinds(items):
    # Collate into kind groups (decl, type, etc.).
    collated = {_KIND.get_group(k): [] for k in _KIND}
    for item in items:
        group = _KIND.get_group(item.kind)
        collated[group].append(item)
    return collated