Пример #1
0
 def generic_scope(self):
     scope = Scope()
     if self.name is not None:   # for recursive calls
         function_type = Function(self, Unknown(), NullEvaluator())
         scope.add(Symbol(self.name, function_type))
     for name, argtype in self.get_dict().items():
         scope.add(Symbol(name, argtype))
     if self.vararg_name:
         scope.add(Symbol(self.vararg_name, List(Unknown())))
     if self.kwarg_name:
         scope.add(Symbol(self.kwarg_name, Dict(Unknown(), Unknown())))
     return scope
Пример #2
0
    def evaluate(self, argument_scope):
        # argument_scope does not contain "self" parameter at this point
        # because we create the "self" instance inside this method
        instance = Instance(self._class_object.name, Scope())
        class_attributes = self._class_object.attributes
        for name in class_attributes.names():
            symbol_type = class_attributes.get_type(name)
            if isinstance(symbol_type, Function):
                function_type = Function(
                    symbol_type.signature,
                    symbol_type.return_type,
                    symbol_type.evaluator, instance)
                instance.attributes.add(Symbol(name, function_type))

        # Note: error checking for arguments passed in has already been
        # handled because the signature for the class object is loaded
        # based on the signature of the __init__ function
        init_function_type = instance.attributes.get_type('__init__')
        if init_function_type is not None:
            symbol = Symbol(init_function_type.signature.names[0], instance)
            argument_scope.add(symbol)
            init_function_type.evaluator.evaluate(argument_scope)
        instance.initialized = True
        return instance, UnknownValue()
