Esempio n. 1
0
    def visit_Compare(self, node):
        """

        Args:
            node:

        Returns:

        """
        # Handle left and right
        left = self.visit(node.left)
        comparators = [self.visit(compare) for compare in node.comparators]

        # Handle ops
        for op, right in zip(node.ops, comparators):
            if isinstance(op, (ast.Eq, ast.NotEq, ast.Is, ast.IsNot)):
                continue
            elif isinstance(op, (ast.Lt, ast.LtE, ast.GtE, ast.Gt)):
                if are_types_equal(left, right):
                    if isinstance(left, ORDERABLE_TYPES):
                        continue
            elif isinstance(op, (ast.In, ast.NotIn)):
                if isinstance(right, INDEXABLE_TYPES):
                    continue
            self._issue(
                incompatible_types(self.locate(),
                                   op,
                                   left,
                                   right,
                                   report=self.report))
        return BoolType()
Esempio n. 2
0
    def combine_states(self, left, right):
        """

        Args:
            left:
            right:

        Returns:

        """
        state = State(left.name, [left], left.type, 'branch', self.locate(),
                      read=left.read, set=left.set, over=left.over,
                      over_position=left.over_position)
        if right is None:
            state.read = 'no' if left.read == 'no' else 'maybe'
            state.set = 'no' if left.set == 'no' else 'maybe'
            state.over = 'no' if left.over == 'no' else 'maybe'
        else:
            if not are_types_equal(left.type, right.type):
                self._issue(type_changes(self.locate(), left.name, left.type, right.type))
            state.read = self.match_rso(left.read, right.read)
            state.set = self.match_rso(left.set, right.set)
            state.over = self.match_rso(left.over, right.over)
            if left.over == 'no':
                state.over_position = right.over_position
            state.trace.append(right)
        return state
Esempio n. 3
0
    def store_variable(self,
                       name,
                       store_type,
                       position=None,
                       force_create=False):
        """
        Update the variable with the given name to now have the new type.

        Args:
            name (str): The unqualified name of the variable. The variable will
                        be assumed to be in the current scope.
            store_type (Type): The new type of this variable.
            position: The location that this store occurred at
        Returns:
            State: The new state of the variable.
        """
        if position is None:
            position = self.locate()
        full_name = self._scope_chain_str(name)
        current_path = self.path_chain[0]
        variable = self.find_variable_scope(name)
        if not variable.exists or force_create:
            # Create a new instance of the variable on the current path
            new_state = State(name, [],
                              store_type,
                              'store',
                              position,
                              read='no',
                              set='yes',
                              over='no')
            self.name_map[current_path][full_name] = new_state
        else:
            new_state = self.trace_state(variable.state, "store", position)
            if not variable.in_scope:
                self._issue(
                    write_out_of_scope(self.locate(), name,
                                       report=self.report))
            # Type change?
            if not are_types_equal(store_type, variable.state.type):
                self._issue(
                    type_changes(position, name, variable.state.type,
                                 store_type))
            new_state.type = store_type
            # Overwritten?
            if variable.state.set == 'yes' and variable.state.read == 'no':
                new_state.over_position = position
                new_state.over = 'yes'
            else:
                new_state.set = 'yes'
                new_state.read = 'no'
            self.name_map[current_path][full_name] = new_state
        # If this is a class scope...
        current_scope = self.scope_chain[0]
        if current_scope in self.class_scopes:
            self.class_scopes[current_scope].add_attr(name, new_state.type)
        return new_state
