rope icon indicating copy to clipboard operation
rope copied to clipboard

patched_ast will set region[0] to None when unmatched parenthesis inside string

Open eivindjahren opened this issue 2 years ago • 1 comments

Describe the bug A clear and concise description of what the bug is.

To Reproduce Steps to reproduce the behavior:

  1. Code before refactoring:
class A:
    def __init__(self):
        pass

    def _unmatched_inside_f_string(self, val):
        if val < 0:
            raise IndexError(
                (
                    f"{val})"
                )
            )

    def other_func(self):
        pass

  1. Rename class A to B

  2. Expected code after refactoring:

class B:
    def __init__(self):
        pass

    def _unmatched_inside_f_string(self, val):
        if val < 0:
            raise IndexError(
                (
                    f"{val})"
                )
            )

    def other_func(self):
        pass

  1. Describe the error or unexpected result that you are getting
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/eivind/Projects/ropemode/src/ropemode/decorators.py", line 54, in newfunc
    return func(*args, **kwds)
  File "/home/eivind/Projects/ropemode/src/ropemode/interface.py", line 52, in do_refactor
    refactoring(self, self.env).show(initial_asking=initial_asking)
  File "/home/eivind/Projects/ropemode/src/ropemode/refactor.py", line 33, in show
    self._create_refactoring()
  File "/home/eivind/Projects/ropemode/src/ropemode/refactor.py", line 119, in _create_refactoring
    self.renamer = rope.refactor.rename.Rename(
  File "/home/eivind/Projects/rope/rope/refactor/rename.py", line 32, in __init__
    self.old_instance, self.old_pyname = evaluate.eval_location2(
  File "/home/eivind/Projects/rope/rope/base/evaluate.py", line 21, in eval_location2
    return pyname_finder.get_primary_and_pyname_at(offset)
  File "/home/eivind/Projects/rope/rope/base/evaluate.py", line 87, in get_primary_and_pyname_at
    holding_scope = self.module_scope.get_inner_scope_for_offset(offset)
  File "/home/eivind/Projects/rope/rope/base/pyscopes.py", line 160, in get_inner_scope_for_offset
    return self._scope_finder.get_holding_scope_for_offset(self, offset)
  File "/home/eivind/Projects/rope/rope/base/pyscopes.py", line 317, in get_holding_scope_for_offset
    if inner_scope.in_region(offset):
  File "/home/eivind/Projects/rope/rope/base/pyscopes.py", line 118, in in_region
    return region[0] < offset < region[1]
TypeError: '<' not supported between instances of 'NoneType' and 'int'

Additional context The problem seems to be that under certain conditions, a region tuple gets its first value assigned to 0 when there is an unmatched parenthesis inside a string. The following test can be used to reproduce:

    def test_works_with_unmatched_parens_inside_string(self):
        source = """
class A:
    def __init__(self):
        pass

    def _unmatched_inside_f_string(self, val):
        if val < 0:
            raise IndexError(
                (
                    f"{val})"
                )
            )

    def other_func(self):
        pass
        """
        ast_frag = patchedast.get_patched_ast(source, True)
        assert ast_frag.body[-1].region[0] != None

eivindjahren avatar Jul 20 '22 11:07 eivindjahren

@eivindjahren Thank you for writing detailed bug report with great reproduction scenario.

lieryan avatar Jul 20 '22 13:07 lieryan