def test__cli__formatters__violation(tmpdir): """Test formatting violations. NB Position is 1 + start_pos. """ s = RawSegment( "foobarbar", PositionMarker( slice(10, 19), slice(10, 19), TemplatedFile.from_string(" \n\n foobarbar"), ), ) r = RuleGhost("A", "DESC") v = SQLLintError(segment=s, rule=r) formatter = OutputStreamFormatter( FileOutput(FluffConfig(require_dialect=False), str(tmpdir / "out.txt")), False ) f = formatter.format_violation(v) # Position is 3, 3 becase foobarbar is on the third # line (i.e. it has two newlines preceding it) and # it's at the third position in that line (i.e. there # are two characters between it and the preceding # newline). assert escape_ansi(f) == "L: 3 | P: 3 | A | DESC"
def test__cli__formatters__violation(): """Test formatting violations. NB Position is 1 + start_pos. """ s = RawSegment("foobarbar", FilePositionMarker(0, 20, 11, 100)) r = RuleGhost("A", "DESC") v = SQLLintError(segment=s, rule=r) f = format_violation(v) assert escape_ansi(f) == "L: 20 | P: 11 | A | DESC"
def to_linting_error(self, rule): """Convert a linting result to a :exc:`SQLLintError` if appropriate.""" if self.anchor: # Allow description override from the LintResult description = self.description or rule.description return SQLLintError( rule=rule, segment=self.anchor, fixes=self.fixes, description=description, ) else: return None
def test__cli__formatters__violation(): """Test formatting violations. NB Position is 1 + start_pos. """ s = RawSegment( "foobarbar", PositionMarker( slice(10, 19), slice(10, 19), TemplatedFile.from_string(" \n\n foobarbar"), ), ) r = RuleGhost("A", "DESC") v = SQLLintError(segment=s, rule=r) f = format_violation(v) # Position is 3, 3 becase foobarbar is on the third # line (i.e. it has two newlines preceding it) and # it's at the third position in that line (i.e. there # are two characters between it and the preceeding # newline). assert escape_ansi(f) == "L: 3 | P: 3 | A | DESC"
def crawl( self, segment, dialect, parent_stack=None, siblings_pre=None, siblings_post=None, raw_stack=None, memory=None, fname=None, templated_file: Optional["TemplatedFile"] = None, ): """Recursively perform the crawl operation on a given segment. Returns: A tuple of (vs, raw_stack, fixes, memory) """ # parent stack should be a tuple if it exists # Rules should evaluate on segments FIRST, before evaluating on their # children. They should also return a list of violations. parent_stack = parent_stack or () raw_stack = raw_stack or () siblings_post = siblings_post or () siblings_pre = siblings_pre or () memory = memory or {} vs: List[SQLLintError] = [] fixes: List[LintFix] = [] # First, check whether we're looking at an unparsable and whether # this rule will still operate on that. if not self._works_on_unparsable and segment.is_type("unparsable"): # Abort here if it doesn't. Otherwise we'll get odd results. return vs, raw_stack, [], memory # TODO: Document what options are available to the evaluation function. try: res = self._eval( segment=segment, parent_stack=parent_stack, siblings_pre=siblings_pre, siblings_post=siblings_post, raw_stack=raw_stack, memory=memory, dialect=dialect, path=pathlib.Path(fname) if fname else None, templated_file=templated_file, ) # Any exception at this point would halt the linter and # cause the user to get no results except Exception as e: self.logger.critical( f"Applying rule {self.code} threw an Exception: {e}", exc_info=True) exception_line, _ = segment.pos_marker.source_position() vs.append( SQLLintError( rule=self, segment=segment, fixes=[], description=(f"""Unexpected exception: {str(e)}; Could you open an issue at https://github.com/sqlfluff/sqlfluff/issues ? You can ignore this exception for now, by adding '--noqa: {self.code}' at the end of line {exception_line} """), )) return vs, raw_stack, fixes, memory new_lerrs = [] new_fixes = [] if res is None: # Assume this means no problems (also means no memory) pass elif isinstance(res, LintResult): # Extract any memory memory = res.memory lerr = res.to_linting_error(rule=self) if lerr: new_lerrs = [lerr] new_fixes = res.fixes elif isinstance(res, list) and all( isinstance(elem, LintResult) for elem in res): # Extract any memory from the *last* one, assuming # it was the last to be added memory = res[-1].memory for elem in res: lerr = elem.to_linting_error(rule=self) if lerr: new_lerrs.append(lerr) new_fixes += elem.fixes else: raise TypeError( "Got unexpected result [{0!r}] back from linting rule: {1!r}". format(res, self.code)) for lerr in new_lerrs: self.logger.debug("!! Violation Found: %r", lerr.description) for fix in new_fixes: self.logger.debug("!! Fix Proposed: %r", fix) # Consume the new results vs += new_lerrs fixes += new_fixes # The raw stack only keeps track of the previous raw segments if len(segment.segments) == 0: raw_stack += (segment, ) # Parent stack keeps track of all the parent segments parent_stack += (segment, ) for idx, child in enumerate(segment.segments): dvs, raw_stack, child_fixes, memory = self.crawl( segment=child, parent_stack=parent_stack, siblings_pre=segment.segments[:idx], siblings_post=segment.segments[idx + 1:], raw_stack=raw_stack, memory=memory, dialect=dialect, fname=fname, templated_file=templated_file, ) vs += dvs fixes += child_fixes return vs, raw_stack, fixes, memory
def crawl( self, tree: BaseSegment, dialect: Dialect, fix: bool, templated_file: Optional["TemplatedFile"], ignore_mask: List[NoQaDirective], fname: Optional[str], ) -> Tuple[List[SQLLintError], Tuple[RawSegment, ...], List[LintFix], Any]: """Run the rule on a given tree. Returns: A tuple of (vs, raw_stack, fixes, memory) """ root_context = RuleContext( dialect=dialect, fix=fix, templated_file=templated_file, path=pathlib.Path(fname) if fname else None, segment=tree, ) vs: List[SQLLintError] = [] fixes: List[LintFix] = [] # Propagates memory from one rule _eval() to the next. memory: Any = root_context.memory context = root_context for context in self.crawl_behaviour.crawl(root_context): try: context.memory = memory res = self._eval(context=context) except (bdb.BdbQuit, KeyboardInterrupt): # pragma: no cover raise # Any exception at this point would halt the linter and # cause the user to get no results except Exception as e: self.logger.critical( f"Applying rule {self.code} threw an Exception: {e}", exc_info=True ) assert context.segment.pos_marker exception_line, _ = context.segment.pos_marker.source_position() self._log_critical_errors(e) vs.append( SQLLintError( rule=self, segment=context.segment, fixes=[], description=( f"Unexpected exception: {str(e)};\n" "Could you open an issue at " "https://github.com/sqlfluff/sqlfluff/issues ?\n" "You can ignore this exception for now, by adding " f"'-- noqa: {self.code}' at the end\n" f"of line {exception_line}\n" ), ) ) return vs, context.raw_stack, fixes, context.memory new_lerrs: List[SQLLintError] = [] new_fixes: List[LintFix] = [] if res is None or res == []: # Assume this means no problems (also means no memory) pass elif isinstance(res, LintResult): # Extract any memory memory = res.memory self._adjust_anchors_for_fixes(context, res) self._process_lint_result( res, templated_file, ignore_mask, new_lerrs, new_fixes ) elif isinstance(res, list) and all( isinstance(elem, LintResult) for elem in res ): # Extract any memory from the *last* one, assuming # it was the last to be added memory = res[-1].memory for elem in res: self._adjust_anchors_for_fixes(context, elem) self._process_lint_result( elem, templated_file, ignore_mask, new_lerrs, new_fixes ) else: # pragma: no cover raise TypeError( "Got unexpected result [{!r}] back from linting rule: {!r}".format( res, self.code ) ) for lerr in new_lerrs: self.logger.info("!! Violation Found: %r", lerr.description) for lfix in new_fixes: self.logger.info("!! Fix Proposed: %r", lfix) # Consume the new results vs += new_lerrs fixes += new_fixes return vs, context.raw_stack if context else tuple(), fixes, context.memory