コード例 #1
0
    def _eval(self, context: RuleContext) -> Optional[LintResult]:
        """Use ``!=`` instead of ``<>`` for "not equal to" comparison."""
        # Only care about not_equal_to segments. We should only get
        # comparison operator types from the crawler, but not all will
        # be "not_equal_to".
        if context.segment.name != "not_equal_to":
            return None

        # Get the comparison operator children
        raw_comparison_operators = (
            FunctionalContext(context)
            .segment.children()
            .select(select_if=sp.is_type("raw_comparison_operator"))
        )

        # Only care about ``<>``
        if [r.raw for r in raw_comparison_operators] != ["<", ">"]:
            return None

        # Provide a fix and replace ``<>`` with ``!=``
        # As each symbol is a separate symbol this is done in two steps:
        # 1. Replace < with !
        # 2. Replace > with =
        fixes = [
            LintFix.replace(
                raw_comparison_operators[0],
                [SymbolSegment(raw="!", type="raw_comparison_operator")],
            ),
            LintFix.replace(
                raw_comparison_operators[1],
                [SymbolSegment(raw="=", type="raw_comparison_operator")],
            ),
        ]

        return LintResult(context.segment, fixes)
コード例 #2
0
ファイル: L008.py プロジェクト: sqlfluff/sqlfluff
    def _eval(self, context: RuleContext) -> Optional[LintResult]:
        # We only care about commas.
        assert context.segment.is_type("comma")

        # 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
コード例 #3
0
ファイル: L038.py プロジェクト: sqlfluff/sqlfluff
    def _eval(self, context: RuleContext) -> Optional[LintResult]:
        """Trailing commas within select clause."""
        # Config type hints
        self.select_clause_trailing_comma: str

        segment = FunctionalContext(context).segment
        children = segment.children()
        # Iterate content to find last element
        last_content: BaseSegment = children.last(sp.is_code())[0]

        # What mode are we in?
        if self.select_clause_trailing_comma == "forbid":
            # Is it a comma?
            if last_content.is_type("comma"):
                return LintResult(
                    anchor=last_content,
                    fixes=[LintFix.delete(last_content)],
                    description="Trailing comma in select statement forbidden",
                )
        elif self.select_clause_trailing_comma == "require":
            if not last_content.is_type("comma"):
                new_comma = SymbolSegment(",", type="comma")
                return LintResult(
                    anchor=last_content,
                    fixes=[
                        LintFix.replace(last_content,
                                        [last_content, new_comma])
                    ],
                    description="Trailing comma in select statement required",
                )
        return None
コード例 #4
0
    def _eval(self, context: RuleContext) -> Optional[LintResult]:
        """Find rule violations and provide fixes.

        0. Look for a case expression
        1. Look for "ELSE"
        2. Mark "ELSE" for deletion (populate "fixes")
        3. Backtrack and mark all newlines/whitespaces for deletion
        4. Look for a raw "NULL" segment
        5.a. The raw "NULL" segment is found, we mark it for deletion and return
        5.b. We reach the end of case when without matching "NULL": the rule passes
        """
        assert context.segment.is_type("case_expression")
        children = FunctionalContext(context).segment.children()
        else_clause = children.first(sp.is_type("else_clause"))

        # Does the "ELSE" have a "NULL"? NOTE: Here, it's safe to look for
        # "NULL", as an expression would *contain* NULL but not be == NULL.
        if else_clause and else_clause.children(
                lambda child: child.raw_upper == "NULL"):
            # Found ELSE with NULL. Delete the whole else clause as well as
            # indents/whitespaces/meta preceding the ELSE. :TRICKY: Note
            # the use of reversed() to make select() effectively search in
            # reverse.
            before_else = children.reversed().select(
                start_seg=else_clause[0],
                loop_while=sp.or_(sp.is_name("whitespace", "newline"),
                                  sp.is_meta()),
            )
            return LintResult(
                anchor=context.segment,
                fixes=[LintFix.delete(else_clause[0])] +
                [LintFix.delete(seg) for seg in before_else],
            )
        return None
