예제 #1
0
    def _check_ignore_assignment_expr_suggestion(node: nodes.If,
                                                 name: Optional[str]) -> bool:
        """Return True if suggestion for assignment expr should be ignored.

        E.g., in cases where a match statement would be a better fit
        (multiple conditions).
        """
        if isinstance(node.test, nodes.Compare):
            next_if_node: Optional[nodes.If] = None
            next_sibling = node.next_sibling()
            if len(node.orelse) == 1 and isinstance(node.orelse[0], nodes.If):
                # elif block
                next_if_node = node.orelse[0]
            elif isinstance(next_sibling, nodes.If):
                # separate if block
                next_if_node = next_sibling

            if (  # pylint: disable=too-many-boolean-expressions
                    next_if_node is not None
                    and (isinstance(next_if_node.test, nodes.Compare)
                         and isinstance(next_if_node.test.left, nodes.Name)
                         and next_if_node.test.left.name == name
                         or isinstance(next_if_node.test, nodes.Name)
                         and next_if_node.test.name == name)):
                return True
        return False
예제 #2
0
    def visit_if(self, node: nodes.If) -> None:
        self._current_block.add_statement(node.test)
        node.cfg_block = self._current_block
        old_curr = self._current_block

        # Handle "then" branch.
        then_block = self._current_cfg.create_block(old_curr)
        self._current_block = then_block
        for child in node.body:
            child.accept(self)
        end_if = self._current_block

        # Handle "else" branch.
        if node.orelse == []:
            end_else = old_curr
        else:
            else_block = self._current_cfg.create_block(old_curr)
            self._current_block = else_block
            for child in node.orelse:
                child.accept(self)
            end_else = self._current_block

        after_if_block = self._current_cfg.create_block()
        self._current_cfg.link_or_merge(end_if, after_if_block)
        self._current_cfg.link_or_merge(end_else, after_if_block)

        self._current_block = after_if_block
예제 #3
0
    def _check_consider_using_assignment_expr(self, node: nodes.If) -> None:
        """Check if an assignment expression (walrus operator) can be used.

        For example if an assignment is directly followed by an if statement:
        >>> x = 2
        >>> if x:
        >>>     ...

        Can be replaced by:
        >>> if (x := 2):
        >>>     ...

        Note: Assignment expressions were added in Python 3.8
        """
        # Check if `node.test` contains a `Name` node
        node_name: Optional[nodes.Name] = None
        if isinstance(node.test, nodes.Name):
            node_name = node.test
        elif (isinstance(node.test, nodes.UnaryOp) and node.test.op == "not"
              and isinstance(node.test.operand, nodes.Name)):
            node_name = node.test.operand
        elif (isinstance(node.test, nodes.Compare)
              and isinstance(node.test.left, nodes.Name)
              and len(node.test.ops) == 1):
            node_name = node.test.left
        else:
            return

        # Make sure the previous node is an assignment to the same name
        # used in `node.test`. Furthermore, ignore if assignment spans multiple lines.
        prev_sibling = node.previous_sibling()
        if CodeStyleChecker._check_prev_sibling_to_if_stmt(
                prev_sibling, node_name.name):

            # Check if match statement would be a better fit.
            # I.e. multiple ifs that test the same name.
            if CodeStyleChecker._check_ignore_assignment_expr_suggestion(
                    node, node_name.name):
                return

            # Build suggestion string. Check length of suggestion
            # does not exceed max-line-length-suggestions
            test_str = node.test.as_string().replace(
                node_name.name,
                f"({node_name.name} := {prev_sibling.value.as_string()})",
                1,
            )
            suggestion = f"if {test_str}:"
            if (node.col_offset is not None
                    and len(suggestion) + node.col_offset > self._max_length
                    or len(suggestion) > self._max_length):
                return

            self.add_message(
                "consider-using-assignment-expr",
                node=node_name,
                args=(suggestion, ),
            )
예제 #4
0
 def visit_if(self, node: nodes.If) -> None:
     body_ends_with_if = isinstance(
         node.body[-1], nodes.If
     ) and self._has_no_else_clause(node.body[-1])
     if node.has_elif_block() and body_ends_with_if:
         self.add_message("confusing-consecutive-elif", node=node.orelse[0])