diff options
| author | David Lamparter <equinox@diac24.net> | 2019-06-04 20:27:05 +0200 | 
|---|---|---|
| committer | David Lamparter <equinox@diac24.net> | 2019-06-12 19:23:00 +0200 | 
| commit | 4381a59be0d5122c732cc9831af9d82e6fa87c4a (patch) | |
| tree | f729cb01f23ff70d82a04bc46695c74d0e18de87 /python | |
| parent | 3779776a39d81168b701b2cc2ce50a3b2331d3e8 (diff) | |
lib/clippy: assert() for non-optional args
This is mostly to help static analysis; since we know from the command
string which args are optional and which aren't, we can add assert()
statements on them.
Fixes: #3270
Signed-off-by: David Lamparter <equinox@diac24.net>
Diffstat (limited to 'python')
| -rw-r--r-- | python/clidef.py | 38 | 
1 files changed, 34 insertions, 4 deletions
diff --git a/python/clidef.py b/python/clidef.py index 68682eea6c..be68001310 100644 --- a/python/clidef.py +++ b/python/clidef.py @@ -37,6 +37,7 @@ class RenderHandler(object):      deref = ''      drop_str = False      canfail = True +    canassert = False  class StringHandler(RenderHandler):      argtype = 'const char *' @@ -44,6 +45,7 @@ class StringHandler(RenderHandler):      code = Template('$varname = (argv[_i]->type == WORD_TKN) ? argv[_i]->text : argv[_i]->arg;')      drop_str = True      canfail = False +    canassert = True  class LongHandler(RenderHandler):      argtype = 'long' @@ -111,6 +113,7 @@ if (argv[_i]->text[0] == 'X') {  	_fail = !inet_aton(argv[_i]->arg, &s__$varname.sin.sin_addr);  	$varname = &s__$varname;  }''') +    canassert = True  def mix_handlers(handlers):      def combine(a, b): @@ -171,6 +174,7 @@ $argblocks  		return CMD_WARNING;  #endif  #endif +$argassert  	return ${fnname}_magic(self, vty, argc, argv$arglist);  } @@ -182,6 +186,21 @@ argblock = Template('''  			$code  		}''') +def get_always_args(token, always_args, args = [], stack = []): +    if token in stack: +        return +    if token.type == 'END_TKN': +        for arg in list(always_args): +            if arg not in args: +                always_args.remove(arg) +        return + +    stack = stack + [token] +    if token.type in handlers and token.varname is not None: +        args = args + [token.varname] +    for nexttkn in token.next(): +        get_always_args(nexttkn, always_args, args, stack) +  def process_file(fn, ofd, dumpfd, all_defun):      errors = 0      filedata = clippy.parse(fn) @@ -206,6 +225,7 @@ def process_file(fn, ofd, dumpfd, all_defun):              graph = clippy.Graph(cmddef)              args = OrderedDict() +            always_args = set()              for token, depth in clippy.graph_iterate(graph):                  if token.type not in handlers:                      continue @@ -213,6 +233,9 @@ def process_file(fn, ofd, dumpfd, all_defun):                      continue                  arg = args.setdefault(token.varname, [])                  arg.append(handlers[token.type](token)) +                always_args.add(token.varname) + +            get_always_args(graph.first(), always_args)              #print('-' * 76)              #pprint(entry) @@ -224,30 +247,36 @@ def process_file(fn, ofd, dumpfd, all_defun):              argdecls = []              arglist = []              argblocks = [] +            argassert = []              doc = []              canfail = 0 -            def do_add(handler, varname, attr = ''): +            def do_add(handler, basename, varname, attr = ''):                  argdefs.append(',\\\n\t%s %s%s' % (handler.argtype, varname, attr))                  argdecls.append('\t%s\n' % (handler.decl.substitute({'varname': varname}).replace('\n', '\n\t')))                  arglist.append(', %s%s' % (handler.deref, varname)) +                if basename in always_args and handler.canassert: +                    argassert.append('''\tif (!%s) { +\t\tvty_out(vty, "Internal CLI error [%%s]\\n", "%s"); +\t\treturn CMD_WARNING; +\t}\n''' % (varname, varname))                  if attr == '':                      at = handler.argtype                      if not at.startswith('const '):                          at = '. . . ' + at -                    doc.append('\t%-26s %s' % (at, varname)) +                    doc.append('\t%-26s %s  %s' % (at, 'alw' if basename in always_args else 'opt', varname))              for varname in args.keys():                  handler = mix_handlers(args[varname])                  #print(varname, handler)                  if handler is None: continue -                do_add(handler, varname) +                do_add(handler, varname, varname)                  code = handler.code.substitute({'varname': varname}).replace('\n', '\n\t\t\t')                  if handler.canfail:                      canfail = 1                  strblock = ''                  if not handler.drop_str: -                    do_add(StringHandler(None), '%s_str' % (varname), ' __attribute__ ((unused))') +                    do_add(StringHandler(None), varname, '%s_str' % (varname), ' __attribute__ ((unused))')                      strblock = '\n\t\t\t%s_str = argv[_i]->arg;' % (varname)                  argblocks.append(argblock.substitute({'varname': varname, 'strblock': strblock, 'code': code})) @@ -263,6 +292,7 @@ def process_file(fn, ofd, dumpfd, all_defun):              params['argblocks'] = ''.join(argblocks)              params['canfail'] = canfail              params['nonempty'] = len(argblocks) +            params['argassert'] = ''.join(argassert)              ofd.write(templ.substitute(params))      return errors  | 