コード例 #5
0
 def _create_semicolon_and_delete_whitespace(
     self,
     target_segment: BaseSegment,
     parent_segment: BaseSegment,
     anchor_segment: BaseSegment,
     whitespace_deletions: Segments,
     create_segments: List[BaseSegment],
 ) -> List[LintFix]:
     anchor_segment = self._choose_anchor_segment(parent_segment,
                                                  "create_after",
                                                  anchor_segment,
                                                  filter_meta=True)
     lintfix_fn = LintFix.create_after
     # :TRICKY: Use IdentitySet rather than set() since
     # different segments may compare as equal.
     whitespace_deletion_set = IdentitySet(whitespace_deletions)
     if anchor_segment in whitespace_deletion_set:
         # Can't delete() and create_after() the same segment. Use replace()
         # instead.
         lintfix_fn = LintFix.replace
         whitespace_deletions = whitespace_deletions.select(
             lambda seg: seg is not anchor_segment)
     fixes = [
         lintfix_fn(
             anchor_segment,
             create_segments,
         ),
         LintFix.delete(target_segment, ),
     ]
     fixes.extend(LintFix.delete(d) for d in whitespace_deletions)
     return fixes
コード例 #6
0
ファイル: L058.py プロジェクト: sqlfluff/sqlfluff
    def _eval(self, context: RuleContext) -> LintResult:
        """Nested CASE statement in ELSE clause could be flattened."""
        segment = FunctionalContext(context).segment
        assert segment.select(sp.is_type("case_expression"))
        case1_children = segment.children()
        case1_last_when = case1_children.last(sp.is_type("when_clause")).get()
        case1_else_clause = case1_children.select(sp.is_type("else_clause"))
        case1_else_expressions = case1_else_clause.children(
            sp.is_type("expression"))
        expression_children = case1_else_expressions.children()
        case2 = expression_children.select(sp.is_type("case_expression"))
        # The len() checks below are for safety, to ensure the CASE inside
        # the ELSE is not part of a larger expression. In that case, it's
        # not safe to simplify in this way -- we'd be deleting other code.
        if (not case1_last_when or len(case1_else_expressions) > 1
                or len(expression_children) > 1 or not case2):
            return LintResult()

        # We can assert that this exists because of the previous check.
        assert case1_last_when
        # We can also assert that we'll also have an else clause because
        # otherwise the case2 check above would fail.
        case1_else_clause_seg = case1_else_clause.get()
        assert case1_else_clause_seg

        # Delete stuff between the last "WHEN" clause and the "ELSE" clause.
        case1_to_delete = case1_children.select(start_seg=case1_last_when,
                                                stop_seg=case1_else_clause_seg)

        # Delete the nested "CASE" expression.
        fixes = case1_to_delete.apply(lambda seg: LintFix.delete(seg))

        # Determine the indentation to use when we move the nested "WHEN"
        # and "ELSE" clauses, based on the indentation of case1_last_when.
        # If no whitespace segments found, use default indent.
        indent = (case1_children.select(
            stop_seg=case1_last_when).reversed().select(
                sp.is_type("whitespace")))
        indent_str = "".join(seg.raw
                             for seg in indent) if indent else self.indent

        # Move the nested "when" and "else" clauses after the last outer
        # "when".
        nested_clauses = case2.children(
            sp.is_type("when_clause", "else_clause"))
        create_after_last_when = nested_clauses.apply(
            lambda seg: [NewlineSegment(),
                         WhitespaceSegment(indent_str), seg])
        segments = [
            item for sublist in create_after_last_when for item in sublist
        ]
        fixes.append(
            LintFix.create_after(case1_last_when, segments, source=segments))

        # Delete the outer "else" clause.
        fixes.append(LintFix.delete(case1_else_clause_seg))
        return LintResult(case2[0], fixes=fixes)
コード例 #7
0
    def _eval(self, context: RuleContext) -> LintResult:
        """Look for UNION keyword not immediately followed by DISTINCT or ALL.

        Note that UNION DISTINCT is valid, rule only applies to bare UNION.
        The function does this by looking for a segment of type set_operator
        which has a UNION but no DISTINCT or ALL.

        Note only some dialects have concept of UNION DISTINCT, so rule is only
        applied to dialects that are known to support this syntax.
        """
        if context.dialect.name not in [
                "ansi",
                "hive",
                "mysql",
                "redshift",
        ]:
            return LintResult()

        assert context.segment.is_type("set_operator")
        if "union" in context.segment.raw and not (
                "ALL" in context.segment.raw.upper()
                or "DISTINCT" in context.segment.raw.upper()):
            return LintResult(
                anchor=context.segment,
                fixes=[
                    LintFix.replace(
                        context.segment.segments[0],
                        [
                            KeywordSegment("union"),
                            WhitespaceSegment(),
                            KeywordSegment("distinct"),
                        ],
                    )
                ],
            )
        elif "UNION" in context.segment.raw.upper() and not (
                "ALL" in context.segment.raw.upper()
                or "DISTINCT" in context.segment.raw.upper()):
            return LintResult(
                anchor=context.segment,
                fixes=[
                    LintFix.replace(
                        context.segment.segments[0],
                        [
                            KeywordSegment("UNION"),
                            WhitespaceSegment(),
                            KeywordSegment("DISTINCT"),
                        ],
                    )
                ],
            )
        return LintResult()
