summaryrefslogtreecommitdiffstats
path: root/Tools/c-analyzer/cpython/__main__.py
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/c-analyzer/cpython/__main__.py')
-rw-r--r--Tools/c-analyzer/cpython/__main__.py212
1 files changed, 212 insertions, 0 deletions
diff --git a/Tools/c-analyzer/cpython/__main__.py b/Tools/c-analyzer/cpython/__main__.py
new file mode 100644
index 0000000..6b0f9bc
--- /dev/null
+++ b/Tools/c-analyzer/cpython/__main__.py
@@ -0,0 +1,212 @@
+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)