Skip to content

Commit fbbc9e8

Browse files
authored
Improve invalid-slice-index and add invalid-slice-step (#7762)
1 parent 6de674c commit fbbc9e8

File tree

9 files changed

+78
-9
lines changed

9 files changed

+78
-9
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
LETTERS = ["a", "b", "c", "d"]
2+
3+
LETTERS[::0] # [invalid-slice-step]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
LETTERS = ["a", "b", "c", "d"]
2+
3+
LETTERS[::2]

doc/user_guide/checkers/features.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,6 +1155,9 @@ Typecheck checker Messages
11551155
:invalid-slice-index (E1127): *Slice index is not an int, None, or instance with __index__*
11561156
Used when a slice index is not an integer, None, or an object with an
11571157
__index__ method.
1158+
:invalid-slice-step (E1144): *Slice step cannot be 0*
1159+
Used when a slice step is 0 and the object doesn't implement a custom
1160+
__getitem__ method.
11581161
:too-many-function-args (E1121): *Too many positional arguments for %s call*
11591162
Used when a function call passes too many positional arguments.
11601163
:unexpected-keyword-arg (E1123): *Unexpected keyword argument %r in %s call*

doc/user_guide/messages/messages_overview.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ All messages in the error category:
102102
error/invalid-repr-returned
103103
error/invalid-sequence-index
104104
error/invalid-slice-index
105+
error/invalid-slice-step
105106
error/invalid-slots
106107
error/invalid-slots-object
107108
error/invalid-star-assignment-target
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Extend ``invalid-slice-index`` to emit an warning for invalid slice indices
2+
used with string and byte sequences, and range objects.
3+
4+
Refs #7762

doc/whatsnew/fragments/7762.new_check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Add ``invalid-slice-step`` check to warn about a slice step value of ``0``
2+
for common builtin sequences.
3+
4+
Refs #7762

pylint/checkers/typecheck.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
supports_membership_test,
4949
supports_setitem,
5050
)
51-
from pylint.interfaces import INFERENCE
51+
from pylint.interfaces import HIGH, INFERENCE
5252
from pylint.typing import MessageDefinitionTuple
5353

5454
if sys.version_info >= (3, 8):
@@ -374,6 +374,12 @@ def _missing_member_hint(
374374
"(i.e. doesn't define __hash__ method).",
375375
{"old_names": [("E1140", "unhashable-dict-key")]},
376376
),
377+
"E1144": (
378+
"Slice step cannot be 0",
379+
"invalid-slice-step",
380+
"Used when a slice step is 0 and the object doesn't implement "
381+
"a custom __getitem__ method.",
382+
),
377383
"W1113": (
378384
"Keyword argument before variable positional arguments list "
379385
"in the definition of %s function",
@@ -1785,7 +1791,11 @@ def _check_invalid_slice_index(self, node: nodes.Slice) -> None:
17851791
pass
17861792
invalid_slices_nodes.append(index)
17871793

1788-
if not invalid_slices_nodes:
1794+
invalid_slice_step = (
1795+
node.step and isinstance(node.step, nodes.Const) and node.step.value == 0
1796+
)
1797+
1798+
if not (invalid_slices_nodes or invalid_slice_step):
17891799
return
17901800

17911801
# Anything else is an error, unless the object that is indexed
@@ -1803,11 +1813,19 @@ def _check_invalid_slice_index(self, node: nodes.Slice) -> None:
18031813
astroid.objects.FrozenSet,
18041814
nodes.Set,
18051815
)
1806-
if not isinstance(inferred, known_objects):
1816+
if not (
1817+
isinstance(inferred, known_objects)
1818+
or isinstance(inferred, nodes.Const)
1819+
and inferred.pytype() in {"builtins.str", "builtins.bytes"}
1820+
or isinstance(inferred, astroid.bases.Instance)
1821+
and inferred.pytype() == "builtins.range"
1822+
):
18071823
# Might be an instance that knows how to handle this slice object
18081824
return
18091825
for snode in invalid_slices_nodes:
18101826
self.add_message("invalid-slice-index", node=snode)
1827+
if invalid_slice_step:
1828+
self.add_message("invalid-slice-step", node=node.step, confidence=HIGH)
18111829

18121830
@only_required_for_messages("not-context-manager")
18131831
def visit_with(self, node: nodes.With) -> None:
@@ -2064,6 +2082,7 @@ def visit_set(self, node: nodes.Set) -> None:
20642082
"unhashable-member",
20652083
"invalid-sequence-index",
20662084
"invalid-slice-index",
2085+
"invalid-slice-step",
20672086
)
20682087
def visit_subscript(self, node: nodes.Subscript) -> None:
20692088
self._check_invalid_sequence_index(node)