コード例 #8
0
    def _eval_multiple_select_target_elements(self, select_targets_info, segment):
        """Multiple select targets. Ensure each is on a separate line."""
        # Insert newline before every select target.
        fixes = []
        for i, select_target in enumerate(select_targets_info.select_targets):
            base_segment = (
                segment if not i else select_targets_info.select_targets[i - 1]
            )
            if (
                base_segment.pos_marker.working_line_no
                == select_target.pos_marker.working_line_no
            ):
                # Find and delete any whitespace before the select target.
                start_seg = select_targets_info.select_idx
                # If any select modifier (e.g. distinct ) is present, start
                # there rather than at the beginning.
                modifier = segment.get_child("select_clause_modifier")
                if modifier:
                    start_seg = segment.segments.index(modifier)

                ws_to_delete = segment.select_children(
                    start_seg=segment.segments[start_seg]
                    if not i
                    else select_targets_info.select_targets[i - 1],
                    select_if=lambda s: s.is_type("whitespace"),
                    loop_while=lambda s: s.is_type("whitespace", "comma") or s.is_meta,
                )
                fixes += [LintFix.delete(ws) for ws in ws_to_delete]
                fixes.append(LintFix.create_before(select_target, [NewlineSegment()]))

            # If we are at the last select target check if the FROM clause
            # is on the same line, and if so move it to its own line.
            if select_targets_info.from_segment:
                if (i + 1 == len(select_targets_info.select_targets)) and (
                    select_target.pos_marker.working_line_no
                    == select_targets_info.from_segment.pos_marker.working_line_no
                ):
                    fixes.extend(
                        [
                            LintFix.delete(ws)
                            for ws in select_targets_info.pre_from_whitespace
                        ]
                    )
                    fixes.append(
                        LintFix.create_before(
                            select_targets_info.from_segment,
                            [NewlineSegment()],
                        )
                    )

        if fixes:
            return LintResult(anchor=segment, fixes=fixes)
コード例 #9
0
ファイル: L009.py プロジェクト: sqlfluff/sqlfluff
    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.
        parent_stack, segment = get_last_segment(FunctionalContext(context).segment)
        self.logger.debug("Found last segment as: %s", segment)

        trailing_newlines = Segments(*get_trailing_newlines(context.segment))
        trailing_literal_newlines = trailing_newlines
        self.logger.debug(
            "Untemplated trailing newlines: %s", trailing_literal_newlines
        )
        if context.templated_file:
            trailing_literal_newlines = trailing_newlines.select(
                loop_while=lambda seg: sp.templated_slices(
                    seg, context.templated_file
                ).all(tsp.is_slice_type("literal"))
            )
        self.logger.debug("Templated trailing newlines: %s", trailing_literal_newlines)
        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 = segment[0]
            else:
                fix_anchor_segment = parent_stack[1]
            self.logger.debug("Anchor on: %s", fix_anchor_segment)

            return LintResult(
                anchor=segment[0],
                fixes=[
                    LintFix.create_after(
                        fix_anchor_segment,
                        [NewlineSegment()],
                    )
                ],
            )
        elif len(trailing_literal_newlines) > 1:
            # Delete extra newlines.
            return LintResult(
                anchor=segment[0],
                fixes=[LintFix.delete(d) for d in trailing_literal_newlines[1:]],
            )
        else:
            # Single newline, no need for fix.
            return None
コード例 #10
0
ファイル: L065.py プロジェクト: sqlfluff/sqlfluff
def _generate_fixes(
    whitespace_segment: Optional[BaseSegment],
) -> Optional[List[LintFix]]:

    if whitespace_segment:
        return [
            LintFix.replace(
                anchor_segment=whitespace_segment,
                # NB: Currently we are just inserting a Newline here. This alone will
                # produce not properly indented SQL. We rely on L003 to deal with
                # indentation later.
                # As a future improvement we could maybe add WhitespaceSegment( ... )
                # here directly.
                edit_segments=[NewlineSegment()],
            )
        ]
    else:
        # We should rarely reach here as set operators are always surrounded by either
        # WhitespaceSegment or NewlineSegment.
        # However, in exceptional cases the WhitespaceSegment might be enclosed in the
        # surrounding segment hierachy and not accessible by the rule logic.
        # At the time of writing this is true for `tsql` as covered in the test
        # `test_fail_autofix_in_tsql_disabled`. If we encounter such case, we skip
        # fixing.
        return []