Пример #3
0
def _visit_expression(node, expected_type, context, warnings):
    recur = partial(visit_expression, context=context, warnings=warnings)
    probe = partial(expression_type, context=context)
    comp = partial(comprehension_type, context=context, warnings=warnings)

    token = get_token(node)
    if token == 'BoolOp':
        for expr in node.values:
            recur(expr, Bool())
        return Bool()  # more restrictive than Python
    if token == 'BinOp':
        operator = get_token(node.op)
        if operator == 'Add':
            left_probe = probe(node.left)
            right_probe = probe(node.right)
            if isinstance(left_probe, Tuple) or isinstance(right_probe, Tuple):
                left = recur(node.left, BaseTuple())
                right = recur(node.right, BaseTuple())
                if isinstance(left, Tuple) and isinstance(right, Tuple):
                    return Tuple(left.item_types + right.item_types)
                else:
                    return Unknown()
            union_type = Union(Num(), Str(), List(Unknown()))
            left_intersect = type_intersection(left_probe, union_type)
            right_intersect = type_intersection(right_probe, union_type)
            sub_intersect = type_intersection(left_intersect, right_intersect)
            full_intersect = type_intersection(expected_type, sub_intersect)
            intersect = (full_intersect or sub_intersect or left_intersect
                         or right_intersect or union_type)
            recur(node.left, intersect)
            recur(node.right, intersect)
            return intersect
        elif operator == 'Mult':
            union_type = Union(Num(), Str())
            expected_intersect = type_intersection(expected_type, union_type)
            left_intersect = type_intersection(probe(node.left), union_type)
            right = recur(node.right, Num())
            if isinstance(left_intersect, Num):
                recur(node.left, Num())
                return Num()
            elif isinstance(left_intersect, Str):
                recur(node.left, Str())
                return Str()
            elif isinstance(expected_intersect, Num):
                recur(node.left, Num())
                return Num()
            elif isinstance(expected_intersect, Str):
                recur(node.left, Str())
                return Str()
            else:
                recur(node.left, union_type)
                return union_type
        elif operator == 'Mod':
            # num % num OR str % unknown
            union_type = Union(Num(), Str())
            expected_intersect = type_intersection(expected_type, union_type)
            left_intersect = type_intersection(probe(node.left), union_type)
            if isinstance(left_intersect, Num):
                recur(node.left, Num())
                recur(node.right, Num())
                return Num()
            elif isinstance(left_intersect, Str):
                recur(node.left, Str())
                recur(node.right, Unknown())
                return Str()
            elif isinstance(expected_intersect, Num):
                recur(node.left, Num())
                recur(node.right, Num())
                return Num()
            elif isinstance(expected_intersect, Str):
                recur(node.left, Str())
                recur(node.right, Unknown())
                return Str()
            else:
                recur(node.left, union_type)
                recur(node.right, Unknown())
                return union_type
        else:
            recur(node.left, Num())
            recur(node.right, Num())
            return Num()
    if token == 'UnaryOp':
        if get_token(node.op) == 'Not':
            recur(node.operand, Bool())
            return Bool()
        else:
            recur(node.operand, Num())
            return Num()
    if token == 'Lambda':
        return construct_function_type(node, LambdaVisitor(context))
    if token == 'IfExp':
        recur(node.test, Bool())
        if_inferences, else_inferences = maybe_inferences(node.test, context)
        context.begin_scope(Scope(if_inferences))
        body_type = recur(node.body, expected_type)
        context.end_scope()
        context.begin_scope(Scope(else_inferences))
        else_type = recur(node.orelse, expected_type)
        context.end_scope()
        return unify_types([body_type, else_type])
    if token == 'Dict':
        key_type = unify_types([recur(key, Unknown()) for key in node.keys])
        value_type = unify_types(
            [recur(value, Unknown()) for value in node.values])
        return Dict(key_type, value_type)
    if token == 'Set':
        subtype = (expected_type.item_type
                   if isinstance(expected_type, Set) else Unknown())
        return Set(unify_types([recur(elt, Unknown()) for elt in node.elts]))
    if token == 'ListComp':
        subtype = (expected_type.item_type
                   if isinstance(expected_type, List) else Unknown())
        return List(comp(node.elt, node.generators, subtype))
    if token == 'SetComp':
        subtype = (expected_type.item_type
                   if isinstance(expected_type, Set) else Unknown())
        return Set(comp(node.elt, node.generators, subtype))
    if token == 'DictComp':
        expected_key_type = (expected_type.key_type if isinstance(
            expected_type, Dict) else Unknown())
        expected_value_type = (expected_type.value_type if isinstance(
            expected_type, Dict) else Unknown())
        key_type = comp(node.key, node.generators, expected_key_type)
        value_type = comp(node.value, node.generators, expected_value_type)
        return Dict(key_type, value_type)
    if token == 'GeneratorExp':
        subtype = (expected_type.item_type
                   if isinstance(expected_type, List) else Unknown())
        return List(comp(node.elt, node.generators, subtype))
    if token == 'Yield':
        return List(recur(node.value, Unknown()))
    if token == 'Compare':
        operator = get_token(node.ops[0])
        if len(node.ops) > 1 or len(node.comparators) > 1:
            warnings.warn(node, 'comparison-operator-chaining')
        if operator in ['Eq', 'NotEq', 'Lt', 'LtE', 'Gt', 'GtE']:
            # all operands are constrained to have the same type
            # as their intersection
            left_probe = probe(node.left)
            right_probe = probe(node.comparators[0])
            intersection = type_intersection(left_probe, right_probe)
            if intersection is None:
                recur(node.left, right_probe)
                recur(node.comparators[0], left_probe)
            else:
                recur(node.left, intersection)
                recur(node.comparators[0], intersection)
        if operator in ['Is', 'IsNot']:
            recur(node.left, Maybe(Unknown()))
            recur(node.comparators[0], NoneType())
        if operator in ['In', 'NotIn']:
            # constrain right to list/set of left, and left to inst. of right
            left_probe = probe(node.left)
            right_probe = probe(node.comparators[0])
            union_type = Union(List(left_probe), Set(left_probe),
                               Dict(left_probe, Unknown()), Str())
            recur(node.comparators[0], union_type)
            if isinstance(right_probe, (List, Set)):
                recur(node.left, right_probe.item_type)
            elif isinstance(right_probe, Dict):
                recur(node.left, right_probe.key_type)
            else:
                recur(node.left, Unknown())
        return Bool()
    if token == 'Call':
        function_type = recur(node.func, Unknown())
        if not isinstance(function_type, (Class, Function)):
            if not isinstance(function_type, Unknown):
                warnings.warn(node, 'not-a-function')
            return Unknown()
        signature = function_type.signature
        instance = (function_type.instance if isinstance(
            function_type, Function) else None)
        offset = 1 if (instance is not None
                       or isinstance(function_type, Class)) else 0

        argument_scope = Scope()
        if instance is not None:
            self_symbol = Symbol(signature.names[0], instance)
            argument_scope.add(self_symbol)

        # make sure all required arguments are specified
        if node.starargs is None and node.kwargs is None:
            start = offset + len(node.args)
            required = signature.names[start:signature.min_count]
            kwarg_names = [keyword.arg for keyword in node.keywords]
            missing = [name for name in required if name not in kwarg_names]
            for missing_argument in missing:
                warnings.warn(node, 'missing-argument', missing_argument)

        # check for too many arguments
        if signature.vararg_name is None:
            if len(node.args) + len(node.keywords) > len(signature.types):
                warnings.warn(node, 'too-many-arguments')

        # load positional arguments
        for i, arg in enumerate(node.args):
            if i + offset >= len(signature):
                break
            arg_type = recur(arg, signature.types[i + offset])
            value = static_evaluate(arg, context)
            argument_scope.add(
                Symbol(signature.names[i + offset], arg_type, value))

        # load keyword arguments
        for kwarg in node.keywords:
            # TODO: make sure there is no overlap with positional args
            expected_type = signature.get_dict().get(kwarg.arg)
            if expected_type is None:
                warnings.warn(node, 'extra-keyword', kwarg.arg)
            else:
                arg_type = recur(kwarg.value, expected_type)
                value = static_evaluate(kwarg.value, context)
                argument_scope.add(Symbol(kwarg.arg, arg_type, value))

        if node.starargs is not None:
            recur(node.starargs, List(Unknown()))
        if node.kwargs is not None:
            recur(node.kwargs, Dict(Unknown(), Unknown()))

        return_type, _ = function_type.evaluator.evaluate(argument_scope)
        return return_type
    if token == 'Repr':
        return Str()
    if token == 'Num':
        return Num()
    if token == 'Str':
        return Str()
    if token == 'Attribute':
        value_type = recur(node.value, Unknown())
        if isinstance(value_type, Unknown):
            return Unknown()
        if not isinstance(value_type, Instance):
            warnings.warn(node, 'not-an-instance')
            return Unknown()
        attr_type = value_type.attributes.get_type(node.attr)
        if attr_type is None:
            warnings.warn(node, 'not-a-member')
            return Unknown()
        return attr_type
    if token == 'Subscript':
        union_type = Union(List(Unknown()), Dict(Unknown(), Unknown()),
                           BaseTuple())
        value_type = recur(node.value, union_type)
        if get_token(node.slice) == 'Index':
            if isinstance(value_type, Tuple):
                index = static_evaluate(node.slice.value, context)
                if isinstance(index, UnknownValue):
                    return Unknown()
                if not isinstance(index, int):
                    return Unknown()
                if not 0 <= index < len(value_type.item_types):
                    return Unknown()
                return value_type.item_types[index]
            elif isinstance(value_type, List):
                return value_type.item_type
            elif isinstance(value_type, Dict):
                return value_type.value_type
            else:
                return Unknown()
        elif get_token(node.slice) == 'Slice':
            if node.slice.lower is not None:
                recur(node.slice.lower, Num())
            if node.slice.upper is not None:
                recur(node.slice.upper, Num())
            if node.slice.step is not None:
                recur(node.slice.step, Num())
            return value_type
        else:
            return value_type
    if token == 'Name':
        defined_type = context.get_type(node.id)
        if defined_type is None:
            warnings.warn(node, 'undefined', node.id)
        context.add_constraint(node.id, expected_type)
        return defined_type or Unknown()
    if token == 'List':
        subtype = (expected_type.item_type
                   if isinstance(expected_type, List) else Unknown())
        return List(unify_types([recur(elt, subtype) for elt in node.elts]))
    if token == 'Tuple':
        if (isinstance(expected_type, Tuple)
                and len(node.elts) == len(expected_type.item_types)):
            return Tuple([
                recur(element, type_)
                for element, type_ in zip(node.elts, expected_type.item_types)
            ])
        return Tuple([recur(element, Unknown()) for element in node.elts])
    raise Exception('visit_expression does not recognize ' + token)
