Fix diff parser: Decorators were sometimes parsed without their functions

This commit is contained in:
Dave Halter
2019-01-05 09:29:00 +01:00
parent 5da51720cd
commit c1846dd082
2 changed files with 48 additions and 2 deletions

View File

@@ -23,6 +23,8 @@ DEBUG_DIFF_PARSER = False
def _assert_valid_graph(node): def _assert_valid_graph(node):
""" """
Checks if the parent/children relationship is correct. Checks if the parent/children relationship is correct.
This is a check that only runs during debugging/testing.
""" """
try: try:
children = node.children 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' 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): if not _flows_finished(pgen_grammar, stack):
return False return False
for stack_node in reversed(stack): 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 stack_node.nonterminal == 'suite':
# If only newline is in the suite, the suite is not valid, yet. # If only newline is in the suite, the suite is not valid, yet.
return len(stack_node.nodes) > 1 return len(stack_node.nodes) > 1
@@ -388,7 +394,7 @@ class DiffParser(object):
elif typ == PythonTokenTypes.NEWLINE and start_pos[0] >= until_line: elif typ == PythonTokenTypes.NEWLINE and start_pos[0] >= until_line:
yield PythonToken(typ, string, start_pos, prefix) yield PythonToken(typ, string, start_pos, prefix)
# Check if the parser is actually in a valid suite state. # 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 start_pos = start_pos[0] + 1, 0
while len(indents) > int(omitted_first_indent): while len(indents) > int(omitted_first_indent):
indents.pop() indents.pop()

View File

@@ -59,6 +59,7 @@ class Differ(object):
new_module = diff_parser.update(self.lines, lines) new_module = diff_parser.update(self.lines, lines)
self.lines = lines self.lines = lines
assert code == new_module.get_code() assert code == new_module.get_code()
_assert_valid_graph(new_module) _assert_valid_graph(new_module)
assert diff_parser._copy_count == copies assert diff_parser._copy_count == copies
assert diff_parser._parser_count == parsers assert diff_parser._parser_count == parsers
@@ -745,3 +746,42 @@ def test_paren_before_docstring(differ):
differ.initialize(code1) differ.initialize(code1)
differ.parse(code2, parsers=1, copies=1, expect_error_leaves=True) differ.parse(code2, parsers=1, copies=1, expect_error_leaves=True)
differ.parse(code1, parsers=2, copies=1) 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)