Skip to content

Commit

Permalink
Merge pull request #4666 from yushao2/fix-use-maxsplit-arg
Browse files Browse the repository at this point in the history
Fix false-positive of `use-maxsplit-arg` when index is incremented inside a loop
  • Loading branch information
yushao2 committed Jul 7, 2021
2 parents e4cd2ef + 9706392 commit 708527d
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 24 deletions.
5 changes: 5 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ Release date: TBA

Closes #4652

* Fix false-positive of ``use-maxsplit-arg`` when index is incremented in
a loop

Closes #4664


What's New in Pylint 2.9.3?
===========================
Expand Down
71 changes: 47 additions & 24 deletions pylint/checkers/refactoring/recommendation_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,38 +78,61 @@ def _check_use_maxsplit_arg(self, node: astroid.Call) -> None:
"""Add message when accessing first or last elements of a str.split() or str.rsplit()."""

# Check if call is split() or rsplit()
if (
if not (
isinstance(node.func, astroid.Attribute)
and node.func.attrname in ("split", "rsplit")
and isinstance(utils.safe_infer(node.func), astroid.BoundMethod)
):
try:
utils.get_argument_from_call(node, 0, "sep")
except utils.NoSuchArgumentError:
return
return

try:
utils.get_argument_from_call(node, 0, "sep")
except utils.NoSuchArgumentError:
return

try:
# Ignore if maxsplit arg has been set
utils.get_argument_from_call(node, 1, "maxsplit")
return
except utils.NoSuchArgumentError:
pass

if isinstance(node.parent, astroid.Subscript):
try:
# Ignore if maxsplit arg has been set
utils.get_argument_from_call(node, 1, "maxsplit")
subscript_value = utils.get_subscript_const_value(node.parent).value
except utils.InferredTypeError:
return
except utils.NoSuchArgumentError:
pass

if isinstance(node.parent, astroid.Subscript):
try:
subscript_value = utils.get_subscript_const_value(node.parent).value
except utils.InferredTypeError:
return

if subscript_value in (-1, 0):
fn_name = node.func.attrname
new_fn = "rsplit" if subscript_value == -1 else "split"
new_name = (
node.func.as_string().rsplit(fn_name, maxsplit=1)[0]
+ new_fn
+ f"({node.args[0].as_string()}, maxsplit=1)[{subscript_value}]"
)
self.add_message("use-maxsplit-arg", node=node, args=(new_name,))
# Check for cases where variable (Name) subscripts may be mutated within a loop
if isinstance(node.parent.slice, astroid.Name):
# Check if loop present within the scope of the node
scope = node.scope()
for loop_node in scope.nodes_of_class((astroid.For, astroid.While)):
loop_node = cast(astroid.node_classes.NodeNG, loop_node)
if not loop_node.parent_of(node):
continue

# Check if var is mutated within loop (Assign/AugAssign)
for assignment_node in loop_node.nodes_of_class(astroid.AugAssign):
assignment_node = cast(astroid.AugAssign, assignment_node)
if node.parent.slice.name == assignment_node.target.name:
return
for assignment_node in loop_node.nodes_of_class(astroid.Assign):
assignment_node = cast(astroid.Assign, assignment_node)
if node.parent.slice.name in [
n.name for n in assignment_node.targets
]:
return

if subscript_value in (-1, 0):
fn_name = node.func.attrname
new_fn = "rsplit" if subscript_value == -1 else "split"
new_name = (
node.func.as_string().rsplit(fn_name, maxsplit=1)[0]
+ new_fn
+ f"({node.args[0].as_string()}, maxsplit=1)[{subscript_value}]"
)
self.add_message("use-maxsplit-arg", node=node, args=(new_name,))

@utils.check_messages("consider-using-enumerate", "consider-using-dict-items")
def visit_for(self, node: astroid.For) -> None:
Expand Down
8 changes: 8 additions & 0 deletions tests/functional/u/use/use_maxsplit_arg.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,11 @@ class Bar():
a = "1,2,3".split('\n')[0] # [use-maxsplit-arg]
a = "1,2,3".split('split')[-1] # [use-maxsplit-arg]
a = "1,2,3".rsplit('rsplit')[0] # [use-maxsplit-arg]

# Test cases for false-positive reported in #4664
# https://github.com/PyCQA/pylint/issues/4664
source = 'A.B.C.D.E.F.G'
i = 0
for j in range(5):
print(source.split('.')[i])
i = i + 1

0 comments on commit 708527d

Please sign in to comment.