(root)/
Python-3.12.0/
Tools/
c-analyzer/
c_analyzer/
analyze.py
       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 ESC[4;38;5;81mSkipped(ESC[4;38;5;149mTypeDeclaration):
     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              )