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
|
from collections.abc import Generator
from dataclasses import dataclass
from mypy.nodes import Block, FuncItem, IfStmt, MatchStmt, ReturnStmt, Statement, WithStmt
from mypy.patterns import AsPattern
from refurb.error import Error
@dataclass
class ErrorInfo(Error):
"""
Don't explicitly return if you are already at the end of the control flow
for the current function:
Bad:
```
def func():
print("hello world!")
return
def func2(x):
if x == 1:
print("x is 1")
else:
print("x is not 1")
return
```
Good:
```
def func():
print("hello world!")
def func2(x):
if x == 1:
print("x is 1")
else:
print("x is not 1")
```
"""
name = "no-redundant-return"
code = 125
msg: str = "Return is redundant here"
categories = ("control-flow", "readability")
def get_trailing_return(node: Statement) -> Generator[Statement, None, None]:
match node:
case ReturnStmt(expr=None):
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 [ReturnStmt()], _:
continue
yield from get_trailing_return(body.body[-1])
case (IfStmt(else_body=Block(body=[*_, stmt])) | WithStmt(body=Block(body=[*_, stmt]))):
yield from get_trailing_return(stmt)
return None
def check(node: FuncItem, errors: list[Error]) -> None:
match node:
case FuncItem(body=Block(body=[*prev, stmt])):
if not prev and isinstance(stmt, ReturnStmt):
return
errors.extend(ErrorInfo.from_node(x) for x in get_trailing_return(stmt))
|