コード例 #11
0
    def _get_fix(self, segment, fixed_raw):
        """Given a segment found to have a fix, returns a LintFix for it.

        May be overridden by subclasses, which is useful when the parse tree
        structure varies from this simple base case.
        """
        return LintFix.replace(segment, [segment.edit(fixed_raw)])
コード例 #12
0
ファイル: L043.py プロジェクト: sqlfluff/sqlfluff
    def _coalesce_fix_list(
        context: RuleContext,
        coalesce_arg_1: BaseSegment,
        coalesce_arg_2: BaseSegment,
        preceding_not: bool = False,
    ) -> List[LintFix]:
        """Generate list of fixes to convert CASE statement to COALESCE function."""
        # Add coalesce and opening parenthesis.
        edits = [
            KeywordSegment("coalesce"),
            SymbolSegment("(", type="start_bracket"),
            coalesce_arg_1,
            SymbolSegment(",", type="comma"),
            WhitespaceSegment(),
            coalesce_arg_2,
            SymbolSegment(")", type="end_bracket"),
        ]

        if preceding_not:
            not_edits: List[BaseSegment] = [
                KeywordSegment("not"),
                WhitespaceSegment(),
            ]
            edits = not_edits + edits

        fixes = [LintFix.replace(
            context.segment,
            edits,
        )]
        return fixes
コード例 #13
0
    def _eval(self, context: RuleContext) -> LintResult:
        """Function name not immediately followed by bracket.

        Look for Function Segment with anything other than the
        function name before brackets
        """
        segment = FunctionalContext(context).segment
        # We only trigger on start_bracket (open parenthesis)
        assert segment.all(sp.is_type("function"))
        children = segment.children()

        function_name = children.first(sp.is_type("function_name"))[0]
        start_bracket = children.first(sp.is_type("bracketed"))[0]

        intermediate_segments = children.select(start_seg=function_name,
                                                stop_seg=start_bracket)
        if intermediate_segments:
            # It's only safe to fix if there is only whitespace
            # or newlines in the intervening section.
            if intermediate_segments.all(sp.is_type("whitespace", "newline")):
                return LintResult(
                    anchor=intermediate_segments[0],
                    fixes=[
                        LintFix.delete(seg) for seg in intermediate_segments
                    ],
                )
            else:
                # It's not all whitespace, just report the error.
                return LintResult(anchor=intermediate_segments[0], )
        return LintResult()
コード例 #14
0
    def _eval(self, context: RuleContext) -> Optional[LintResult]:
        """Use ``COALESCE`` instead of ``IFNULL`` or ``NVL``."""
        # We only care about function names, and they should be the
        # only things we get
        assert context.segment.is_type("function_name_identifier")

        # Only care if the function is ``IFNULL`` or ``NVL``.
        if context.segment.raw_upper not in {"IFNULL", "NVL"}:
            return None

        # Create fix to replace ``IFNULL`` or ``NVL`` with ``COALESCE``.
        fix = LintFix.replace(
            context.segment,
            [CodeSegment(
                raw="COALESCE",
                type="function_name_identifier",
            )],
        )

        return LintResult(
            anchor=context.segment,
            fixes=[fix],
            description=
            f"Use 'COALESCE' instead of '{context.segment.raw_upper}'.",
        )
コード例 #15
0
    def _eval(self, context: RuleContext) -> Optional[LintResult]:
        """Mixed Tabs and Spaces in single whitespace.

        Only trigger from whitespace segments if they contain
        multiple kinds of whitespace.
        """
        # Config type hints
        self.tab_space_size: int

        if context.segment.is_type("whitespace"):
            if " " in context.segment.raw and "\t" in context.segment.raw:
                if not context.raw_stack or context.raw_stack[-1].is_type("newline"):
                    # We've got a single whitespace at the beginning of a line.
                    # It's got a mix of spaces and tabs. Replace each tab with
                    # a multiple of spaces
                    return LintResult(
                        anchor=context.segment,
                        fixes=[
                            LintFix.replace(
                                context.segment,
                                [
                                    context.segment.edit(
                                        context.segment.raw.replace(
                                            "\t", " " * self.tab_space_size
                                        )
                                    ),
                                ],
                            ),
                        ],
                    )
        return None
