diff --git a/parso/python/diff.py b/parso/python/diff.py index dfe311c..af9e492 100644 --- a/parso/python/diff.py +++ b/parso/python/diff.py @@ -23,6 +23,8 @@ DEBUG_DIFF_PARSER = False def _assert_valid_graph(node): """ Checks if the parent/children relationship is correct. + + This is a check that only runs during debugging/testing. """ try: children = node.children @@ -112,11 +114,15 @@ def _func_or_class_has_suite(node): return node.type in ('classdef', 'funcdef') and node.children[-1].type == 'suite' -def suite_or_file_input_is_valid(pgen_grammar, stack): +def _suite_or_file_input_is_valid(pgen_grammar, stack): if not _flows_finished(pgen_grammar, stack): return False for stack_node in reversed(stack): + if stack_node.nonterminal == 'decorator': + # A decorator is only valid with the upcoming function. + return False + if stack_node.nonterminal == 'suite': # If only newline is in the suite, the suite is not valid, yet. return len(stack_node.nodes) > 1 @@ -388,7 +394,7 @@ class DiffParser(object): elif typ == PythonTokenTypes.NEWLINE and start_pos[0] >= until_line: yield PythonToken(typ, string, start_pos, prefix) # Check if the parser is actually in a valid suite state. - if suite_or_file_input_is_valid(self._pgen_grammar, stack): + if _suite_or_file_input_is_valid(self._pgen_grammar, stack): start_pos = start_pos[0] + 1, 0 while len(indents) > int(omitted_first_indent): indents.pop() diff --git a/test/test_diff_parser.py b/test/test_diff_parser.py index 46d51ef..530d967 100644 --- a/test/test_diff_parser.py +++ b/test/test_diff_parser.py @@ -59,6 +59,7 @@ class Differ(object): new_module = diff_parser.update(self.lines, lines) self.lines = lines assert code == new_module.get_code() + _assert_valid_graph(new_module) assert diff_parser._copy_count == copies assert diff_parser._parser_count == parsers @@ -745,3 +746,42 @@ def test_paren_before_docstring(differ): differ.initialize(code1) differ.parse(code2, parsers=1, copies=1, expect_error_leaves=True) differ.parse(code1, parsers=2, copies=1) + + +def test_x(differ): + code1 = dedent('''\ + class StackNode(object): + def __init__(self, dfa): + self.dfa = dfa + self.nodes = [] + + @property + def nonterminal(self): + return self.dfa.from_rule + + def __repr__(self): + return '%s(%s, %s)' % (self.__class__.__name__, self.dfa, self.nodes) + + def x(): + pass + ''') + + code2 = dedent('''\ + class StackNode(object): + def __init__(self, dfa): + self.dfa = dfa + self.nodes = [] + (msg, type_.name, value, start_pos)) + + @property + def nonterminal(self): + + def __repr__(self): + + def x(): + pass + ''') + + differ.initialize(code1) + differ.parse(code2, parsers=3, copies=1, expect_error_leaves=True) + differ.parse(code1, parsers=2, copies=2)