Skip to content

Commit

Permalink
Add better type deduction for no-default-or
Browse files Browse the repository at this point in the history
  • Loading branch information
dosisod committed Feb 11, 2024
1 parent 15d504a commit 4098c21
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 49 deletions.
29 changes: 28 additions & 1 deletion refurb/checks/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ def is_same_type(ty: Type | TypeInfo | None, *expected: TypeLike) -> bool:
return any(_is_same_type(ty, t) for t in expected)


SIMPLE_TYPES = {
SIMPLE_TYPES: dict[str, type | object | None] = {
"Any": Any,
"None": None,
"builtins.bool": bool,
Expand Down Expand Up @@ -523,6 +523,24 @@ def get_mypy_type(node: Node) -> Type | None:
case StrExpr():
return _get_builtin_mypy_type("str")

case BytesExpr():
return _get_builtin_mypy_type("bytes")

case IntExpr():
return _get_builtin_mypy_type("int")

case FloatExpr():
return _get_builtin_mypy_type("float")

case DictExpr():
return _get_builtin_mypy_type("dict")

case ListExpr():
return _get_builtin_mypy_type("list")

case TupleExpr():
return _get_builtin_mypy_type("tuple")

case Var(type=ty):
return ty

Expand Down Expand Up @@ -566,3 +584,12 @@ def get_mypy_type(node: Node) -> Type | None:
return ty

return None


def mypy_type_to_python_type(ty: Type | None) -> type | None:
match ty:
# TODO: return annotated types if instance has args (ie, `list[int]`)
case Instance(type=TypeInfo(fullname=fullname)):
return SIMPLE_TYPES.get(fullname) # type: ignore

return None # pragma: no cover
75 changes: 30 additions & 45 deletions refurb/checks/readability/no_or_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@
BytesExpr,
CallExpr,
DictExpr,
FloatExpr,
IntExpr,
ListExpr,
NameExpr,
OpExpr,
StrExpr,
TupleExpr,
Var,
)

from refurb.checks.common import extract_binary_oper, is_same_type, stringify
from refurb.checks.common import (
extract_binary_oper,
get_mypy_type,
is_same_type,
mypy_type_to_python_type,
stringify,
)
from refurb.error import Error


Expand Down Expand Up @@ -49,46 +55,25 @@ def is_markdown_header(line: str) -> bool:

def check(node: OpExpr, errors: list[Error]) -> None:
match extract_binary_oper("or", node):
case (NameExpr(node=Var(type=ty)) as lhs, arg):
match arg:
case CallExpr(callee=NameExpr(fullname=fullname), args=[]):
if fullname == "builtins.set":
expected_type: type = set

elif fullname == "builtins.frozenset":
expected_type = frozenset

else:
return

case ListExpr(items=[]):
expected_type = list

case DictExpr(items=[]):
expected_type = dict

case TupleExpr(items=[]):
expected_type = tuple

case StrExpr(value=""):
expected_type = str

case BytesExpr(value=""):
expected_type = bytes

case IntExpr(value=0):
expected_type = int

case NameExpr(fullname="builtins.False"):
expected_type = bool

case _:
return

if is_same_type(ty, expected_type):
lhs_expr = stringify(lhs)
rhs_expr = stringify(arg)

msg = f"Replace `{lhs_expr} or {rhs_expr}` with `{lhs_expr}`"

errors.append(ErrorInfo.from_node(node, msg))
case (
lhs,
(
CallExpr(callee=NameExpr(fullname="builtins.set" | "builtins.frozenset"), args=[])
| ListExpr(items=[])
| DictExpr(items=[])
| TupleExpr(items=[])
| StrExpr(value="")
| BytesExpr(value="")
| IntExpr(value=0)
| FloatExpr(value=0.0)
| NameExpr(fullname="builtins.False")
) as rhs,
) if (
(expected_type := mypy_type_to_python_type(get_mypy_type(rhs)))
and is_same_type(get_mypy_type(lhs), expected_type)
):
lhs_expr = stringify(lhs)

msg = f"Replace `{lhs_expr} or {stringify(rhs)}` with `{lhs_expr}`"

errors.append(ErrorInfo.from_node(node, msg))
7 changes: 4 additions & 3 deletions test/data/err_143.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,17 @@
_ = n or False
_ = i or 0

fl = 0.0
_ = fl or 0.0


class C:
x: int
def __init__(self) -> None:
# x could be anything here
self.x = 123

c = C()

_ = c.x or 0
_ = C().x or 0


# these should not
Expand Down
3 changes: 3 additions & 0 deletions test/data/err_143.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ test/data/err_143.py:20:5 [FURB143]: Replace `r or ""` with `r`
test/data/err_143.py:21:5 [FURB143]: Replace `b or b""` with `b`
test/data/err_143.py:22:5 [FURB143]: Replace `n or False` with `n`
test/data/err_143.py:23:5 [FURB143]: Replace `i or 0` with `i`
test/data/err_143.py:26:5 [FURB143]: Replace `fl or 0.0` with `fl`
test/data/err_143.py:34:5 [FURB143]: Replace `c.x or 0` with `c.x`
test/data/err_143.py:35:5 [FURB143]: Replace `C().x or 0` with `C().x`

0 comments on commit 4098c21

Please sign in to comment.