コード例 #16
0
ファイル: L023.py プロジェクト: sqlfluff/sqlfluff
 def _eval(self, context: RuleContext) -> Optional[List[LintResult]]:
     """Single whitespace expected in mother middle segment."""
     error_buffer: List[LintResult] = []
     last_code = None
     mid_segs: List[BaseSegment] = []
     for seg in context.segment.iter_segments(
             expanding=self.expand_children):
         if seg.is_code:
             if (last_code and self.matches_target_tuples(
                     last_code, [self.pre_segment_identifier])
                     and self.matches_target_tuples(
                         seg, [self.post_segment_identifier])):
                 # Do we actually have the right amount of whitespace?
                 raw_inner = "".join(s.raw for s in mid_segs)
                 if raw_inner != " " and not (self.allow_newline and any(
                         s.is_type("newline") for s in mid_segs)):
                     if not raw_inner.strip():
                         # There's some whitespace and/or newlines, or nothing
                         fixes = []
                         if raw_inner:
                             # There's whitespace and/or newlines. Drop those.
                             fixes += [
                                 LintFix.delete(mid_seg)
                                 for mid_seg in mid_segs
                             ]
                         # Enforce a single space
                         fixes += [
                             LintFix.create_before(
                                 seg,
                                 [WhitespaceSegment()],
                             )
                         ]
                     else:
                         # Don't otherwise suggest a fix for now.
                         # Only whitespace & newlines are covered.
                         # At least a comment section between `AS` and `(` can
                         # result in an unfixable error.
                         # TODO: Enable more complex fixing here.
                         fixes = None  # pragma: no cover
                     error_buffer.append(
                         LintResult(anchor=last_code, fixes=fixes))
             mid_segs = []
             if not seg.is_meta:
                 last_code = seg
         else:
             mid_segs.append(seg)
     return error_buffer or None
コード例 #17
0
                    def _fixes_for_move_after_select_clause(
                        stop_seg: BaseSegment,
                        delete_segments: Optional[Segments] = None,
                        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 leftover
                        child segments that need to be moved to become
                        *siblings* of the 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,
                        )
                        # :TRICKY: Below, we have a couple places where we
                        # filter to guard against deleting the same segment
                        # multiple times -- this is illegal.
                        # :TRICKY: Use IdentitySet rather than set() since
                        # different segments may compare as equal.
                        all_deletes = IdentitySet(
                            fix.anchor for fix in fixes if fix.edit_type == "delete"
                        )
                        fixes_ = []
                        for seg in delete_segments or []:
                            if seg not in all_deletes:
                                fixes.append(LintFix.delete(seg))
                                all_deletes.add(seg)
                        fixes_ += [
                            LintFix.delete(seg)
                            for seg in move_after_select_clause
                            if seg not in all_deletes
                        ]
                        fixes_.append(
                            LintFix.create_after(
                                select_clause[0],
                                ([NewlineSegment()] if add_newline else [])
                                + list(move_after_select_clause),
                            )
                        )
                        return fixes_
コード例 #18
0
    def _eval(self, context: RuleContext) -> List[LintResult]:
        """Top-level statements should not be wrapped in brackets."""
        # Because of the root_only_crawler, this can control its own
        # crawling behaviour.
        results = []
        for parent, bracketed_segment in self._iter_bracketed_statements(
                context.segment):
            self.logger.debug("Evaluating %s in %s", bracketed_segment, parent)
            # Replace the bracketed segment with it's
            # children, excluding the bracket symbols.
            bracket_set = {"start_bracket", "end_bracket"}

            filtered_children = Segments(*[
                segment for segment in bracketed_segment.segments if
                segment.get_type() not in bracket_set and not segment.is_meta
            ])

            # Lift leading/trailing whitespace and inline comments to the
            # segment above. This avoids introducing a parse error (ANSI and other
            # dialects generally don't allow this at lower levels of the parse
            # tree).
            to_lift_predicate = sp.or_(sp.is_whitespace(),
                                       sp.is_name("inline_comment"))
            leading = filtered_children.select(loop_while=to_lift_predicate)
            self.logger.debug("Leading: %s", leading)
            trailing = (filtered_children.reversed().select(
                loop_while=to_lift_predicate).reversed())
            self.logger.debug("Trailing: %s", trailing)
            lift_nodes = IdentitySet(leading + trailing)
            fixes = []
            if lift_nodes:
                fixes.append(LintFix.create_before(parent, list(leading)))
                fixes.append(LintFix.create_after(parent, list(trailing)))
                fixes.extend(
                    [LintFix.delete(segment) for segment in lift_nodes])
                filtered_children = filtered_children[len(leading
                                                          ):-len(trailing)]

            fixes.append(
                LintFix.replace(
                    bracketed_segment,
                    filtered_children,
                ))

            results.append(LintResult(anchor=bracketed_segment, fixes=fixes))
        return results
