diff --git a/parso/normalizer.py b/parso/normalizer.py index 92eac93..825fd60 100644 --- a/parso/normalizer.py +++ b/parso/normalizer.py @@ -6,6 +6,11 @@ class Normalizer(object): self._config = config self.issues = [] + def walk(self, node): + value = self.visit(node) + self.finalize() + return value + def visit(self, node): try: children = node.children @@ -16,12 +21,15 @@ class Normalizer(object): return ''.join(self.visit(child) for child in children) @contextmanager - def visit_node(self): + def visit_node(self, node): yield - def normalize(self, leaf): + def visit_leaf(self, leaf): return leaf.prefix + leaf.value + def finalize(self): + pass + def add_issue(self, code, message, node): issue = Issue(node, code, message) if issue not in self.issues: diff --git a/parso/python/normalizer.py b/parso/python/normalizer.py index 04860cb..4655fda 100644 --- a/parso/python/normalizer.py +++ b/parso/python/normalizer.py @@ -1,4 +1,6 @@ -from parso.normalizer import Normalizer +from contextlib import contextmanager + +from parso.normalizer import Normalizer, NormalizerConfig class CompressNormalizer(Normalizer): @@ -7,3 +9,48 @@ class CompressNormalizer(Normalizer): """ def normalize(self, leaf): return leaf.prefix + leaf.value + + +class ErrorFinder(Normalizer): + """ + Searches for errors in the syntax tree. + """ + def __init__(self, *args, **kwargs): + super(ErrorFinder, self).__init__(*args, **kwargs) + self._error_dict = {} + + @contextmanager + def visit_node(self, node): + if node.type == 'error_node': + leaf = node.get_next_leaf() + self._add_error(901, "Syntax Error", leaf) + + yield + + def visit_leaf(self, leaf): + if leaf.type == 'error_leaf': + self._add_error(901, "Syntax Error", leaf) + + return '' + + def _add_error(self, code, message, node): + line = node.start_pos[0] + self._error_dict.setdefault(line, (code, message, node)) + + def finalize(self): + for code, message, node in self._error_dict.values(): + self.add_issue(code, message, node) + + def add_issue(self, code, message, node): + # Check if the issues are on the same line. + prev = node.get_previous_leaf() + if prev is not None and prev.type == 'error_leaf': + # There's already an error nearby. There's a huge chance they are + # related, so don't report this one. + return + + super(ErrorFinder, self).add_issue(code, message, node) + + +class ErrorFinderConfig(NormalizerConfig): + normalizer_class = ErrorFinder diff --git a/parso/tree.py b/parso/tree.py index eb5bb8e..1965c68 100644 --- a/parso/tree.py +++ b/parso/tree.py @@ -168,11 +168,11 @@ class NodeOrLeaf(object): The returned code will be normalized, e.g. PEP8 for Python. """ normalizer = self._get_normalizer(normalizer_config) - return normalizer.visit(self) + return normalizer.walk(self) def _get_normalizer_issues(self, normalizer_config=None): normalizer = self._get_normalizer(normalizer_config) - normalizer.visit(self) + normalizer.walk(self) return normalizer.issues