summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2009-11-17 14:47:25 -0500
committerDavid Malcolm <dmalcolm@redhat.com>2009-11-17 14:47:25 -0500
commit28a01c0fd593df0f64d06b4205c5a93b8068ea51 (patch)
tree36eddd2ca00184c26d86af817f5ab6adceba69f5
parent254f88a995334acfe04b5774a6be048a365cdb86 (diff)
downloadcheck-cpython-28a01c0fd593df0f64d06b4205c5a93b8068ea51.tar.gz
check-cpython-28a01c0fd593df0f64d06b4205c5a93b8068ea51.tar.xz
check-cpython-28a01c0fd593df0f64d06b4205c5a93b8068ea51.zip
Generalize variadic function handling
Avoid having to manually write a rule for each number of variable arguments to PyArg_ParseTuple etc by capturing each argument in turn (with lists of prior and subsequent expressions). The downside is that the rule is invoked once per argument at each callsite, with knowledge of only one type, rather than invoking it once per callsite with knowledge of all arguments. Thanks to Julia Lawall and Nicolas Palix
-rw-r--r--pyarg-parsetuple.cocci130
-rw-r--r--validate.py47
2 files changed, 79 insertions, 98 deletions
diff --git a/pyarg-parsetuple.cocci b/pyarg-parsetuple.cocci
index c0d9595..dd31702 100644
--- a/pyarg-parsetuple.cocci
+++ b/pyarg-parsetuple.cocci
@@ -16,123 +16,97 @@
@initialize:python@
"""
Analyze format strings passed to variadic function, compare to vararg types actually passed
-
-FIXME: generalize this to arbitrary number of varargs; how to express this in SmPL?
"""
import sys
sys.path.append('.')
-from validate import validate_types
+from validate import validate_type
num_errors = 0
-@ check_PyArg_ParseTuple_1 @
+@ check_PyArg_ParseTuple @
position pos;
expression args;
expression fmt;
-type t1;
-t1 e1;
+expression list[len_E] E;
+expression list[len_F] F;
+type t;
+t e;
@@
-PyArg_ParseTuple@pos(args, fmt, e1)
+PyArg_ParseTuple@pos(args, fmt, E, e, F)
@script:python@
-pos << check_PyArg_ParseTuple_1.pos;
-fmt << check_PyArg_ParseTuple_1.fmt;
-t1 << check_PyArg_ParseTuple_1.t1;
+pos << check_PyArg_ParseTuple.pos;
+fmt << check_PyArg_ParseTuple.fmt;
+len_E << check_PyArg_ParseTuple.len_E;
+len_F << check_PyArg_ParseTuple.len_F;
+t << check_PyArg_ParseTuple.t;
@@
+num_errors += validate_type(pos[0],
+ fmt.expr,
+ int(len_E),
+ int(len_E) + 1 + int(len_F),
+ t)
-# For some reason, locations are coming as a 1-tuple containing a Location (from
-# coccilibs.elems), rather than the location itself
-# Hence we use p1[0], not p1
-num_errors += validate_types(pos[0], fmt.expr, [t1])
+# likewise for PyArg_Parse etc:
-@ check_PyArg_ParseTuple_2 @
+@ check_PyArg_Parse @
position pos;
expression args;
expression fmt;
-type t1;
-t1 e1;
-type t2;
-t2 e2;
+expression list[len_E] E;
+expression list[len_F] F;
+type t;
+t e;
@@
-PyArg_ParseTuple(args@pos, fmt, e1, e2)
+PyArg_Parse@pos(args, fmt, E, e, F)
@script:python@
-fmt << check_PyArg_ParseTuple_2.fmt;
-pos << check_PyArg_ParseTuple_2.pos;
-t1 << check_PyArg_ParseTuple_2.t1;
-t2 << check_PyArg_ParseTuple_2.t2;
+pos << check_PyArg_Parse.pos;
+fmt << check_PyArg_Parse.fmt;
+len_E << check_PyArg_Parse.len_E;
+len_F << check_PyArg_Parse.len_F;
+t << check_PyArg_Parse.t;
@@
-num_errors += validate_types(pos[0], fmt.expr, [t1, t2])
+num_errors += validate_type(pos[0],
+ fmt.expr,
+ int(len_E),
+ int(len_E) + 1 + int(len_F),
+ t)
-@ check_PyArg_ParseTuple_3 @
+@ check_PyArg_ParseTupleAndKeywords @
position pos;
expression args;
+expression keywords;
expression fmt;
-type t1; t1 e1;
-type t2; t2 e2;
-type t3; t3 e3;
-@@
-
-PyArg_ParseTuple(args@pos, fmt, e1, e2, e3)
-
-@script:python@
-pos << check_PyArg_ParseTuple_3.pos;
-fmt << check_PyArg_ParseTuple_3.fmt;
-pos << check_PyArg_ParseTuple_3.pos;
-t1 << check_PyArg_ParseTuple_3.t1;
-t2 << check_PyArg_ParseTuple_3.t2;
-t3 << check_PyArg_ParseTuple_3.t3;
+expression argnames;
+expression list[len_E] E;
+expression list[len_F] F;
+type t;
+t e;
@@
-num_errors += validate_types(pos[0], fmt.expr, [t1, t2, t3])
-
-# and so on... need to find a general way of doing this, rather than repeating for 4, 5, 6...
-# likewise for PyArg_Parse:
+PyArg_ParseTupleAndKeywords@pos(args, keywords, fmt, argnames, E, e, F)
-@ check_PyArg_Parse_1 @
-position pos;
-expression args;
-expression fmt;
-type t1;
-t1 e1;
-@@
-
-PyArg_Parse@pos(args, fmt, e1)
@script:python@
-pos << check_PyArg_Parse_1.pos;
-args << check_PyArg_Parse_1.args;
-fmt << check_PyArg_Parse_1.fmt;
-pos << check_PyArg_Parse_1.pos;
-t1 << check_PyArg_Parse_1.t1;
+pos << check_PyArg_ParseTupleAndKeywords.pos;
+fmt << check_PyArg_ParseTupleAndKeywords.fmt;
+len_E << check_PyArg_ParseTupleAndKeywords.len_E;
+len_F << check_PyArg_ParseTupleAndKeywords.len_F;
+t << check_PyArg_ParseTupleAndKeywords.t;
@@
-num_errors += validate_types(pos[0], fmt.expr, [t1])
-
-# again, for all N
-# similarly for PyArg_ParseTupleAndKeywords:
+num_errors += validate_type(pos[0],
+ fmt.expr,
+ int(len_E),
+ int(len_E) + 1 + int(len_F),
+ t)
-@ check_PyArg_ParseTupleAndKeywords_1 @
-position pos;
-expression args, kw, fmt, keywords;
-type t1;
-t1 e1;
-@@
-PyArg_ParseTupleAndKeywords@pos(args, kw, fmt, keywords, e1)
-@script:python@
-pos << check_PyArg_Parse_1.pos;
-args << check_PyArg_Parse_1.args;
-fmt << check_PyArg_Parse_1.fmt;
-pos << check_PyArg_Parse_1.pos;
-t1 << check_PyArg_Parse_1.t1;
-@@
-num_errors += validate_types(pos[0], fmt.expr, [t1])
-# etc
@script:python @
@@
diff --git a/validate.py b/validate.py
index 786d7c0..6d50921 100644
--- a/validate.py
+++ b/validate.py
@@ -141,28 +141,26 @@ def get_types(location, strfmt):
class WrongNumberOfVars(FormatStringError):
- def __init__(self, location, format_string, exp_types, actual_types):
+ def __init__(self, location, format_string, exp_types, num_args):
FormatStringError.__init__(self, location, format_string)
self.exp_types = exp_types
- self.actual_types = actual_types
+ self.num_args = num_args
class NotEnoughVars(WrongNumberOfVars):
def _get_desc(self):
- return 'Not enough arguments in "%s" : expected %i (%s), but got %i (%s)' % (
+ return 'Not enough arguments in "%s" : expected %i (%s), but got %i' % (
self.format_string,
len(self.exp_types),
self.exp_types,
- len(self.actual_types),
- self.actual_types)
+ self.num_args)
class TooManyVars(WrongNumberOfVars):
def _get_desc(self):
- return 'Too many arguments in "%s": expected %i (%s), but got %i (%s)' % (
+ return 'Too many arguments in "%s": expected %i (%s), but got %i' % (
self.format_string,
len(self.exp_types),
self.exp_types,
- len(self.actual_types),
- self.actual_types)
+ self.num_args)
class MismatchingType(FormatStringError):
def __init__(self, location, format_string, arg_num, exp_type, actual_type):
@@ -194,24 +192,33 @@ def type_equality(t1, t2):
return False
-def validate_types(location, format_string, actual_types):
+def validate_type(location, format_string, index, actual_num_args, actual_type):
if False:
- print 'validate_types(%s, %s, %s)' % (
- repr(location), repr(format_string), repr(actual_types))
+ print 'validate_types(%s, %s, %s, %s, %s)' % (
+ repr(location),
+ repr(format_string),
+ repr(index),
+ repr(actual_num_args),
+ repr(actual_type))
+
try:
exp_types = get_types(location, format_string[1:-1]) # strip leading and trailing " chars
- if len(actual_types) < len(exp_types):
- raise NotEnoughVars(location, format_string, exp_types, actual_types)
- if len(actual_types) > len(exp_types):
- raise TooManyVars(location, format_string, exp_types, actual_types)
- for i, (exp, actual) in enumerate(zip(exp_types, actual_types)):
- if not type_equality(exp, actual):
- raise MismatchingType(location, format_string, i+1, exp, actual)
+ if actual_num_args < len(exp_types):
+ raise NotEnoughVars(location, format_string, exp_types, actual_num_args)
+ if actual_num_args > len(exp_types):
+ raise TooManyVars(location, format_string, exp_types, actual_num_args)
+ exp_type = exp_types[index]
+ if not type_equality(exp_type, actual_type):
+ raise MismatchingType(location, format_string, index+1, exp_type, actual_type)
except CExtensionError, err:
print err
if False:
- print 'validate_types(%s, %s, %s)' % (
- repr(location), repr(format_string), repr(actual_types))
+ print 'validate_types(%s, %s, %s, %s, %s)' % (
+ repr(location),
+ repr(format_string),
+ repr(index),
+ repr(actual_num_args),
+ repr(actual_type))
return 1
return 0