Path: blob/main/Tools/c-analyzer/c_analyzer/analyze.py
12 views
from c_parser.info import (1KIND,2TypeDeclaration,3POTSType,4FuncPtr,5)6from c_parser.match import (7is_pots,8is_funcptr,9)10from .info import (11IGNORED,12UNKNOWN,13SystemType,14)15from .match import (16is_system_type,17)181920def get_typespecs(typedecls):21typespecs = {}22for decl in typedecls:23if decl.shortkey not in typespecs:24typespecs[decl.shortkey] = [decl]25else:26typespecs[decl.shortkey].append(decl)27return typespecs282930def analyze_decl(decl, typespecs, knowntypespecs, types, knowntypes, *,31analyze_resolved=None):32resolved = resolve_decl(decl, typespecs, knowntypespecs, types)33if resolved is None:34# The decl is supposed to be skipped or ignored.35return None36if analyze_resolved is None:37return resolved, None38return analyze_resolved(resolved, decl, types, knowntypes)3940# This alias helps us avoid name collisions.41_analyze_decl = analyze_decl424344def analyze_type_decls(types, analyze_decl, handle_unresolved=True):45unresolved = set(types)46while unresolved:47updated = []48for decl in unresolved:49resolved = analyze_decl(decl)50if resolved is None:51# The decl should be skipped or ignored.52types[decl] = IGNORED53updated.append(decl)54continue55typedeps, _ = resolved56if typedeps is None:57raise NotImplementedError(decl)58if UNKNOWN in typedeps:59# At least one dependency is unknown, so this decl60# is not resolvable.61types[decl] = UNKNOWN62updated.append(decl)63continue64if None in typedeps:65# XXX66# Handle direct recursive types first.67nonrecursive = 168if decl.kind is KIND.STRUCT or decl.kind is KIND.UNION:69nonrecursive = 070i = 071for member, dep in zip(decl.members, typedeps):72if dep is None:73if member.vartype.typespec != decl.shortkey:74nonrecursive += 175else:76typedeps[i] = decl77i += 178if nonrecursive:79# We don't have all dependencies resolved yet.80continue81types[decl] = resolved82updated.append(decl)83if updated:84for decl in updated:85unresolved.remove(decl)86else:87# XXX88# Handle indirect recursive types.89...90# We couldn't resolve the rest.91# Let the caller deal with it!92break93if unresolved and handle_unresolved:94if handle_unresolved is True:95handle_unresolved = _handle_unresolved96handle_unresolved(unresolved, types, analyze_decl)979899def resolve_decl(decl, typespecs, knowntypespecs, types):100if decl.kind is KIND.ENUM:101typedeps = []102else:103if decl.kind is KIND.VARIABLE:104vartypes = [decl.vartype]105elif decl.kind is KIND.FUNCTION:106vartypes = [decl.signature.returntype]107elif decl.kind is KIND.TYPEDEF:108vartypes = [decl.vartype]109elif decl.kind is KIND.STRUCT or decl.kind is KIND.UNION:110vartypes = [m.vartype for m in decl.members]111else:112# Skip this one!113return None114115typedeps = []116for vartype in vartypes:117typespec = vartype.typespec118if is_pots(typespec):119typedecl = POTSType(typespec)120elif is_system_type(typespec):121typedecl = SystemType(typespec)122elif is_funcptr(vartype):123typedecl = FuncPtr(vartype)124else:125typedecl = find_typedecl(decl, typespec, typespecs)126if typedecl is None:127typedecl = find_typedecl(decl, typespec, knowntypespecs)128elif not isinstance(typedecl, TypeDeclaration):129raise NotImplementedError(repr(typedecl))130if typedecl is None:131# We couldn't find it!132typedecl = UNKNOWN133elif typedecl not in types:134# XXX How can this happen?135typedecl = UNKNOWN136elif types[typedecl] is UNKNOWN:137typedecl = UNKNOWN138elif types[typedecl] is IGNORED:139# We don't care if it didn't resolve.140pass141elif types[typedecl] is None:142# The typedecl for the typespec hasn't been resolved yet.143typedecl = None144typedeps.append(typedecl)145return typedeps146147148def find_typedecl(decl, typespec, typespecs):149specdecls = typespecs.get(typespec)150if not specdecls:151return None152153filename = decl.filename154155if len(specdecls) == 1:156typedecl, = specdecls157if '-' in typespec and typedecl.filename != filename:158# Inlined types are always in the same file.159return None160return typedecl161162# Decide which one to return.163candidates = []164samefile = None165for typedecl in specdecls:166type_filename = typedecl.filename167if type_filename == filename:168if samefile is not None:169# We expect type names to be unique in a file.170raise NotImplementedError((decl, samefile, typedecl))171samefile = typedecl172elif filename.endswith('.c') and not type_filename.endswith('.h'):173# If the decl is in a source file then we expect the174# type to be in the same file or in a header file.175continue176candidates.append(typedecl)177if not candidates:178return None179elif len(candidates) == 1:180winner, = candidates181# XXX Check for inline?182elif '-' in typespec:183# Inlined types are always in the same file.184winner = samefile185elif samefile is not None:186# Favor types in the same file.187winner = samefile188else:189# We don't know which to return.190raise NotImplementedError((decl, candidates))191192return winner193194195#############################196# handling unresolved decls197198class Skipped(TypeDeclaration):199def __init__(self):200_file = _name = _data = _parent = None201super().__init__(_file, _name, _data, _parent, _shortkey='<skipped>')202_SKIPPED = Skipped()203del Skipped204205206def _handle_unresolved(unresolved, types, analyze_decl):207#raise NotImplementedError(unresolved)208209dump = True210dump = False211if dump:212print()213for decl in types: # Preserve the original order.214if decl not in unresolved:215assert types[decl] is not None, decl216if types[decl] in (UNKNOWN, IGNORED):217unresolved.add(decl)218if dump:219_dump_unresolved(decl, types, analyze_decl)220print()221else:222assert types[decl][0] is not None, (decl, types[decl])223assert None not in types[decl][0], (decl, types[decl])224else:225assert types[decl] is None226if dump:227_dump_unresolved(decl, types, analyze_decl)228print()229#raise NotImplementedError230231for decl in unresolved:232types[decl] = ([_SKIPPED], None)233234for decl in types:235assert types[decl]236237238def _dump_unresolved(decl, types, analyze_decl):239if isinstance(decl, str):240typespec = decl241decl, = (d for d in types if d.shortkey == typespec)242elif type(decl) is tuple:243filename, typespec = decl244if '-' in typespec:245found = [d for d in types246if d.shortkey == typespec and d.filename == filename]247#if not found:248# raise NotImplementedError(decl)249decl, = found250else:251found = [d for d in types if d.shortkey == typespec]252if not found:253print(f'*** {typespec} ???')254return255#raise NotImplementedError(decl)256else:257decl, = found258resolved = analyze_decl(decl)259if resolved:260typedeps, _ = resolved or (None, None)261262if decl.kind is KIND.STRUCT or decl.kind is KIND.UNION:263print(f'*** {decl.shortkey} {decl.filename}')264for member, mtype in zip(decl.members, typedeps):265typespec = member.vartype.typespec266if typespec == decl.shortkey:267print(f' ~~~~: {typespec:20} - {member!r}')268continue269status = None270if is_pots(typespec):271mtype = typespec272status = 'okay'273elif is_system_type(typespec):274mtype = typespec275status = 'okay'276elif mtype is None:277if '-' in member.vartype.typespec:278mtype, = [d for d in types279if d.shortkey == member.vartype.typespec280and d.filename == decl.filename]281else:282found = [d for d in types283if d.shortkey == typespec]284if not found:285print(f' ???: {typespec:20}')286continue287mtype, = found288if status is None:289status = 'okay' if types.get(mtype) else 'oops'290if mtype is _SKIPPED:291status = 'okay'292mtype = '<skipped>'293elif isinstance(mtype, FuncPtr):294status = 'okay'295mtype = str(mtype.vartype)296elif not isinstance(mtype, str):297if hasattr(mtype, 'vartype'):298if is_funcptr(mtype.vartype):299status = 'okay'300mtype = str(mtype).rpartition('(')[0].rstrip()301status = ' okay' if status == 'okay' else f'--> {status}'302print(f' {status}: {typespec:20} - {member!r} ({mtype})')303else:304print(f'*** {decl} ({decl.vartype!r})')305if decl.vartype.typespec.startswith('struct ') or is_funcptr(decl):306_dump_unresolved(307(decl.filename, decl.vartype.typespec),308types,309analyze_decl,310)311312313