| import argparse |
| import re |
| import sys |
| |
| from c_analyzer.common import show |
| from c_analyzer.common.info import UNKNOWN |
| |
| from . import SOURCE_DIRS |
| from .find import supported_vars |
| from .known import ( |
| from_file as known_from_file, |
| DATA_FILE as KNOWN_FILE, |
| ) |
| from .supported import IGNORED_FILE |
| |
| |
| def _check_results(unknown, knownvars, used): |
| def _match_unused_global(variable): |
| found = [] |
| for varid in knownvars: |
| if varid in used: |
| continue |
| if varid.funcname is not None: |
| continue |
| if varid.name != variable.name: |
| continue |
| if variable.filename and variable.filename != UNKNOWN: |
| if variable.filename == varid.filename: |
| found.append(varid) |
| else: |
| found.append(varid) |
| return found |
| |
| badknown = set() |
| for variable in sorted(unknown): |
| msg = None |
| if variable.funcname != UNKNOWN: |
| msg = f'could not find global symbol {variable.id}' |
| elif m := _match_unused_global(variable): |
| assert isinstance(m, list) |
| badknown.update(m) |
| elif variable.name in ('completed', 'id'): # XXX Figure out where these variables are. |
| unknown.remove(variable) |
| else: |
| msg = f'could not find local symbol {variable.id}' |
| if msg: |
| #raise Exception(msg) |
| print(msg) |
| if badknown: |
| print('---') |
| print(f'{len(badknown)} globals in known.tsv, but may actually be local:') |
| for varid in sorted(badknown): |
| print(f'{varid.filename:30} {varid.name}') |
| unused = sorted(varid |
| for varid in set(knownvars) - used |
| if varid.name != 'id') # XXX Figure out where these variables are. |
| if unused: |
| print('---') |
| print(f'did not use {len(unused)} known vars:') |
| for varid in unused: |
| print(f'{varid.filename:30} {varid.funcname or "-":20} {varid.name}') |
| raise Exception('not all known symbols used') |
| if unknown: |
| print('---') |
| raise Exception('could not find all symbols') |
| |
| |
| # XXX Move this check to its own command. |
| def cmd_check_cache(cmd, *, |
| known=KNOWN_FILE, |
| ignored=IGNORED_FILE, |
| _known_from_file=known_from_file, |
| _find=supported_vars, |
| ): |
| known = _known_from_file(known) |
| |
| used = set() |
| unknown = set() |
| for var, supported in _find(known=known, ignored=ignored): |
| if supported is None: |
| unknown.add(var) |
| continue |
| used.add(var.id) |
| _check_results(unknown, known['variables'], used) |
| |
| |
| def cmd_check(cmd, *, |
| known=KNOWN_FILE, |
| ignored=IGNORED_FILE, |
| _find=supported_vars, |
| _show=show.basic, |
| _print=print, |
| ): |
| """ |
| Fail if there are unsupported globals variables. |
| |
| In the failure case, the list of unsupported variables |
| will be printed out. |
| """ |
| unsupported = [] |
| for var, supported in _find(known=known, ignored=ignored): |
| if not supported: |
| unsupported.append(var) |
| |
| if not unsupported: |
| #_print('okay') |
| return |
| |
| _print('ERROR: found unsupported global variables') |
| _print() |
| _show(sorted(unsupported)) |
| _print(f' ({len(unsupported)} total)') |
| sys.exit(1) |
| |
| |
| def cmd_show(cmd, *, |
| known=KNOWN_FILE, |
| ignored=IGNORED_FILE, |
| skip_objects=False, |
| _find=supported_vars, |
| _show=show.basic, |
| _print=print, |
| ): |
| """ |
| Print out the list of found global variables. |
| |
| The variables will be distinguished as "supported" or "unsupported". |
| """ |
| allsupported = [] |
| allunsupported = [] |
| for found, supported in _find(known=known, |
| ignored=ignored, |
| skip_objects=skip_objects, |
| ): |
| if supported is None: |
| continue |
| (allsupported if supported else allunsupported |
| ).append(found) |
| |
| _print('supported:') |
| _print('----------') |
| _show(sorted(allsupported)) |
| _print(f' ({len(allsupported)} total)') |
| _print() |
| _print('unsupported:') |
| _print('------------') |
| _show(sorted(allunsupported)) |
| _print(f' ({len(allunsupported)} total)') |
| |
| |
| ############################# |
| # the script |
| |
| COMMANDS = { |
| 'check': cmd_check, |
| 'show': cmd_show, |
| } |
| |
| PROG = sys.argv[0] |
| PROG = 'c-globals.py' |
| |
| |
| def parse_args(prog=PROG, argv=sys.argv[1:], *, _fail=None): |
| common = argparse.ArgumentParser(add_help=False) |
| common.add_argument('--ignored', metavar='FILE', |
| default=IGNORED_FILE, |
| help='path to file that lists ignored vars') |
| common.add_argument('--known', metavar='FILE', |
| default=KNOWN_FILE, |
| help='path to file that lists known types') |
| #common.add_argument('dirs', metavar='DIR', nargs='*', |
| # default=SOURCE_DIRS, |
| # help='a directory to check') |
| |
| parser = argparse.ArgumentParser( |
| prog=prog, |
| ) |
| subs = parser.add_subparsers(dest='cmd') |
| |
| check = subs.add_parser('check', parents=[common]) |
| |
| show = subs.add_parser('show', parents=[common]) |
| show.add_argument('--skip-objects', action='store_true') |
| |
| if _fail is None: |
| def _fail(msg): |
| parser.error(msg) |
| |
| # Now parse the args. |
| args = parser.parse_args(argv) |
| ns = vars(args) |
| |
| cmd = ns.pop('cmd') |
| if not cmd: |
| _fail('missing command') |
| |
| return cmd, ns |
| |
| |
| def main(cmd, cmdkwargs=None, *, _COMMANDS=COMMANDS): |
| try: |
| cmdfunc = _COMMANDS[cmd] |
| except KeyError: |
| raise ValueError( |
| f'unsupported cmd {cmd!r}' if cmd else 'missing cmd') |
| |
| cmdfunc(cmd, **cmdkwargs or {}) |
| |
| |
| if __name__ == '__main__': |
| cmd, cmdkwargs = parse_args() |
| main(cmd, cmdkwargs) |