1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
|
from collections.abc import Generator
from dataclasses import dataclass
from mypy.nodes import (
Block,
ContinueStmt,
ForStmt,
IfStmt,
MatchStmt,
Statement,
WhileStmt,
WithStmt,
)
from mypy.patterns import AsPattern
from refurb.error import Error
@dataclass
class ErrorInfo(Error):
"""
Don't explicitly continue if you are already at the end of the control flow
for the current for/while loop:
Bad:
```
def func():
for _ in range(10):
print("hello world!")
continue
def func2(x):
for x in range(10):
if x == 1:
print("x is 1")
else:
print("x is not 1")
continue
```
Good:
```
def func():
for _ in range(10):
print("hello world!")
def func2(x):
for x in range(10):
if x == 1:
print("x is 1")
else:
print("x is not 1")
```
"""
name = "no-redundant-continue"
code = 133
msg: str = "Continue is redundant here"
categories = ("control-flow", "readability")
def get_trailing_continue(node: Statement) -> Generator[Statement, None, None]:
match node:
case ContinueStmt():
yield node
case MatchStmt(bodies=bodies, patterns=patterns):
for body, pattern in zip(bodies, patterns):
match (body.body, pattern):
case _, AsPattern(pattern=None, name=None):
pass
case [ContinueStmt()], _:
continue
yield from get_trailing_continue(body.body[-1])
case (IfStmt(else_body=Block(body=[*_, stmt])) | WithStmt(body=Block(body=[*_, stmt]))):
yield from get_trailing_continue(stmt)
return None
def check(node: ForStmt | WhileStmt, errors: list[Error]) -> None:
match node:
case (ForStmt(body=Block(body=[*prev, stmt])) | WhileStmt(body=Block(body=[*prev, stmt]))):
if not prev and isinstance(stmt, ContinueStmt):
return
errors.extend(ErrorInfo.from_node(x) for x in get_trailing_continue(stmt))
|