コード例 #19
0
 def _report_unused_alias(cls, alias: AliasInfo) -> LintResult:
     fixes = [LintFix.delete(alias.alias_expression)]  # type: ignore
     # Walk back to remove indents/whitespaces
     to_delete = (
         Segments(
             *alias.from_expression_element.segments).reversed().select(
                 start_seg=alias.alias_expression,
                 # Stop once we reach an other, "regular" segment.
                 loop_while=sp.or_(sp.is_whitespace(), sp.is_meta()),
             ))
     fixes += [LintFix.delete(seg) for seg in to_delete]
     return LintResult(
         anchor=alias.segment,
         description="Alias {!r} is never used in SELECT statement.".format(
             alias.ref_str),
         fixes=fixes,
     )
コード例 #20
0
    def _eval(self, context: RuleContext) -> LintResult:
        """Incorrect indentation found in file."""
        # Config type hints
        self.tab_space_size: int
        self.indent_unit: str

        tab = "\t"
        space = " "
        correct_indent = self.indent
        wrong_indent = (
            tab if self.indent_unit == "space" else space * self.tab_space_size
        )
        if (
            context.segment.is_type("whitespace")
            and wrong_indent in context.segment.raw
        ):
            fixes = []
            description = "Incorrect indentation type found in file."
            edit_indent = context.segment.raw.replace(wrong_indent, correct_indent)
            pre_seg = context.raw_stack[-1] if context.raw_stack else None
            # Ensure that the number of space indents is a multiple of tab_space_size
            # before attempting to convert spaces to tabs to avoid mixed indents
            # unless we are converted tabs to spaces (indent_unit = space)
            if (
                (
                    self.indent_unit == "space"
                    or context.segment.raw.count(space) % self.tab_space_size == 0
                )
                # Only attempt a fix at the start of a newline for now
                and (pre_seg is None or pre_seg.is_type("newline"))
            ):
                fixes = [
                    LintFix.replace(
                        context.segment,
                        [
                            WhitespaceSegment(raw=edit_indent),
                        ],
                    )
                ]
            elif not (pre_seg is None or pre_seg.is_type("newline")):
                # give a helpful message if the wrong indent has been found and is not
                # at the start of a newline
                description += (
                    " The indent occurs after other text, so a manual fix is needed."
                )
            else:
                # If we get here, the indent_unit is tabs, and the number of spaces is
                # not a multiple of tab_space_size
                description += " The number of spaces is not a multiple of "
                "tab_space_size, so a manual fix is needed."
            return LintResult(
                anchor=context.segment, fixes=fixes, description=description
            )
        return LintResult()
コード例 #21
0
 def _eval(self, context):
     """Stars make newlines."""
     if context.segment.is_type("whitespace"):
         return LintResult(
             anchor=context.segment,
             fixes=[
                 LintFix.replace(
                     context.segment,
                     [WhitespaceSegment(context.segment.raw + " ")])
             ],
         )
コード例 #22
0
    def _coerce_indent_to(
        self,
        desired_indent: str,
        current_indent_buffer: List[BaseSegment],
        current_anchor: BaseSegment,
    ) -> List[LintFix]:
        """Generate fixes to make an indent a certain size.

        Rather than blindly creating indent, we should _edit_
        if at all possible, this stops other rules trying to
        remove floating double indents.
        """
        existing_whitespace = [
            seg for seg in current_indent_buffer if seg.is_type("whitespace")
        ]
        # Should we have an indent?
        if len(desired_indent) == 0:
            # No? Just delete everything
            return [LintFix.delete(seg) for seg in existing_whitespace]
        else:
            # Is there already an indent?
            if existing_whitespace:
                # Edit the first, delete the rest.
                edit_fix = LintFix.replace(
                    existing_whitespace[0],
                    [existing_whitespace[0].edit(desired_indent)],
                )
                delete_fixes = [
                    LintFix.delete(seg) for seg in existing_whitespace[1:]
                ]
                return [edit_fix] + delete_fixes
            else:
                # Just create an indent.
                return [
                    LintFix.create_before(
                        current_anchor,
                        [
                            WhitespaceSegment(raw=desired_indent, ),
                        ],
                    )
                ]
