From 28a01c0fd593df0f64d06b4205c5a93b8068ea51 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Tue, 17 Nov 2009 14:47:25 -0500 Subject: 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 --- pyarg-parsetuple.cocci | 130 ++++++++++++++++++++----------------------------- validate.py | 47 ++++++++++-------- 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 -- cgit