diff --git a/parso/python/diff.py b/parso/python/diff.py index 8f84684..d5c57e6 100644 --- a/parso/python/diff.py +++ b/parso/python/diff.py @@ -690,6 +690,36 @@ class _NodesTree(object): new_nodes.append(node) + # Pop error nodes at the end from the list + if new_nodes: + while new_nodes: + last_node = new_nodes[-1] + if (last_node.type in ('error_leaf', 'error_node') + or _is_flow_node(new_nodes[-1])): + # Error leafs/nodes don't have a defined start/end. Error + # nodes might not end with a newline (e.g. if there's an + # open `(`). Therefore ignore all of them unless they are + # succeeded with valid parser state. + # If we copy flows at the end, they might be continued + # after the copy limit (in the new parser). + # In this while loop we try to remove until we find a newline. + new_prefix = '' + new_nodes.pop() + while new_nodes: + last_node = new_nodes[-1] + if last_node.get_last_leaf().type == 'newline': + break + new_nodes.pop() + continue + if len(new_nodes) > 1 and new_nodes[-2].type == 'error_node': + # The problem here is that Parso error recovery sometimes + # influences nodes before this node. + # Since the new last node is an error node this will get + # cleaned up in the next while iteration. + new_nodes.pop() + continue + break + if not new_nodes: return [], working_stack, prefix, added_indents @@ -721,37 +751,6 @@ class _NodesTree(object): working_stack = new_working_stack had_valid_suite_last = True - # Pop error nodes at the end from the list - if new_nodes: - while new_nodes: - last_node = new_nodes[-1] - if (last_node.type in ('error_leaf', 'error_node') - or _is_flow_node(new_nodes[-1])): - # Error leafs/nodes don't have a defined start/end. Error - # nodes might not end with a newline (e.g. if there's an - # open `(`). Therefore ignore all of them unless they are - # succeeded with valid parser state. - # If we copy flows at the end, they might be continued - # after the copy limit (in the new parser). - # In this while loop we try to remove until we find a newline. - new_prefix = '' - new_nodes.pop() - while new_nodes: - last_node = new_nodes[-1] - if last_node.get_last_leaf().type == 'newline': - break - new_nodes.pop() - continue - if len(new_nodes) > 1 and new_nodes[-2].type == 'error_node': - # The problem here is that Jedi error recovery sometimes - # will mark last node. - # Since the new last node is an error node this will get - # cleaned up in the next while iteration. - new_nodes.pop() - continue - - break - if new_nodes: if not _ends_with_newline(new_nodes[-1].get_last_leaf()) and not had_valid_suite_last: p = new_nodes[-1].get_next_leaf().prefix diff --git a/test/test_diff_parser.py b/test/test_diff_parser.py index a7547b5..a8950bc 100644 --- a/test/test_diff_parser.py +++ b/test/test_diff_parser.py @@ -155,7 +155,7 @@ def test_func_with_for_and_comment(differ): # COMMENT a""") differ.initialize(src) - differ.parse('a\n' + src, copies=1, parsers=2) + differ.parse('a\n' + src, copies=1, parsers=3) def test_one_statement_func(differ): @@ -302,7 +302,7 @@ def test_unfinished_nodes(differ): a(1) ''') differ.initialize(code) - differ.parse(code2, parsers=1, copies=2) + differ.parse(code2, parsers=2, copies=2) def test_nested_if_and_scopes(differ): @@ -1447,7 +1447,7 @@ def test_repeating_invalid_indent(differ): c ''') differ.initialize(code1) - differ.parse(code2, parsers=1, copies=1, expect_error_leaves=True) + differ.parse(code2, parsers=2, copies=1, expect_error_leaves=True) def test_another_random_indent(differ): @@ -1470,3 +1470,20 @@ def test_another_random_indent(differ): ''') differ.initialize(code1) differ.parse(code2, parsers=1, copies=3) + + +def test_invalid_function(differ): + code1 = dedent('''\ + a + def foo(): + def foo(): + b + ''') + code2 = dedent('''\ + a + def foo(): + def foo(): + b + ''') + differ.initialize(code1) + differ.parse(code2, parsers=1, copies=1, expect_error_leaves=True)