コード例 #23
0
ファイル: L043.py プロジェクト: sqlfluff/sqlfluff
 def _column_only_fix_list(
     context: RuleContext,
     column_reference_segment: BaseSegment,
 ) -> List[LintFix]:
     """Generate list of fixes to reduce CASE statement to a single column."""
     fixes = [
         LintFix.replace(
             context.segment,
             [column_reference_segment],
         )
     ]
     return fixes
コード例 #24
0
    def _handle_semicolon_newline(
        self,
        target_segment: RawSegment,
        parent_segment: BaseSegment,
        info: SegmentMoveContext,
    ) -> Optional[LintResult]:
        # Adjust before_segment and anchor_segment for preceding inline
        # comments. Inline comments can contain noqa logic so we need to add the
        # newline after the inline comment.
        (
            before_segment,
            anchor_segment,
        ) = self._handle_preceding_inline_comments(info.before_segment,
                                                   info.anchor_segment)

        if (len(before_segment) == 1) and all(
                s.is_type("newline") for s in before_segment):
            return None

        # If preceding segment is not a single newline then delete the old
        # semi-colon/preceding whitespace and then insert the
        # semi-colon in the correct location.

        # This handles an edge case in which an inline comment comes after
        # the semi-colon.
        anchor_segment = self._handle_trailing_inline_comments(
            parent_segment, anchor_segment)
        fixes = []
        if anchor_segment is target_segment:
            fixes.append(
                LintFix.replace(
                    anchor_segment,
                    [
                        NewlineSegment(),
                        SymbolSegment(raw=";", type="statement_terminator"),
                    ],
                ))
        else:
            fixes.extend(
                self._create_semicolon_and_delete_whitespace(
                    target_segment,
                    parent_segment,
                    anchor_segment,
                    info.whitespace_deletions,
                    [
                        NewlineSegment(),
                        SymbolSegment(raw=";", type="statement_terminator"),
                    ],
                ))
        return LintResult(
            anchor=anchor_segment,
            fixes=fixes,
        )
コード例 #25
0
    def _eval(self, context: RuleContext) -> Optional[LintResult]:
        """Looking for DISTINCT before a bracket.

        Look for DISTINCT keyword immediately followed by open parenthesis.
        """
        # We trigger on `select_clause` and look for `select_clause_modifier`
        assert context.segment.is_type("select_clause")
        children = FunctionalContext(context).segment.children()
        modifier = children.select(sp.is_type("select_clause_modifier"))
        first_element = children.select(
            sp.is_type("select_clause_element")).first()
        if not modifier or not first_element:
            return None
        # is the first element only an expression with only brackets?
        expression = (first_element.children(sp.is_type("expression")).first()
                      or first_element)
        bracketed = expression.children(sp.is_type("bracketed")).first()
        if bracketed:
            fixes = []
            # If there's nothing else in the expression, remove the brackets.
            if len(expression[0].segments) == 1:
                # Remove the brackets and strip any meta segments.
                fixes.append(
                    LintFix.replace(
                        bracketed[0],
                        self.filter_meta(bracketed[0].segments)[1:-1]), )
            # If no whitespace between DISTINCT and expression, add it.
            if not children.select(sp.is_whitespace(),
                                   start_seg=modifier[0],
                                   stop_seg=first_element[0]):
                fixes.append(
                    LintFix.create_before(
                        first_element[0],
                        [WhitespaceSegment()],
                    ))
            # If no fixes, no problem.
            if fixes:
                return LintResult(anchor=modifier[0], fixes=fixes)
        return None
コード例 #26
0
ファイル: L007.py プロジェクト: sqlfluff/sqlfluff
def _generate_fixes(
    operator_new_lines: str,
    change_list: Segments,
    operator: BaseSegment,
    insert_anchor: BaseSegment,
) -> LintResult:
    # Duplicate the change list and append the operator
    inserts: List[BaseSegment] = [
        *change_list,
        operator,
    ]

    if operator_new_lines == "before":
        # We do yet another reverse here,
        # This could be avoided but makes all "changes" relate to "before" config state
        inserts = [*reversed(inserts)]

    # ensure to insert in the right place
    edit_type = "create_before" if operator_new_lines == "before" else "create_after"
    fixes = [
        # Insert elements reversed
        LintFix(
            edit_type=edit_type,
            edit=map(lambda el: copy.deepcopy(el), reversed(inserts)),
            anchor=insert_anchor,
        ),
        # remove the Op
        LintFix.delete(operator),
        # Delete the original elements (related to insert)
        *change_list.apply(LintFix.delete),
    ]
    desc = before_description if operator_new_lines == "before" else after_description
    return LintResult(
        anchor=operator,
        description=desc,
        fixes=fixes,
    )
