Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# **Upcoming release**

- ...
- Fix inlining parameters used as indices in subscript assignments (#827).

# Release 1.15.0

Expand Down
6 changes: 6 additions & 0 deletions rope/base/worder.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,12 @@ def get_assignment_type(self, offset):
if double not in ("==", "<=", ">=", "!="):
for op in [single, double, triple]:
if op.endswith("="):
for end in range(next_char, next_char + len(op) - 1):
if self.code[end] in ")]":
# A subscript index is read, unlike an unpacking target.
parens_start = self._find_parens_start(end)
if self._find_primary_start(end) < parens_start:
return None
return op

def get_primary_range(self, offset):
Expand Down
50 changes: 50 additions & 0 deletions ropetest/codeanalyzetest.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,56 @@ def test_is_assigned_here_for_not_equal_test(self):
word_finder = worder.Worder(code)
self.assertFalse(word_finder.is_assigned_here(0))

def test_subscript_assignment_does_not_assign_its_index(self):
for code in (
"get_registry()[name] = value\n",
"registry[name]=value\n",
"registry[name]+=value\n",
"registry[(name)]=value\n",
"registry[left + name] = value\n",
"registry[:name] = value\n",
"registry[name:]=value\n",
"registry[registry[name]]=value\n",
"[registry[name]]=values\n",
):
with self.subTest(code=code):
word_finder = worder.Worder(code)
self.assertFalse(word_finder.is_assigned_here(code.index("name")))

def test_grouped_and_list_assignment_targets_are_assigned(self):
for code in (
"(name)=value\n",
"(name) = value\n",
"[name]=value\n",
"[name] = value\n",
"[(name)]=value\n",
"([name])=value\n",
):
with self.subTest(code=code):
word_finder = worder.Worder(code)
self.assertTrue(word_finder.is_assigned_here(code.index("name")))

def test_assignment_operator_types(self):
for operator in (
"=", "+=", "-=", "*=", "/=", "//=", "%=", "**=", "@=",
"&=", "|=", "^=", "<<=", ">>=", ":=",
):
code = "name " + operator + " value\n"
if operator == ":=":
code = "(" + code.rstrip() + ")\n"
with self.subTest(operator=operator):
word_finder = worder.Worder(code)
self.assertEqual(
operator, word_finder.get_assignment_type(code.index("name"))
)

def test_comparison_operators_do_not_assign(self):
for operator in ("==", "!=", "<=", ">="):
code = "name " + operator + " value\n"
with self.subTest(operator=operator):
word_finder = worder.Worder(code)
self.assertFalse(word_finder.is_assigned_here(0))

# XXX: is_assigned_here should work for tuple assignments
def xxx_test_is_assigned_here_for_tuple_assignment(self):
code = "a, b = (1, 2)\n"
Expand Down
20 changes: 20 additions & 0 deletions ropetest/refactor/inlinetest.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,26 @@ def a_func(param):
self._inline2(self.mod, self.mod.read().index("a_func") + 1)
self.assertEqual("print(1)\n", self.mod.read())

def test_parameters_used_as_subscript_assignment_indices(self):
code = dedent("""\
registry = {}
def get_registry():
return registry
def register(name, value):
get_registry()[name] = value
register("cache", 42)
""")
refactored = self._inline(code, code.index("register") + 1)
self.assertEqual(
dedent("""\
registry = {}
def get_registry():
return registry
get_registry()["cache"] = 42
"""),
refactored,
)

def test_parameters_renaming_for_passed_statements(self):
self.mod.write(dedent("""\
def a_func(param):
Expand Down
Loading