Skip to content

Commit 0b963dd

Browse files
ThomasdezeeuwThomas de ZeeuwMichaReiser
authored
Add unreachable code rule (#5384)
Co-authored-by: Thomas de Zeeuw <[email protected]> Co-authored-by: Micha Reiser <[email protected]>
1 parent 937de12 commit 0b963dd

30 files changed

+4688
-4
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ crates/ruff/resources/test/fixtures/isort/line_ending_crlf.py text eol=crlf
44
crates/ruff/resources/test/fixtures/pycodestyle/W605_1.py text eol=crlf
55

66
ruff.schema.json linguist-generated=true text=auto eol=lf
7+
*.md.snap linguist-language=Markdown

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ruff/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ name = "ruff"
1717
[dependencies]
1818
ruff_cache = { path = "../ruff_cache" }
1919
ruff_diagnostics = { path = "../ruff_diagnostics", features = ["serde"] }
20+
ruff_index = { path = "../ruff_index" }
2021
ruff_macros = { path = "../ruff_macros" }
2122
ruff_python_whitespace = { path = "../ruff_python_whitespace" }
2223
ruff_python_ast = { path = "../ruff_python_ast", features = ["serde"] }
@@ -88,3 +89,5 @@ colored = { workspace = true, features = ["no-color"] }
8889
[features]
8990
default = []
9091
schemars = ["dep:schemars"]
92+
# Enables the UnreachableCode rule
93+
unreachable-code = []
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
def func():
2+
assert True
3+
4+
def func():
5+
assert False
6+
7+
def func():
8+
assert True, "oops"
9+
10+
def func():
11+
assert False, "oops"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
def func():
2+
async for i in range(5):
3+
print(i)
4+
5+
def func():
6+
async for i in range(20):
7+
print(i)
8+
else:
9+
return 0
10+
11+
def func():
12+
async for i in range(10):
13+
if i == 5:
14+
return 1
15+
return 0
16+
17+
def func():
18+
async for i in range(111):
19+
if i == 5:
20+
return 1
21+
else:
22+
return 0
23+
return 2
24+
25+
def func():
26+
async for i in range(12):
27+
continue
28+
29+
def func():
30+
async for i in range(1110):
31+
if True:
32+
continue
33+
34+
def func():
35+
async for i in range(13):
36+
break
37+
38+
def func():
39+
async for i in range(1110):
40+
if True:
41+
break
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
def func():
2+
for i in range(5):
3+
print(i)
4+
5+
def func():
6+
for i in range(20):
7+
print(i)
8+
else:
9+
return 0
10+
11+
def func():
12+
for i in range(10):
13+
if i == 5:
14+
return 1
15+
return 0
16+
17+
def func():
18+
for i in range(111):
19+
if i == 5:
20+
return 1
21+
else:
22+
return 0
23+
return 2
24+
25+
def func():
26+
for i in range(12):
27+
continue
28+
29+
def func():
30+
for i in range(1110):
31+
if True:
32+
continue
33+
34+
def func():
35+
for i in range(13):
36+
break
37+
38+
def func():
39+
for i in range(1110):
40+
if True:
41+
break
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
def func():
2+
if False:
3+
return 0
4+
return 1
5+
6+
def func():
7+
if True:
8+
return 1
9+
return 0
10+
11+
def func():
12+
if False:
13+
return 0
14+
else:
15+
return 1
16+
17+
def func():
18+
if True:
19+
return 1
20+
else:
21+
return 0
22+
23+
def func():
24+
if False:
25+
return 0
26+
else:
27+
return 1
28+
return "unreachable"
29+
30+
def func():
31+
if True:
32+
return 1
33+
else:
34+
return 0
35+
return "unreachable"
36+
37+
def func():
38+
if True:
39+
if True:
40+
return 1
41+
return 2
42+
else:
43+
return 3
44+
return "unreachable2"
45+
46+
def func():
47+
if False:
48+
return 0
49+
50+
def func():
51+
if True:
52+
return 1
53+
54+
def func():
55+
if True:
56+
return 1
57+
elif False:
58+
return 2
59+
else:
60+
return 0
61+
62+
def func():
63+
if False:
64+
return 1
65+
elif True:
66+
return 2
67+
else:
68+
return 0
69+
70+
def func():
71+
if True:
72+
if False:
73+
return 0
74+
elif True:
75+
return 1
76+
else:
77+
return 2
78+
return 3
79+
elif True:
80+
return 4
81+
else:
82+
return 5
83+
return 6
84+
85+
def func():
86+
if False:
87+
return "unreached"
88+
elif False:
89+
return "also unreached"
90+
return "reached"
91+
92+
# Test case found in the Bokeh repository that trigger a false positive.
93+
def func(self, obj: BytesRep) -> bytes:
94+
data = obj["data"]
95+
96+
if isinstance(data, str):
97+
return base64.b64decode(data)
98+
elif isinstance(data, Buffer):
99+
buffer = data
100+
else:
101+
id = data["id"]
102+
103+
if id in self._buffers:
104+
buffer = self._buffers[id]
105+
else:
106+
self.error(f"can't resolve buffer '{id}'")
107+
108+
return buffer.data
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
def func(status):
2+
match status:
3+
case _:
4+
return 0
5+
return "unreachable"
6+
7+
def func(status):
8+
match status:
9+
case 1:
10+
return 1
11+
return 0
12+
13+
def func(status):
14+
match status:
15+
case 1:
16+
return 1
17+
case _:
18+
return 0
19+
20+
def func(status):
21+
match status:
22+
case 1 | 2 | 3:
23+
return 5
24+
return 6
25+
26+
def func(status):
27+
match status:
28+
case 1 | 2 | 3:
29+
return 5
30+
case _:
31+
return 10
32+
return 0
33+
34+
def func(status):
35+
match status:
36+
case 0:
37+
return 0
38+
case 1:
39+
return 1
40+
case 1:
41+
return "1 again"
42+
case _:
43+
return 3
44+
45+
def func(status):
46+
i = 0
47+
match status, i:
48+
case _, _:
49+
return 0
50+
51+
def func(status):
52+
i = 0
53+
match status, i:
54+
case _, 0:
55+
return 0
56+
case _, 2:
57+
return 0
58+
59+
def func(point):
60+
match point:
61+
case (0, 0):
62+
print("Origin")
63+
case _:
64+
raise ValueError("oops")
65+
66+
def func(point):
67+
match point:
68+
case (0, 0):
69+
print("Origin")
70+
case (0, y):
71+
print(f"Y={y}")
72+
case (x, 0):
73+
print(f"X={x}")
74+
case (x, y):
75+
print(f"X={x}, Y={y}")
76+
case _:
77+
raise ValueError("Not a point")
78+
79+
def where_is(point):
80+
class Point:
81+
x: int
82+
y: int
83+
84+
match point:
85+
case Point(x=0, y=0):
86+
print("Origin")
87+
case Point(x=0, y=y):
88+
print(f"Y={y}")
89+
case Point(x=x, y=0):
90+
print(f"X={x}")
91+
case Point():
92+
print("Somewhere else")
93+
case _:
94+
print("Not a point")
95+
96+
def func(points):
97+
match points:
98+
case []:
99+
print("No points")
100+
case [Point(0, 0)]:
101+
print("The origin")
102+
case [Point(x, y)]:
103+
print(f"Single point {x}, {y}")
104+
case [Point(0, y1), Point(0, y2)]:
105+
print(f"Two on the Y axis at {y1}, {y2}")
106+
case _:
107+
print("Something else")
108+
109+
def func(point):
110+
match point:
111+
case Point(x, y) if x == y:
112+
print(f"Y=X at {x}")
113+
case Point(x, y):
114+
print(f"Not on the diagonal")
115+
116+
def func():
117+
from enum import Enum
118+
class Color(Enum):
119+
RED = 'red'
120+
GREEN = 'green'
121+
BLUE = 'blue'
122+
123+
color = Color(input("Enter your choice of 'red', 'blue' or 'green': "))
124+
125+
match color:
126+
case Color.RED:
127+
print("I see red!")
128+
case Color.GREEN:
129+
print("Grass is green")
130+
case Color.BLUE:
131+
print("I'm feeling the blues :(")
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
def func():
2+
raise Exception
3+
4+
def func():
5+
raise "a glass!"
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
def func():
2+
pass
3+
4+
def func():
5+
pass
6+
7+
def func():
8+
return
9+
10+
def func():
11+
return 1
12+
13+
def func():
14+
return 1
15+
return "unreachable"
16+
17+
def func():
18+
i = 0
19+
20+
def func():
21+
i = 0
22+
i += 2
23+
return i

0 commit comments

Comments
 (0)