Exemplo n.º 1
0
    def _eval(self, context: RuleContext) -> Optional[LintResult]:
        # We only care about commas.
        if context.segment.name != "comma":
            return None

        # Get subsequent whitespace segment and the first non-whitespace segment.
        subsequent_whitespace, first_non_whitespace = self._get_subsequent_whitespace(
            context
        )

        if (
            not subsequent_whitespace
            and (first_non_whitespace is not None)
            and (not first_non_whitespace.is_type("newline"))
        ):
            # No trailing whitespace and not followed by a newline,
            # therefore create a whitespace after the comma.
            return LintResult(
                anchor=first_non_whitespace,
                fixes=[LintFix.create_after(context.segment, [WhitespaceSegment()])],
            )
        elif (
            subsequent_whitespace
            and (subsequent_whitespace.raw != " ")
            and (first_non_whitespace is not None)
            and (not first_non_whitespace.is_comment)
        ):
            # Excess trailing whitespace therefore edit to only be one space long.
            return LintResult(
                anchor=subsequent_whitespace,
                fixes=[LintFix.replace(subsequent_whitespace, [WhitespaceSegment()])],
            )

        return None
Exemplo n.º 2
0
    def _eval(self, context: RuleContext) -> Optional[LintResult]:
        """Files must end with a single trailing newline.

        We only care about the segment and the siblings which come after it
        for this rule, we discard the others into the kwargs argument.

        """
        # We only care about the final segment of the parse tree.
        if not self.is_final_segment(context):
            return None

        # Include current segment for complete stack and reverse.
        parent_stack: Segments = context.functional.parent_stack
        complete_stack: Segments = (context.functional.raw_stack +
                                    context.functional.segment)
        reversed_complete_stack = complete_stack.reversed()

        # Find the trailing newline segments.
        trailing_newlines = reversed_complete_stack.select(
            select_if=sp.is_type("newline"),
            loop_while=sp.or_(sp.is_whitespace(), sp.is_type("dedent")),
        )
        trailing_literal_newlines = trailing_newlines.select(
            loop_while=lambda seg: sp.templated_slices(
                seg, context.templated_file).all(tsp.is_slice_type("literal")))

        if not trailing_literal_newlines:
            # We make an edit to create this segment after the child of the FileSegment.
            if len(parent_stack) == 1:
                fix_anchor_segment = context.segment
            else:
                fix_anchor_segment = parent_stack[1]

            return LintResult(
                anchor=context.segment,
                fixes=[
                    LintFix.create_after(
                        fix_anchor_segment,
                        [NewlineSegment()],
                    )
                ],
            )
        elif len(trailing_literal_newlines) > 1:
            # Delete extra newlines.
            return LintResult(
                anchor=context.segment,
                fixes=[
                    LintFix.delete(d) for d in trailing_literal_newlines[1:]
                ],
            )
        else:
            # Single newline, no need for fix.
            return None
Exemplo n.º 3
0
    def _eval(self, context: RuleContext) -> Optional[LintResult]:
        """Fully qualify JOINs."""
        # Config type hints
        self.fully_qualify_join_types: str

        # We are only interested in JOIN clauses.
        if not context.segment.is_type("join_clause"):
            return None

        join_clause_keywords = [
            segment for segment in context.segment.segments if segment.type == "keyword"
        ]

        # Identify LEFT/RIGHT/OUTER JOIN and if the next keyword is JOIN.
        if (
            self.fully_qualify_join_types in ["outer", "both"]
            and join_clause_keywords[0].raw_upper in ["RIGHT", "LEFT", "FULL"]
            and join_clause_keywords[1].raw_upper == "JOIN"
        ):
            # Define basic-level OUTER capitalization based on JOIN
            outer_kw = ("outer", "OUTER")[join_clause_keywords[1].raw == "JOIN"]
            # Insert OUTER after LEFT/RIGHT/FULL
            return LintResult(
                context.segment.segments[0],
                fixes=[
                    LintFix.create_after(
                        context.segment.segments[0],
                        [WhitespaceSegment(), KeywordSegment(outer_kw)],
                    )
                ],
            )

        # Identify lone JOIN by looking at first child segment.
        if (
            self.fully_qualify_join_types in ["inner", "both"]
            and join_clause_keywords[0].raw_upper == "JOIN"
        ):
            # Define basic-level INNER capitalization based on JOIN
            inner_kw = ("inner", "INNER")[join_clause_keywords[0].raw == "JOIN"]
            # Replace lone JOIN with INNER JOIN.
            return LintResult(
                context.segment.segments[0],
                fixes=[
                    LintFix.create_before(
                        context.segment.segments[0],
                        [KeywordSegment(inner_kw), WhitespaceSegment()],
                    )
                ],
            )

        return None