Пример #4
0
def _visit_expression(node, expected_type, context, warnings):
    recur = partial(visit_expression, context=context, warnings=warnings)
    probe = partial(expression_type, context=context)
    comp = partial(comprehension_type, context=context, warnings=warnings)

    token = get_token(node)
    if token == 'BoolOp':
        for expr in node.values:
            recur(expr, Bool())
        return Bool()   # more restrictive than Python
    if token == 'BinOp':
        operator = get_token(node.op)
        if operator == 'Add':
            left_probe = probe(node.left)
            right_probe = probe(node.right)
            if isinstance(left_probe, Tuple) or isinstance(right_probe, Tuple):
                left = recur(node.left, BaseTuple())
                right = recur(node.right, BaseTuple())
                if isinstance(left, Tuple) and isinstance(right, Tuple):
                    return Tuple(left.item_types + right.item_types)
                else:
                    return Unknown()
            union_type = Union(Num(), Str(), List(Unknown()))
            left_intersect = type_intersection(left_probe, union_type)
            right_intersect = type_intersection(right_probe, union_type)
            sub_intersect = type_intersection(left_intersect, right_intersect)
            full_intersect = type_intersection(expected_type, sub_intersect)
            intersect = (full_intersect or sub_intersect or left_intersect
                         or right_intersect or union_type)
            recur(node.left, intersect)
            recur(node.right, intersect)
            return intersect
        elif operator == 'Mult':
            union_type = Union(Num(), Str())
            expected_intersect = type_intersection(expected_type, union_type)
            left_intersect = type_intersection(probe(node.left), union_type)
            right = recur(node.right, Num())
            if isinstance(left_intersect, Num):
                recur(node.left, Num())
                return Num()
            elif isinstance(left_intersect, Str):
                recur(node.left, Str())
                return Str()
            elif isinstance(expected_intersect, Num):
                recur(node.left, Num())
                return Num()
            elif isinstance(expected_intersect, Str):
                recur(node.left, Str())
                return Str()
            else:
                recur(node.left, union_type)
                return union_type
        elif operator == 'Mod':
            # num % num OR str % unknown
            union_type = Union(Num(), Str())
            expected_intersect = type_intersection(expected_type, union_type)
            left_intersect = type_intersection(probe(node.left), union_type)
            if isinstance(left_intersect, Num):
                recur(node.left, Num())
                recur(node.right, Num())
                return Num()
            elif isinstance(left_intersect, Str):
                recur(node.left, Str())
                recur(node.right, Unknown())
                return Str()
            elif isinstance(expected_intersect, Num):
                recur(node.left, Num())
                recur(node.right, Num())
                return Num()
            elif isinstance(expected_intersect, Str):
                recur(node.left, Str())
                recur(node.right, Unknown())
                return Str()
            else:
                recur(node.left, union_type)
                recur(node.right, Unknown())
                return union_type
        else:
            recur(node.left, Num())
            recur(node.right, Num())
            return Num()
    if token == 'UnaryOp':
        if get_token(node.op) == 'Not':
            recur(node.operand, Bool())
            return Bool()
        else:
            recur(node.operand, Num())
            return Num()
    if token == 'Lambda':
        return construct_function_type(node, LambdaVisitor(context))
    if token == 'IfExp':
        recur(node.test, Bool())
        if_inferences, else_inferences = maybe_inferences(node.test, context)
        context.begin_scope(Scope(if_inferences))
        body_type = recur(node.body, expected_type)
        context.end_scope()
        context.begin_scope(Scope(else_inferences))
        else_type = recur(node.orelse, expected_type)
        context.end_scope()
        return unify_types([body_type, else_type])
    if token == 'Dict':
        key_type = unify_types([recur(key, Unknown()) for key in node.keys])
        value_type = unify_types([recur(value, Unknown())
                                  for value in node.values])
        return Dict(key_type, value_type)
    if token == 'Set':
        subtype = (expected_type.item_type if isinstance(expected_type, Set)
                   else Unknown())
        return Set(unify_types([recur(elt, Unknown()) for elt in node.elts]))
    if token == 'ListComp':
        subtype = (expected_type.item_type if isinstance(expected_type, List)
                   else Unknown())
        return List(comp(node.elt, node.generators, subtype))
    if token == 'SetComp':
        subtype = (expected_type.item_type if isinstance(expected_type, Set)
                   else Unknown())
        return Set(comp(node.elt, node.generators, subtype))
    if token == 'DictComp':
        expected_key_type = (expected_type.key_type
                             if isinstance(expected_type, Dict)
                             else Unknown())
        expected_value_type = (expected_type.value_type
                               if isinstance(expected_type, Dict)
                               else Unknown())
        key_type = comp(node.key, node.generators, expected_key_type)
        value_type = comp(node.value, node.generators, expected_value_type)
        return Dict(key_type, value_type)
    if token == 'GeneratorExp':
        subtype = (expected_type.item_type if isinstance(expected_type, List)
                   else Unknown())
        return List(comp(node.elt, node.generators, subtype))
    if token == 'Yield':
        return List(recur(node.value, Unknown()))
    if token == 'Compare':
        operator = get_token(node.ops[0])
        if len(node.ops) > 1 or len(node.comparators) > 1:
            warnings.warn(node, 'comparison-operator-chaining')
        if operator in ['Eq', 'NotEq', 'Lt', 'LtE', 'Gt', 'GtE']:
            # all operands are constrained to have the same type
            # as their intersection
            left_probe = probe(node.left)
            right_probe = probe(node.comparators[0])
            intersection = type_intersection(left_probe, right_probe)
            if intersection is None:
                recur(node.left, right_probe)
                recur(node.comparators[0], left_probe)
            else:
                recur(node.left, intersection)
                recur(node.comparators[0], intersection)
        if operator in ['Is', 'IsNot']:
            recur(node.left, Maybe(Unknown()))
            recur(node.comparators[0], NoneType())
        if operator in ['In', 'NotIn']:
            # constrain right to list/set of left, and left to inst. of right
            left_probe = probe(node.left)
            right_probe = probe(node.comparators[0])
            union_type = Union(List(left_probe), Set(left_probe),
                               Dict(left_probe, Unknown()), Str())
            recur(node.comparators[0], union_type)
            if isinstance(right_probe, (List, Set)):
                recur(node.left, right_probe.item_type)
            elif isinstance(right_probe, Dict):
                recur(node.left, right_probe.key_type)
            else:
                recur(node.left, Unknown())
        return Bool()
    if token == 'Call':
        function_type = recur(node.func, Unknown())
        if not isinstance(function_type, (Class, Function)):
            if not isinstance(function_type, Unknown):
                warnings.warn(node, 'not-a-function')
            return Unknown()
        signature = function_type.signature
        instance = (function_type.instance
                    if isinstance(function_type, Function) else None)
        offset = 1 if (instance is not None
                       or isinstance(function_type, Class)) else 0

        argument_scope = Scope()
        if instance is not None:
            self_symbol = Symbol(signature.names[0], instance)
            argument_scope.add(self_symbol)

        # make sure all required arguments are specified
        if node.starargs is None and node.kwargs is None:
            start = offset + len(node.args)
            required = signature.names[start:signature.min_count]
            kwarg_names = [keyword.arg for keyword in node.keywords]
            missing = [name for name in required if name not in kwarg_names]
            for missing_argument in missing:
                warnings.warn(node, 'missing-argument', missing_argument)

        # check for too many arguments
        if signature.vararg_name is None:
            if len(node.args) + len(node.keywords) > len(signature.types):
                warnings.warn(node, 'too-many-arguments')

        # load positional arguments
        for i, arg in enumerate(node.args):
            if i + offset >= len(signature):
                break
            arg_type = recur(arg, signature.types[i + offset])
            value = static_evaluate(arg, context)
            argument_scope.add(Symbol(signature.names[i + offset],
                                      arg_type, value))

        # load keyword arguments
        for kwarg in node.keywords:
            # TODO: make sure there is no overlap with positional args
            expected_type = signature.get_dict().get(kwarg.arg)
            if expected_type is None:
                warnings.warn(node, 'extra-keyword', kwarg.arg)
            else:
                arg_type = recur(kwarg.value, expected_type)
                value = static_evaluate(kwarg.value, context)
                argument_scope.add(Symbol(kwarg.arg, arg_type, value))

        if node.starargs is not None:
            recur(node.starargs, List(Unknown()))
        if node.kwargs is not None:
            recur(node.kwargs, Dict(Unknown(), Unknown()))

        return_type, _ = function_type.evaluator.evaluate(argument_scope)
        return return_type
    if token == 'Repr':
        return Str()
    if token == 'Num':
        return Num()
    if token == 'Str':
        return Str()
    if token == 'Attribute':
        value_type = recur(node.value, Unknown())
        if isinstance(value_type, Unknown):
            return Unknown()
        if not isinstance(value_type, Instance):
            warnings.warn(node, 'not-an-instance')
            return Unknown()
        attr_type = value_type.attributes.get_type(node.attr)
        if attr_type is None:
            warnings.warn(node, 'not-a-member')
            return Unknown()
        return attr_type
    if token == 'Subscript':
        union_type = Union(List(Unknown()), Dict(Unknown(), Unknown()),
                           BaseTuple())
        value_type = recur(node.value, union_type)
        if get_token(node.slice) == 'Index':
            if isinstance(value_type, Tuple):
                index = static_evaluate(node.slice.value, context)
                if isinstance(index, UnknownValue):
                    return Unknown()
                if not isinstance(index, int):
                    return Unknown()
                if not 0 <= index < len(value_type.item_types):
                    return Unknown()
                return value_type.item_types[index]
            elif isinstance(value_type, List):
                return value_type.item_type
            elif isinstance(value_type, Dict):
                return value_type.value_type
            else:
                return Unknown()
        elif get_token(node.slice) == 'Slice':
            if node.slice.lower is not None:
                recur(node.slice.lower, Num())
            if node.slice.upper is not None:
                recur(node.slice.upper, Num())
            if node.slice.step is not None:
                recur(node.slice.step, Num())
            return value_type
        else:
            return value_type
    if token == 'Name':
        defined_type = context.get_type(node.id)
        if defined_type is None:
            warnings.warn(node, 'undefined', node.id)
        context.add_constraint(node.id, expected_type)
        return defined_type or Unknown()
    if token == 'List':
        subtype = (expected_type.item_type if isinstance(expected_type, List)
                   else Unknown())
        return List(unify_types([recur(elt, subtype) for elt in node.elts]))
    if token == 'Tuple':
        if (isinstance(expected_type, Tuple)
                and len(node.elts) == len(expected_type.item_types)):
            return Tuple([recur(element, type_) for element, type_ in
                          zip(node.elts, expected_type.item_types)])
        return Tuple([recur(element, Unknown()) for element in node.elts])
    raise Exception('visit_expression does not recognize ' + token)