diff --git a/docs/categories.md b/docs/categories.md index 02696f9..1e18aa4 100644 --- a/docs/categories.md +++ b/docs/categories.md @@ -49,6 +49,11 @@ These checks cover: These checks relate to the [itertools](https://docs.python.org/3/library/itertools.html) standard library module. +## `math` + +These checks relate to the [math](https://docs.python.org/3/library/math.html) +standard library module. + ## `operator` These checks relate to the [operator](https://docs.python.org/3/library/operator.html) diff --git a/refurb/checks/math/__init__.py b/refurb/checks/math/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/refurb/checks/math/use_constants.py b/refurb/checks/math/use_constants.py new file mode 100644 index 0000000..b64484b --- /dev/null +++ b/refurb/checks/math/use_constants.py @@ -0,0 +1,56 @@ +from dataclasses import dataclass + +from mypy.nodes import FloatExpr + +from refurb.error import Error + + +@dataclass +class ErrorInfo(Error): + """ + Don't hardcode math constants like pi, tau, or e, use the `math.pi`, + `math.tau`, or `math.e` constants respectively. + + Bad: + + ``` + def area(r: float) -> float: + return 3.1415 * r * r + ``` + + Good: + + ``` + import math + + def area(r: float) -> float: + return math.pi * r * r + ``` + """ + + code = 152 + categories = ["math", "readability"] + + +CONSTANTS = { + "pi": "3.14", + "e": "2.71", + "tau": "6.28", +} + + +def check(node: FloatExpr, errors: list[Error]) -> None: + num = str(node.value) + + if len(num) <= 3: + return None + + for name, value in CONSTANTS.items(): + if num.startswith(value): + errors.append( + ErrorInfo( + node.line, + node.column, + f"Replace `{num}` with `math.{name}`", + ) + ) diff --git a/test/data/err_123.py b/test/data/err_123.py index 9e122b2..3887536 100644 --- a/test/data/err_123.py +++ b/test/data/err_123.py @@ -4,7 +4,7 @@ _ = bytes(b"hello world") _ = complex(1j) _ = dict({"a": 1}) -_ = float(3.14) +_ = float(123.456) _ = list([1, 2, 3]) _ = str("hello world") _ = tuple((1, 2, 3)) @@ -22,7 +22,7 @@ d = {"a": 1} _ = dict(d) -e = 3.14 +e = 123.456 _ = float(e) f = [1, 2, 3] diff --git a/test/data/err_152.py b/test/data/err_152.py new file mode 100644 index 0000000..9ac7b13 --- /dev/null +++ b/test/data/err_152.py @@ -0,0 +1,22 @@ +# these should match + +pi = 3.14 +pi = 3.1415 +pi = 003.1400 +pi = -3.14 +e = 2.71 +e = 2.71828 +e = -2.71 +tau = 6.28 +tau = 6.2831 +tau = 2 * 3.14 +tau = -6.28 + + +# these should not + +_ = 3.1 +_ = 2.7 +_ = 6.2 +_ = 1234 +_ = 3.0 diff --git a/test/data/err_152.txt b/test/data/err_152.txt new file mode 100644 index 0000000..237f66c --- /dev/null +++ b/test/data/err_152.txt @@ -0,0 +1,11 @@ +test/data/err_152.py:3:6 [FURB152]: Replace `3.14` with `math.pi` +test/data/err_152.py:4:6 [FURB152]: Replace `3.1415` with `math.pi` +test/data/err_152.py:5:6 [FURB152]: Replace `3.14` with `math.pi` +test/data/err_152.py:6:7 [FURB152]: Replace `3.14` with `math.pi` +test/data/err_152.py:7:5 [FURB152]: Replace `2.71` with `math.e` +test/data/err_152.py:8:5 [FURB152]: Replace `2.71828` with `math.e` +test/data/err_152.py:9:6 [FURB152]: Replace `2.71` with `math.e` +test/data/err_152.py:10:7 [FURB152]: Replace `6.28` with `math.tau` +test/data/err_152.py:11:7 [FURB152]: Replace `6.2831` with `math.tau` +test/data/err_152.py:12:11 [FURB152]: Replace `3.14` with `math.pi` +test/data/err_152.py:13:8 [FURB152]: Replace `6.28` with `math.tau`