Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/cpython
Path: blob/main/Tools/c-analyzer/c_analyzer/analyze.py
12 views
1
from c_parser.info import (
2
KIND,
3
TypeDeclaration,
4
POTSType,
5
FuncPtr,
6
)
7
from c_parser.match import (
8
is_pots,
9
is_funcptr,
10
)
11
from .info import (
12
IGNORED,
13
UNKNOWN,
14
SystemType,
15
)
16
from .match import (
17
is_system_type,
18
)
19
20
21
def get_typespecs(typedecls):
22
typespecs = {}
23
for decl in typedecls:
24
if decl.shortkey not in typespecs:
25
typespecs[decl.shortkey] = [decl]
26
else:
27
typespecs[decl.shortkey].append(decl)
28
return typespecs
29
30
31
def analyze_decl(decl, typespecs, knowntypespecs, types, knowntypes, *,
32
analyze_resolved=None):
33
resolved = resolve_decl(decl, typespecs, knowntypespecs, types)
34
if resolved is None:
35
# The decl is supposed to be skipped or ignored.
36
return None
37
if analyze_resolved is None:
38
return resolved, None
39
return analyze_resolved(resolved, decl, types, knowntypes)
40
41
# This alias helps us avoid name collisions.
42
_analyze_decl = analyze_decl
43
44
45
def analyze_type_decls(types, analyze_decl, handle_unresolved=True):
46
unresolved = set(types)
47
while unresolved:
48
updated = []
49
for decl in unresolved:
50
resolved = analyze_decl(decl)
51
if resolved is None:
52
# The decl should be skipped or ignored.
53
types[decl] = IGNORED
54
updated.append(decl)
55
continue
56
typedeps, _ = resolved
57
if typedeps is None:
58
raise NotImplementedError(decl)
59
if UNKNOWN in typedeps:
60
# At least one dependency is unknown, so this decl
61
# is not resolvable.
62
types[decl] = UNKNOWN
63
updated.append(decl)
64
continue
65
if None in typedeps:
66
# XXX
67
# Handle direct recursive types first.
68
nonrecursive = 1
69
if decl.kind is KIND.STRUCT or decl.kind is KIND.UNION:
70
nonrecursive = 0
71
i = 0
72
for member, dep in zip(decl.members, typedeps):
73
if dep is None:
74
if member.vartype.typespec != decl.shortkey:
75
nonrecursive += 1
76
else:
77
typedeps[i] = decl
78
i += 1
79
if nonrecursive:
80
# We don't have all dependencies resolved yet.
81
continue
82
types[decl] = resolved
83
updated.append(decl)
84
if updated:
85
for decl in updated:
86
unresolved.remove(decl)
87
else:
88
# XXX
89
# Handle indirect recursive types.
90
...
91
# We couldn't resolve the rest.
92
# Let the caller deal with it!
93
break
94
if unresolved and handle_unresolved:
95
if handle_unresolved is True:
96
handle_unresolved = _handle_unresolved
97
handle_unresolved(unresolved, types, analyze_decl)
98
99
100
def resolve_decl(decl, typespecs, knowntypespecs, types):
101
if decl.kind is KIND.ENUM:
102
typedeps = []
103
else:
104
if decl.kind is KIND.VARIABLE:
105
vartypes = [decl.vartype]
106
elif decl.kind is KIND.FUNCTION:
107
vartypes = [decl.signature.returntype]
108
elif decl.kind is KIND.TYPEDEF:
109
vartypes = [decl.vartype]
110
elif decl.kind is KIND.STRUCT or decl.kind is KIND.UNION:
111
vartypes = [m.vartype for m in decl.members]
112
else:
113
# Skip this one!
114
return None
115
116
typedeps = []
117
for vartype in vartypes:
118
typespec = vartype.typespec
119
if is_pots(typespec):
120
typedecl = POTSType(typespec)
121
elif is_system_type(typespec):
122
typedecl = SystemType(typespec)
123
elif is_funcptr(vartype):
124
typedecl = FuncPtr(vartype)
125
else:
126
typedecl = find_typedecl(decl, typespec, typespecs)
127
if typedecl is None:
128
typedecl = find_typedecl(decl, typespec, knowntypespecs)
129
elif not isinstance(typedecl, TypeDeclaration):
130
raise NotImplementedError(repr(typedecl))
131
if typedecl is None:
132
# We couldn't find it!
133
typedecl = UNKNOWN
134
elif typedecl not in types:
135
# XXX How can this happen?
136
typedecl = UNKNOWN
137
elif types[typedecl] is UNKNOWN:
138
typedecl = UNKNOWN
139
elif types[typedecl] is IGNORED:
140
# We don't care if it didn't resolve.
141
pass
142
elif types[typedecl] is None:
143
# The typedecl for the typespec hasn't been resolved yet.
144
typedecl = None
145
typedeps.append(typedecl)
146
return typedeps
147
148
149
def find_typedecl(decl, typespec, typespecs):
150
specdecls = typespecs.get(typespec)
151
if not specdecls:
152
return None
153
154
filename = decl.filename
155
156
if len(specdecls) == 1:
157
typedecl, = specdecls
158
if '-' in typespec and typedecl.filename != filename:
159
# Inlined types are always in the same file.
160
return None
161
return typedecl
162
163
# Decide which one to return.
164
candidates = []
165
samefile = None
166
for typedecl in specdecls:
167
type_filename = typedecl.filename
168
if type_filename == filename:
169
if samefile is not None:
170
# We expect type names to be unique in a file.
171
raise NotImplementedError((decl, samefile, typedecl))
172
samefile = typedecl
173
elif filename.endswith('.c') and not type_filename.endswith('.h'):
174
# If the decl is in a source file then we expect the
175
# type to be in the same file or in a header file.
176
continue
177
candidates.append(typedecl)
178
if not candidates:
179
return None
180
elif len(candidates) == 1:
181
winner, = candidates
182
# XXX Check for inline?
183
elif '-' in typespec:
184
# Inlined types are always in the same file.
185
winner = samefile
186
elif samefile is not None:
187
# Favor types in the same file.
188
winner = samefile
189
else:
190
# We don't know which to return.
191
raise NotImplementedError((decl, candidates))
192
193
return winner
194
195
196
#############################
197
# handling unresolved decls
198
199
class Skipped(TypeDeclaration):
200
def __init__(self):
201
_file = _name = _data = _parent = None
202
super().__init__(_file, _name, _data, _parent, _shortkey='<skipped>')
203
_SKIPPED = Skipped()
204
del Skipped
205
206
207
def _handle_unresolved(unresolved, types, analyze_decl):
208
#raise NotImplementedError(unresolved)
209
210
dump = True
211
dump = False
212
if dump:
213
print()
214
for decl in types: # Preserve the original order.
215
if decl not in unresolved:
216
assert types[decl] is not None, decl
217
if types[decl] in (UNKNOWN, IGNORED):
218
unresolved.add(decl)
219
if dump:
220
_dump_unresolved(decl, types, analyze_decl)
221
print()
222
else:
223
assert types[decl][0] is not None, (decl, types[decl])
224
assert None not in types[decl][0], (decl, types[decl])
225
else:
226
assert types[decl] is None
227
if dump:
228
_dump_unresolved(decl, types, analyze_decl)
229
print()
230
#raise NotImplementedError
231
232
for decl in unresolved:
233
types[decl] = ([_SKIPPED], None)
234
235
for decl in types:
236
assert types[decl]
237
238
239
def _dump_unresolved(decl, types, analyze_decl):
240
if isinstance(decl, str):
241
typespec = decl
242
decl, = (d for d in types if d.shortkey == typespec)
243
elif type(decl) is tuple:
244
filename, typespec = decl
245
if '-' in typespec:
246
found = [d for d in types
247
if d.shortkey == typespec and d.filename == filename]
248
#if not found:
249
# raise NotImplementedError(decl)
250
decl, = found
251
else:
252
found = [d for d in types if d.shortkey == typespec]
253
if not found:
254
print(f'*** {typespec} ???')
255
return
256
#raise NotImplementedError(decl)
257
else:
258
decl, = found
259
resolved = analyze_decl(decl)
260
if resolved:
261
typedeps, _ = resolved or (None, None)
262
263
if decl.kind is KIND.STRUCT or decl.kind is KIND.UNION:
264
print(f'*** {decl.shortkey} {decl.filename}')
265
for member, mtype in zip(decl.members, typedeps):
266
typespec = member.vartype.typespec
267
if typespec == decl.shortkey:
268
print(f' ~~~~: {typespec:20} - {member!r}')
269
continue
270
status = None
271
if is_pots(typespec):
272
mtype = typespec
273
status = 'okay'
274
elif is_system_type(typespec):
275
mtype = typespec
276
status = 'okay'
277
elif mtype is None:
278
if '-' in member.vartype.typespec:
279
mtype, = [d for d in types
280
if d.shortkey == member.vartype.typespec
281
and d.filename == decl.filename]
282
else:
283
found = [d for d in types
284
if d.shortkey == typespec]
285
if not found:
286
print(f' ???: {typespec:20}')
287
continue
288
mtype, = found
289
if status is None:
290
status = 'okay' if types.get(mtype) else 'oops'
291
if mtype is _SKIPPED:
292
status = 'okay'
293
mtype = '<skipped>'
294
elif isinstance(mtype, FuncPtr):
295
status = 'okay'
296
mtype = str(mtype.vartype)
297
elif not isinstance(mtype, str):
298
if hasattr(mtype, 'vartype'):
299
if is_funcptr(mtype.vartype):
300
status = 'okay'
301
mtype = str(mtype).rpartition('(')[0].rstrip()
302
status = ' okay' if status == 'okay' else f'--> {status}'
303
print(f' {status}: {typespec:20} - {member!r} ({mtype})')
304
else:
305
print(f'*** {decl} ({decl.vartype!r})')
306
if decl.vartype.typespec.startswith('struct ') or is_funcptr(decl):
307
_dump_unresolved(
308
(decl.filename, decl.vartype.typespec),
309
types,
310
analyze_decl,
311
)
312
313