コード例 #27
0
 def _eval(self, context: RuleContext) -> Optional[LintResult]:
     """Commas should not have whitespace directly before them."""
     if not context.raw_stack:
         return None  # pragma: no cover
     anchor: Optional[RawSegment] = context.raw_stack[-1]
     if (
         # We need at least one segment previous segment for this to work.
         anchor is not None
         and context.segment.is_type("comma")
         and anchor.is_type("whitespace")
         and anchor.pos_marker.line_pos > 1
     ):
         return LintResult(anchor=anchor, fixes=[LintFix.delete(anchor)])
     # Otherwise fine.
     return None
コード例 #28
0
    def _eval(self, context: RuleContext) -> LintResult:
        """Unnecessary trailing whitespace.

        Look for newline segments, and then evaluate what
        it was preceded by.
        """
        if len(context.raw_stack) > 0 and context.raw_stack[-1].is_type(
                "whitespace"):
            # Look for a newline (or file end), which is preceded by whitespace
            deletions = (
                FunctionalContext(context).raw_stack.reversed().select(
                    loop_while=sp.is_type("whitespace")))
            # NOTE: The presence of a loop marker should prevent false
            # flagging of newlines before jinja loop tags.
            return LintResult(
                anchor=deletions[-1],
                fixes=[LintFix.delete(d) for d in deletions],
            )
        return LintResult()
コード例 #29
0
    def _eval(self, context: RuleContext) -> Optional[LintResult]:
        """Files must not begin with newlines or whitespace."""
        # Only check raw segments. This ensures we don't try and delete the same
        # whitespace multiple times (i.e. for non-raw segments higher in the
        # tree).
        raw_segments = []
        whitespace_types = {"newline", "whitespace", "indent", "dedent"}
        for seg in context.segment.recursive_crawl_all():
            if not seg.is_raw():
                continue

            if seg.is_type(*whitespace_types):
                raw_segments.append(seg)
                continue

            segment = Segments(seg)
            raw_stack = Segments(*raw_segments,
                                 templated_file=context.templated_file)
            # Non-whitespace segment.
            if (not raw_stack.all(sp.is_meta())
                    # Found leaf of parse tree.
                    and not segment.all(sp.is_expandable())
                    # It is possible that a template segment (e.g.
                    # {{ config(materialized='view') }}) renders to an empty string
                    # and as such is omitted from the parsed tree. We therefore
                    # should flag if a templated raw slice intersects with the
                    # source slices in the raw stack and skip this rule to avoid
                    # risking collisions with template objects.
                    and not raw_stack.raw_slices.any(
                        rsp.is_slice_type("templated"))):
                return LintResult(
                    anchor=context.segment,
                    fixes=[LintFix.delete(d) for d in raw_stack],
                )
            else:
                break
        return None
コード例 #30
0
    def _eval(self, context: RuleContext) -> Optional[List[LintResult]]:
        """Ambiguous ordering directions for columns in order by clause.

        This rule checks if some ORDER BY columns explicitly specify ASC or
        DESC and some don't.
        """
        # We only trigger on orderby_clause
        lint_fixes = []
        orderby_spec = self._get_orderby_info(context.segment)
        order_types = {o.order for o in orderby_spec}
        # If ALL columns or NO columns explicitly specify ASC/DESC, all is
        # well.
        if None not in order_types or order_types == {None}:
            return None

        # There's a mix of explicit and default sort order. Make everything
        # explicit.
        for col_info in orderby_spec:
            if not col_info.order:
                # Since ASC is default in SQL, add in ASC for fix
                lint_fixes.append(
                    LintFix.create_before(
                        col_info.separator,
                        [WhitespaceSegment(),
                         KeywordSegment("ASC")],
                    ))

        return [
            LintResult(
                anchor=context.segment,
                fixes=lint_fixes,
                description=
                ("Ambiguous order by clause. Order by clauses should specify "
                 "order direction for ALL columns or NO columns."),
            )
        ]