Example #1
0
def _infer_augmented_assign(node, context, solver):
    """Infer the types for augmented assignments

    Examples:
        a += 5
        b[2] &= x
        c.x -= f(1, 2)
    """
    target_type = expr.infer(node.target, context, solver)
    value_type = expr.infer(node.value, context, solver)
    result_type = expr.binary_operation_type(target_type, node.op, value_type,
                                             node.lineno, solver)

    _infer_assignment_target(node.target, context, result_type, solver)

    return solver.z3_types.none
Example #2
0
def _infer_with(node, context, solver):
    """Infer the types for a with block"""
    for item in node.items:
        if item.optional_vars:
            item_type = expr.infer(item.context_expr, context, solver)
            _infer_assignment_target(item.optional_vars, context, item_type,
                                     solver)

    return _infer_body(node.body, context, node.lineno, solver)
Example #3
0
def infer(node, context, solver, parent=None):
    if parent:
        node._parent = parent
    if isinstance(node, ast.Assign):
        return _infer_assign(node, context, solver)
    elif isinstance(node, ast.AugAssign):
        return _infer_augmented_assign(node, context, solver)
    elif isinstance(node, ast.Return):
        if not node.value:
            return solver.z3_types.none
        return expr.infer(node.value, context, solver)
    elif isinstance(node, ast.Delete):
        return _infer_delete(node, context, solver)
    elif isinstance(node, (ast.If, ast.While)):
        return _infer_control_flow(node, context, solver)
    elif isinstance(node, ast.For):
        return _infer_for(node, context, solver)
    elif sys.version_info[0] >= 3 and sys.version_info[1] >= 5 and isinstance(
            node, ast.AsyncFor):
        # AsyncFor is introduced in Python 3.5
        return _infer_for(node, context, solver)
    elif isinstance(node, ast.With):
        return _infer_with(node, context, solver)
    elif sys.version_info[0] >= 3 and sys.version_info[1] >= 5 and isinstance(
            node, ast.AsyncWith):
        # AsyncWith is introduced in Python 3.5
        return _infer_with(node, context, solver)
    elif isinstance(node, ast.Try):
        return _infer_try(node, context, solver)
    elif isinstance(node, ast.FunctionDef):
        return _infer_func_def(node, context, solver)
    elif isinstance(node, ast.ClassDef):
        return _infer_class_def(node, context, solver)
    elif isinstance(node, ast.Expr):
        expr.infer(node.value, context, solver)
    elif isinstance(node, ast.Import):
        return _infer_import(node, context, solver)
    elif isinstance(node, ast.ImportFrom):
        return _infer_import_from(node, context, solver)
    elif isinstance(node, ast.Raise):
        return _infer_raise(node, context, solver)
    elif sys.version_info >= (3, 6) and isinstance(node, ast.AnnAssign):
        return _infer_annotated_assign(node, context, solver)
    return solver.z3_types.none
Example #4
0
def _delete_element(target, context, lineno, solver):
    """Remove (if needed) a target from the context

    Cases:
        - del var_name: remove its type mapping from the context directly.
        - del subscript:
                    * Tuple/String --> Immutable. Raise exception.
                    * List/Dict --> Do nothing to the context.
    """
    if isinstance(target, (ast.Tuple, ast.List)):  # Multiple deletions
        for elem in target.elts:
            _delete_element(elem, context, lineno, solver)
    elif isinstance(target, ast.Name):
        context.delete_type(target.id)
    elif isinstance(target, ast.Subscript):
        expr.infer(target, context, solver)
        indexed_type = expr.infer(target.value, context, solver)
        solver.add(axioms.delete_subscript(indexed_type, solver.z3_types),
                   fail_message="Deletion in line {}".format(lineno))
    elif isinstance(target, ast.Attribute):
        raise NotImplementedError("Attribute deletion is not supported.")
Example #5
0
def _infer_annotated_assign(node, context, solver):
    """Infer the types of variables in annotated assignment node"""
    if node.value:
        value_type = expr.infer(node.value, context, solver)
        target_type = _infer_assignment_target(node.target, context,
                                               value_type, solver)
    else:
        target_type = _infer_one_target(node.target, context, solver)
    annotation_type = solver.resolve_annotation(node.annotation,
                                                get_module(node))
    solver.add(target_type == annotation_type,
               fail_message="Annotated assignment in line {}".format(
                   node.lineno))
Example #6
0
def _infer_one_target(target, context, solver):
    """
    Get the type of the left hand side of an assignment

    :param target: The target on the left hand side
    :param context: The current context level
    :param solver: The SMT solver
    :return: the type of the target
    """
    if isinstance(target, ast.Name):
        if target.id in context.types_map:
            return context.get_type(target.id)
        else:
            target_type = solver.new_z3_const("assign")
            context.set_type(target.id, target_type)
            return target_type
    elif isinstance(target, ast.Tuple):
        args_types = []
        for elt in target.elts:
            args_types.append(_infer_one_target(elt, context, solver))
        return solver.z3_types.tuples[len(args_types)](*args_types)
    elif isinstance(target, ast.List):
        list_type = solver.new_z3_const("assign")
        for elt in target.elts:
            solver.add(list_type == _infer_one_target(elt, context, solver),
                       fail_message="List assignment in line {}".format(
                           target.lineno))
        return solver.z3_types.list(list_type)
    target_type = expr.infer(target, context, solver)

    if isinstance(target, ast.Subscript):
        solver.add(axioms.subscript_assignment(
            expr.infer(target.value, context, solver), solver.z3_types),
                   fail_message="Subscript assignment in line {}".format(
                       target.lineno))

    return target_type