Esempio n. 4
0
    def assign_target(self, target, target_type):
        """
        Assign the type to the target, handling all kinds of assignment
        statements, including Names, Tuples/Lists, Subscripts, and
        Attributes.

        Args:
            target (ast.AST): The target AST Node.
            target_type (Type): The TIFA type.

        Returns:

        """
        if isinstance(target, ast.Name):
            self.store_variable(target.id, target_type)
        elif isinstance(target, (ast.Tuple, ast.List)):
            for i, elt in enumerate(target.elts):
                elt_type = target_type.iterate(LiteralNum(i))
                self.assign_target(elt, elt_type)
        elif isinstance(target, ast.Subscript):
            left_hand_type = self.visit(target.value)
            if isinstance(left_hand_type, ListType):
                # TODO: Handle updating value in list
                pass
            elif isinstance(left_hand_type, DictType):
                # TODO: Update this for Python 3.9, now that Slice notation has changed
                if not isinstance(target.slice, ast.Index):
                    # TODO: Can't subscript a dictionary assignment
                    return None
                literal = self.get_literal(target.slice.value)
                if not literal:
                    key_type = self.visit(target.slice.value)
                    left_hand_type.empty = False
                    left_hand_type.keys = [key_type.clone()]
                    left_hand_type.values = [target_type.clone()]
                elif left_hand_type.literals:
                    original_type = left_hand_type.has_literal(literal)
                    if not original_type:
                        left_hand_type.update_key(literal, target_type.clone())
                    elif not are_types_equal(original_type, target_type):
                        # TODO: Fix "Dictionary" to be the name of the variable
                        self._issue(
                            type_changes(self.locate(), 'Dictionary',
                                         original_type, target_type))
        elif isinstance(target, ast.Attribute):
            left_hand_type = self.visit(target.value)
            if isinstance(left_hand_type, InstanceType):
                left_hand_type.add_attr(target.attr, target_type)
Esempio n. 5
0
    def visit_IfExp(self, node):
        """

        Args:
            node:

        Returns:

        """
        # Visit the conditional
        self.visit(node.test)

        # Visit the body
        body = self.visit(node.body)

        # Visit the orelse
        orelse = self.visit(node.orelse)

        if are_types_equal(body, orelse):
            return body

        # TODO: Union type?
        return UnknownType()
Esempio n. 6
0
        def definition(tifa, call_type, call_name, parameters, call_position):
            """

            Args:
                tifa:
                call_type:
                call_name:
                parameters:
                call_position:

            Returns:

            """
            function_scope = NewScope(self, definitions_scope)
            with function_scope:
                # Process arguments
                args = node.args.args
                if len(args) != len(parameters):
                    self._issue(
                        incorrect_arity(self.locate(),
                                        function_name,
                                        report=self.report))
                # TODO: Handle special types of parameters
                for arg, parameter in zip(args, parameters):
                    name = arg.arg
                    if arg.annotation:
                        self.visit(arg.annotation)
                        annotation = get_pedal_type_from_annotation(
                            arg.annotation, self)
                        # TODO: Use parameter information to "fill in" empty lists
                        if isinstance(parameter, ListType) and isinstance(
                                annotation, ListType):
                            if isinstance(parameter.subtype, UnknownType):
                                parameter.subtype = annotation.subtype
                        # TODO: Check that arg.type and parameter type match!
                        if not are_types_equal(annotation, parameter, True):
                            self._issue(
                                parameter_type_mismatch(
                                    self.locate(), name, annotation,
                                    parameter))
                    if parameter is not None:
                        parameter = parameter.clone_mutably()
                        self.create_variable(name, parameter, position)
                # Too many arguments
                if len(args) < len(parameters):
                    for undefined_parameter in parameters[len(args):]:
                        self.create_variable(name, UnknownType(), position)
                # Not enough arguments
                if len(args) > len(parameters):
                    for arg in args[len(parameters):]:
                        if arg.annotation:
                            self.visit(arg.annotation)
                            annotation = get_pedal_type_from_annotation(
                                arg.annotation, self)
                        else:
                            annotation = UnknownType()
                        self.create_variable(arg.arg, annotation, position)
                self.visit_statements(node.body)
                return_state = self.find_variable_scope("*return")
                return_value = NoneType()
                # TODO: Figure out if we are not returning something when we should
                # If the pseudo variable exists, we load it and get its type
                if return_state.exists and return_state.in_scope:
                    return_state = self.load_variable("*return", call_position)
                    return_value = return_state.type
                    if node.returns:
                        # self.visit(node.returns)
                        returns = get_pedal_type_from_annotation(
                            node.returns, self)
                        if not are_types_equal(return_value, returns, True):
                            self._issue(
                                multiple_return_types(
                                    return_state.position,
                                    returns.precise_description(),
                                    return_value.precise_description(),
                                    report=self.report))
            return return_value