Exemplo n.º 4
0
                    def _fixes_for_move_after_select_clause(
                        stop_seg: BaseSegment,
                        add_newline: bool = True,
                    ) -> List[LintFix]:
                        """Cleans up by moving leftover select_clause segments.

                        Context: Some of the other fixes we make in
                        _eval_single_select_target_element() leave the
                        select_clause in an illegal state -- a select_clause's
                        *rightmost children cannot be whitespace or comments*.
                        This function addresses that by moving these segments
                        up the parse tree to an ancestor segment chosen by
                        _choose_anchor_segment(). After these fixes are applied,
                        these segments may, for example, be *siblings* of
                        select_clause.
                        """
                        start_seg = (
                            modifier[0] if modifier else select_children[
                                select_targets_info.first_new_line_idx])
                        move_after_select_clause = select_children.select(
                            start_seg=start_seg,
                            stop_seg=stop_seg,
                        )
                        fixes = [
                            LintFix.delete(seg)
                            for seg in move_after_select_clause
                        ]
                        fixes.append(
                            LintFix.create_after(
                                self._choose_anchor_segment(
                                    context,
                                    "create_after",
                                    select_clause[0],
                                    filter_meta=True,
                                ),
                                ([NewlineSegment()] if add_newline else []) +
                                list(move_after_select_clause),
                            ))
                        return fixes
Exemplo n.º 5
0
    def _eval(self, context: RuleContext) -> Optional[LintResult]:
        """Select clause modifiers must appear on same line as SELECT."""
        # We only care about select_clause.
        if not context.segment.is_type("select_clause"):
            return None

        # Get children of select_clause and the corresponding select keyword.
        child_segments = context.functional.segment.children()
        select_keyword = child_segments[0]

        # See if we have a select_clause_modifier.
        select_clause_modifier_seg = child_segments.first(
            sp.is_type("select_clause_modifier"))

        # Rule doesn't apply if there's no select clause modifier.
        if not select_clause_modifier_seg:
            return None

        select_clause_modifier = select_clause_modifier_seg[0]

        # Are there any newlines between the select keyword
        # and the select clause modifier.
        leading_newline_segments = child_segments.select(
            select_if=sp.is_type("newline"),
            loop_while=sp.or_(sp.is_whitespace(), sp.is_meta()),
            start_seg=select_keyword,
        )

        # Rule doesn't apply if select clause modifier
        # is already on the same line as the select keyword.
        if not leading_newline_segments:
            return None

        # We should also check if the following select clause element
        # is on the same line as the select clause modifier.
        trailing_newline_segments = child_segments.select(
            select_if=sp.is_type("newline"),
            loop_while=sp.or_(sp.is_whitespace(), sp.is_meta()),
            start_seg=select_clause_modifier,
        )

        # We will insert these segments directly after the select keyword.
        edit_segments = [
            WhitespaceSegment(),
            select_clause_modifier,
        ]
        if not trailing_newline_segments:
            # if the first select clause element is on the same line
            # as the select clause modifier then also insert a newline.
            edit_segments.append(NewlineSegment())

        fixes = []
        # Move select clause modifier after select keyword.
        fixes.append(
            LintFix.create_after(
                anchor_segment=select_keyword,
                edit_segments=edit_segments,
            ))
        # Delete original newlines between select keyword and select clause modifier
        # and delete the original select clause modifier.
        fixes.extend((LintFix.delete(s) for s in leading_newline_segments))
        fixes.append(LintFix.delete(select_clause_modifier))

        # If there is whitespace (on the same line) after the select clause modifier
        # then also delete this.
        trailing_whitespace_segments = child_segments.select(
            select_if=sp.is_whitespace(),
            loop_while=sp.or_(sp.is_type("whitespace"), sp.is_meta()),
            start_seg=select_clause_modifier,
        )
        if trailing_whitespace_segments:
            fixes.extend(
                (LintFix.delete(s) for s in trailing_whitespace_segments))

        return LintResult(
            anchor=context.segment,
            fixes=fixes,
        )