Example #7
0
def _infer_assign(node, context, solver):
    """Infer the types of target variables in an assignment node."""

    if _is_type_var_declaration(node.value):
        module = get_module(node)
        solver.annotation_resolver.add_type_var(node.targets[0], node.value,
                                                solver, module)
    else:
        value_type = expr.infer(node.value, context, solver)
        for target in node.targets:
            target_type = _infer_assignment_target(target, context, value_type,
                                                   solver)
            if len(node.targets) == 1:
                context.add_assignment(target_type, node)

    return solver.z3_types.none
Example #8
0
def _infer_args_defaults(args_types, defaults, context, solver):
    """Infer the default values of function arguments (if any)

    :param args_types: Z3 constants for arguments types
    :param defaults: AST nodes for default values of arguments
    :param context: The parent of the function context
    :param solver: The inference Z3 solver


    A default array of length `n` represents the default values of the last `n` arguments
    """
    for i, default in enumerate(defaults):
        arg_idx = i + len(args_types) - len(
            defaults)  # The defaults array correspond to the last arguments
        default_type = expr.infer(default, context, solver)
        solver.add(solver.z3_types.subtype(default_type, args_types[arg_idx]),
                   fail_message="Function default argument in line {}".format(
                       defaults[i].lineno))
        solver.optimize.add_soft(default_type == args_types[arg_idx])
Example #9
0
def _infer_for(node, context, solver):
    """Infer the type for a for loop node

    Limitation:
        - The iterable can't be a tuple.
            For example: the following is not allowed:
                for x in (1, 2.0, "string"):
                    ....
    """
    iter_type = expr.infer(node.iter, context, solver)

    # Infer the target in the loop, inside the global context
    # Cases:
    # - Var name. Ex: for i in range(5)..
    # - Tuple. Ex: for (a,b) in [(1,"st"), (3,"st2")]..
    # - List. Ex: for [a,b] in [(1, "st"), (3, "st2")]..
    target_type = solver.new_z3_const("for_target")
    solver.add(axioms.for_loop(iter_type, target_type, solver.z3_types),
               fail_message="For loop in line {}".format(node.lineno))

    _infer_assignment_target(node.target, context, target_type, solver)

    return _infer_control_flow(node, context, solver)
Example #10
0
def _infer_raise(node, context, solver):
    if isinstance(node.exc, ast.Call):
        for arg in node.exc.args:
            expr.infer(arg, context, solver)

    return solver.z3_types.none
Example #11
0
def _infer_control_flow(node, context, solver):
    """Infer the type(s) for an if/while/for statements block.

    Arguments:
        node: The AST node to be inferred
        context: the current context level
    Example:
        if (some_condition):
            ......
            return "some string"
        else:
            ......
            return 2.0

        type: super{String, Float}
    """
    body_context = Context(node, node.body, solver, parent_context=context)
    else_context = Context(node, node.body, solver, parent_context=context)

    var_is_instance = None
    if hasattr(node, "test"):
        expr.infer(node.test, context, solver)
        if (isinstance(node.test, ast.Call)
                and isinstance(node.test.func, ast.Name)
                and node.test.func.id == "isinstance"
                and len(node.test.args) == 2):
            # isinstance(x, t) test: Make `x` to be of type `t` in the then branch
            # The only allowed case is when `x` is variable and `t` is a single type (not a tuple).

            # Get the type `t`
            t = solver.resolve_annotation(node.test.args[1], get_module(node))

            # Set `x` to be an instance of `t` in the then branch
            body_context.isinstance_nodes[ast.dump(node.test.args[0],
                                                   annotate_fields=False)] = t

            # Keep track of the name of the variable `x`
            if isinstance(node.test.args[0], ast.Name):
                var_is_instance = node.test.args[0].id

    body_type = _infer_body(node.body, body_context, node.lineno, solver)
    else_type = _infer_body(node.orelse, else_context, node.lineno, solver)

    # Re-assigning variables in the body branch
    for v in body_context.types_map:
        if context.has_variable(v) and v != var_is_instance:
            t1 = body_context.types_map[v]
            t2 = context.get_type(v)
            solver.add(
                t1 == t2,
                fail_message="re-assigning in flow branching in line {}".
                format(node.lineno))

    # Re-assigning variables in the else branch
    for v in else_context.types_map:
        if context.has_variable(v):
            t1 = else_context.types_map[v]
            t2 = context.get_type(v)
            solver.add(
                t1 == t2,
                fail_message="re-assigning in flow branching in line {}".
                format(node.lineno))

    # Take intersection of variables in both contexts
    for v in body_context.types_map:
        if v in else_context.types_map and not context.has_variable(v):
            var_type = solver.new_z3_const("branching_var")
            t1 = body_context.types_map[v]
            t2 = else_context.types_map[v]

            if inference_config["enforce_same_type_in_branches"]:
                branch_axioms = [t1 == var_type, t2 == var_type]
            else:
                branch_axioms = [
                    solver.z3_types.subtype(t1, var_type),
                    solver.z3_types.subtype(t2, var_type)
                ]

            solver.add(
                branch_axioms,
                fail_message="subtyping in flow branching in line {}".format(
                    node.lineno))

            solver.optimize.add_soft(t1 == var_type)
            solver.optimize.add_soft(t2 == var_type)
            context.set_type(v, var_type)

    result_type = solver.new_z3_const("control_flow")
    solver.add(axioms.control_flow(body_type, else_type, result_type,
                                   solver.z3_types),
               fail_message="Control flow in line {}".format(node.lineno))
    solver.optimize.add_soft(result_type == body_type)
    solver.optimize.add_soft(result_type == else_type)
    return result_type