Skip to content

Commit

Permalink
Add more cases to FURB173
Browse files Browse the repository at this point in the history
  • Loading branch information
dosisod committed Jan 6, 2024
1 parent 68fb0a0 commit 661f756
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 3 deletions.
4 changes: 3 additions & 1 deletion refurb/checks/readability/no_unnecessary_cast.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from dataclasses import dataclass

from mypy.nodes import (
ArgKind,
BytesExpr,
CallExpr,
ComplexExpr,
Expand Down Expand Up @@ -83,7 +84,8 @@ def check(node: CallExpr, errors: list[Error]) -> None:
case CallExpr(
callee=NameExpr(fullname=fullname, name=name),
args=[arg],
) if fullname in FUNC_NAMES:
arg_kinds=[arg_kind],
) if arg_kind != ArgKind.ARG_STAR2 and fullname in FUNC_NAMES:
node_type, msg = FUNC_NAMES[fullname]

if type(arg) == node_type:
Expand Down
62 changes: 60 additions & 2 deletions refurb/checks/readability/use_dict_union.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from dataclasses import dataclass
from itertools import groupby

from mypy.nodes import DictExpr, Expression, RefExpr, Var
from mypy.nodes import ArgKind, CallExpr, DictExpr, Expression, RefExpr, Var

from refurb.checks.common import stringify
from refurb.error import Error
from refurb.settings import Settings

Expand Down Expand Up @@ -55,7 +56,7 @@ def is_builtin_mapping(expr: Expression) -> bool:
return False


def check(node: DictExpr, errors: list[Error], settings: Settings) -> None:
def check(node: DictExpr | CallExpr, errors: list[Error], settings: Settings) -> None:
if settings.get_python_version() < (3, 9):
return # pragma: no cover

Expand Down Expand Up @@ -107,3 +108,60 @@ def check(node: DictExpr, errors: list[Error], settings: Settings) -> None:
msg = f"Replace `{{{old_msg}}}` with `{new_msg}`"

errors.append(ErrorInfo.from_node(node, msg))

case CallExpr(callee=RefExpr(fullname="builtins.dict")):
old = []
args: list[str] = []
kwargs: dict[str, str] = {}

# ignore dict(x) since that is covered by FURB123
match node.arg_kinds:
case []:
return

case [ArgKind.ARG_POS]:
return

# TODO: move dict(a=1, b=2) to FURB112
if all(x == ArgKind.ARG_NAMED for x in node.arg_kinds):
return

for arg, name, kind in zip(node.args, node.arg_names, node.arg_kinds):
# ignore dict(*x)
if kind == ArgKind.ARG_STAR:
return

if kind == ArgKind.ARG_STAR2:
old.append(f"**{stringify(arg)}")

stringified_arg = stringify(arg)

if len(node.args) == 1:
# TODO: dict(**x) can be replaced with x.copy() if we know x has a copy()
# method.
stringified_arg = f"{{**{stringified_arg}}}"

args.append(stringified_arg)

elif name:
old.append(f"{name}={stringify(arg)}")
kwargs[name] = stringify(arg)

else:
old.append(stringify(arg))
args.append(stringify(arg))

inner = ", ".join(old)
old_msg = f"dict({inner})"

if kwargs:
kwargs2 = ", ".join(f'"{name}": {expr}' for name, expr in kwargs.items())
kwargs2 = f"{{{kwargs2}}}"

args.append(kwargs2)

new_msg = " | ".join(args)

msg = f"Replace `{old_msg}` with `{new_msg}`"

errors.append(ErrorInfo.from_node(node, msg))
1 change: 1 addition & 0 deletions test/data/err_123.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@
_ = str(123)
_ = tuple([1, 2, 3])
_ = int("0xFF")
_ = dict(**d) # noqa: FURB173
14 changes: 14 additions & 0 deletions test/data/err_173.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@
_ = {"k": "v", **userdict}


_ = dict(**x)
_ = dict(x, **y)
_ = dict(**x, **y)
_ = dict(x, a=1)
_ = dict(**x, a=1, b=2)
_ = dict(**x, **y, a=1, b=2)


# these should not

_ = {}
Expand All @@ -60,3 +68,9 @@ def __getitem__(self, key: str) -> Any:

# TODO: support more expr types
_ = {"k": "v", **{}}


_ = dict(x) # noqa: FURB123
_ = dict(*({},))
_ = dict() # noqa: FURB112
_ = dict(a=1, b=2)
6 changes: 6 additions & 0 deletions test/data/err_173.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ test/data/err_173.py:25:5 [FURB173]: Replace `{..., **x1}` with `{...} | x1`
test/data/err_173.py:28:5 [FURB173]: Replace `{..., **x1}` with `{...} | x1`
test/data/err_173.py:31:5 [FURB173]: Replace `{..., **x1}` with `{...} | x1`
test/data/err_173.py:34:5 [FURB173]: Replace `{..., **x1}` with `{...} | x1`
test/data/err_173.py:37:5 [FURB173]: Replace `dict(**x)` with `{**x}`
test/data/err_173.py:38:5 [FURB173]: Replace `dict(x, **y)` with `x | y`
test/data/err_173.py:39:5 [FURB173]: Replace `dict(**x, **y)` with `x | y`
test/data/err_173.py:40:5 [FURB173]: Replace `dict(x, a=1)` with `x | {"a": 1}`
test/data/err_173.py:41:5 [FURB173]: Replace `dict(**x, a=1, b=2)` with `x | {"a": 1, "b": 2}`
test/data/err_173.py:42:5 [FURB173]: Replace `dict(**x, **y, a=1, b=2)` with `x | y | {"a": 1, "b": 2}`

0 comments on commit 661f756

Please sign in to comment.