Esempio n. 7
0
 def condition(self, value, expected_type):
     """ Tests if the left and right are equal """
     value_type = self.fields['value_type']
     expected_type = self.fields['expected_type']
     return not are_types_equal(value_type, expected_type)
Esempio n. 8
0
def ensure_function(name,
                    arity=None,
                    parameters=None,
                    returns=None,
                    root=None,
                    compliment=False,
                    **kwargs):
    """ Checks that the function exists and has the right signature. """
    report = kwargs.get('report', MAIN_REPORT)
    root = root or parse_program(report=report)
    defs = root.find_all('FunctionDef')
    defs = [a_def for a_def in defs if a_def._name == name]
    if not defs:
        return missing_function(name, **kwargs)
    if len(defs) > 1:
        lines = [Location.from_ast(a_def) for a_def in defs]
        return duplicate_function_definition(name, lines, **kwargs)
    definition = defs[0]
    # Actual logic
    # 1.2.1 'arity' style - simply checks number of parameters
    if arity is not None or parameters is not None:
        expected_arity = arity if arity is not None else len(parameters)
        actual_arity = len(definition.args.args)
        if actual_arity < expected_arity:
            return too_few_parameters(name, actual_arity, expected_arity,
                                      **kwargs)
        elif actual_arity > expected_arity:
            return too_many_parameters(name, actual_arity, expected_arity,
                                       **kwargs)
    # 1.2.2 'parameters' style - checks each parameter's name and type
    if parameters is not None:
        actual_parameters = definition.args.args
        for expected_parameter, actual_parameter in zip(
                parameters, actual_parameters):
            expected_parameter_type = normalize_type(expected_parameter)
            actual_parameter_name = (actual_parameter.id if actual_parameter.id
                                     is not None else actual_parameter.arg)
            if actual_parameter.annotation is None:
                return missing_parameter_type(name, actual_parameter_name,
                                              expected_parameter_type,
                                              **kwargs)
            try:
                actual_parameter_type = normalize_type(
                    actual_parameter.annotation.ast_node)
            except ValueError as e:
                return invalid_parameter_type(name, actual_parameter_name,
                                              actual_parameter.annotation,
                                              expected_parameter_type,
                                              **kwargs)
            if not are_types_equal(actual_parameter_type,
                                   expected_parameter_type):
                return wrong_parameter_type(name, actual_parameter_name,
                                            actual_parameter_type,
                                            expected_parameter_type, **kwargs)
    # 1.2.3. 'returns' style - checks the return type explicitly
    if returns is not None:
        expected_returns = normalize_type(returns)
        if definition.returns is None:
            return missing_return_type(name, expected_returns, **kwargs)
        try:
            actual_returns = normalize_type(definition.returns.ast_node)
        except ValueError as e:
            return invalid_return_type(name, definition.returns,
                                       expected_returns, **kwargs)
        if not are_types_equal(actual_returns, expected_returns):
            return wrong_return_type(name, actual_returns, expected_returns,
                                     **kwargs)
    # Alternatively, returns positive FF?
    if compliment:
        if isinstance(compliment, str):
            core_compliment(compliment, label="function_defined", **kwargs)
        elif compliment is True:
            core_compliment(f"Defined {name}",
                            label="function_defined",
                            **kwargs)
    elif kwargs.get("score"):
        give_partial(kwargs.pop("score"), label="function_defined", **kwargs)

    return None