tests/functional/i/invalid/invalid_slice_index.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Errors for invalid slice indices"""
22
# pylint: disable=too-few-public-methods,missing-docstring,expression-not-assigned,unnecessary-pass
3-
3+
# pylint: disable=pointless-statement
44

55
TESTLIST = [1, 2, 3]
66

@@ -11,7 +11,10 @@ def function1():
1111

1212
def function2():
1313
"""strings used as indices"""
14-
return TESTLIST['0':'1':] # [invalid-slice-index,invalid-slice-index]
14+
TESTLIST['0':'1':] # [invalid-slice-index,invalid-slice-index]
15+
()['0':'1'] # [invalid-slice-index,invalid-slice-index]
16+
""["a":"z"] # [invalid-slice-index,invalid-slice-index]
17+
b""["a":"z"] # [invalid-slice-index,invalid-slice-index]
1518

1619
def function3():
1720
"""class without __index__ used as index"""
@@ -22,10 +25,27 @@ class NoIndexTest:
2225

2326
return TESTLIST[NoIndexTest()::] # [invalid-slice-index]
2427

28+
def invalid_step():
29+
"""0 is an invalid value for slice step with most builtin sequences."""
30+
TESTLIST[::0] # [invalid-slice-step]
31+
[][::0] # [invalid-slice-step]
32+
""[::0] # [invalid-slice-step]
33+
b""[::0] # [invalid-slice-step]
34+
35+
class Custom:
36+
def __getitem__(self, indices):
37+
...
38+
39+
Custom()[::0] # no error -> custom __getitem__ method
40+
41+
def invalid_slice_range():
42+
range(5)['0':'1'] # [invalid-slice-index,invalid-slice-index]
43+
44+
2545
# Valid indices
2646
def function4():
2747
"""integers used as indices"""
28-
return TESTLIST[0:0:0] # no error
48+
return TESTLIST[0:1:1]
2949

3050
def function5():
3151
"""None used as indices"""
Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
invalid-slice-index:10:20:10:22:function1:Slice index is not an int, None, or instance with __index__:UNDEFINED
22
invalid-slice-index:10:23:10:25:function1:Slice index is not an int, None, or instance with __index__:UNDEFINED
3-
invalid-slice-index:14:20:14:23:function2:Slice index is not an int, None, or instance with __index__:UNDEFINED
4-
invalid-slice-index:14:24:14:27:function2:Slice index is not an int, None, or instance with __index__:UNDEFINED
5-
invalid-slice-index:23:20:23:33:function3:Slice index is not an int, None, or instance with __index__:UNDEFINED
3+
invalid-slice-index:14:13:14:16:function2:Slice index is not an int, None, or instance with __index__:UNDEFINED
4+
invalid-slice-index:14:17:14:20:function2:Slice index is not an int, None, or instance with __index__:UNDEFINED
5+
invalid-slice-index:15:7:15:10:function2:Slice index is not an int, None, or instance with __index__:UNDEFINED
6+
invalid-slice-index:15:11:15:14:function2:Slice index is not an int, None, or instance with __index__:UNDEFINED
7+
invalid-slice-index:16:7:16:10:function2:Slice index is not an int, None, or instance with __index__:UNDEFINED
8+
invalid-slice-index:16:11:16:14:function2:Slice index is not an int, None, or instance with __index__:UNDEFINED
9+
invalid-slice-index:17:8:17:11:function2:Slice index is not an int, None, or instance with __index__:UNDEFINED
10+
invalid-slice-index:17:12:17:15:function2:Slice index is not an int, None, or instance with __index__:UNDEFINED
11+
invalid-slice-index:26:20:26:33:function3:Slice index is not an int, None, or instance with __index__:UNDEFINED
12+
invalid-slice-step:30:15:30:16:invalid_step:Slice step cannot be 0:HIGH
13+
invalid-slice-step:31:9:31:10:invalid_step:Slice step cannot be 0:HIGH
14+
invalid-slice-step:32:9:32:10:invalid_step:Slice step cannot be 0:HIGH
15+
invalid-slice-step:33:10:33:11:invalid_step:Slice step cannot be 0:HIGH
16+
invalid-slice-index:42:13:42:16:invalid_slice_range:Slice index is not an int, None, or instance with __index__:UNDEFINED
17+
invalid-slice-index:42:17:42:20:invalid_slice_range:Slice index is not an int, None, or instance with __index__:UNDEFINED

0 commit comments

Comments
 (0)