Compare commits
88 Commits
nextest
...
charlie/ou
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbeda1a8ab | ||
|
|
154fe7bdcc | ||
|
|
ece30e7c69 | ||
|
|
7be28a38c5 | ||
|
|
5ec73a6137 | ||
|
|
1067261a55 | ||
|
|
93ca8ebbc0 | ||
|
|
834566f34f | ||
|
|
a56121672c | ||
|
|
32f4a96c64 | ||
|
|
c004e03395 | ||
|
|
b57ddd54d2 | ||
|
|
af189db5eb | ||
|
|
c7217e34d7 | ||
|
|
d70247959c | ||
|
|
911d4f2918 | ||
|
|
d9cf31f355 | ||
|
|
7da99cc756 | ||
|
|
9c3b2c3c3c | ||
|
|
37d244d178 | ||
|
|
dbb34804a4 | ||
|
|
3c7486817b | ||
|
|
3c3c5b5c57 | ||
|
|
b0d171ac19 | ||
|
|
b70dde4a77 | ||
|
|
4eaa412370 | ||
|
|
577280c8be | ||
|
|
45680bbb44 | ||
|
|
71ff6f911d | ||
|
|
c05e4628b1 | ||
|
|
2f3a950f6f | ||
|
|
dea65536e9 | ||
|
|
fbc9b5a604 | ||
|
|
253a241f5d | ||
|
|
33806b8b7c | ||
|
|
afcd00da56 | ||
|
|
08e246764f | ||
|
|
0489bbc54c | ||
|
|
60132da7bb | ||
|
|
17a44c0078 | ||
|
|
376d3caf47 | ||
|
|
51d69b448c | ||
|
|
f7dca3d958 | ||
|
|
7c1aa98f43 | ||
|
|
68f605e80a | ||
|
|
96a9717c1a | ||
|
|
f4ba0ea144 | ||
|
|
92143afeee | ||
|
|
eb552da8a9 | ||
|
|
e2b2b1759f | ||
|
|
a3f4d7745a | ||
|
|
edfd888bd6 | ||
|
|
9c382e8291 | ||
|
|
eb2b226142 | ||
|
|
31947af6a3 | ||
|
|
b404e54f33 | ||
|
|
34e8de738e | ||
|
|
1550a6bfe7 | ||
|
|
dc4db39f78 | ||
|
|
f3aaf84a28 | ||
|
|
5de95d7054 | ||
|
|
8d1610d960 | ||
|
|
fad23bbe60 | ||
|
|
25c374856a | ||
|
|
34221346c1 | ||
|
|
924f10186f | ||
|
|
b7634b6ede | ||
|
|
4d49d5e845 | ||
|
|
715d86dae9 | ||
|
|
adb48692d6 | ||
|
|
19ccf1d073 | ||
|
|
e3c36465ec | ||
|
|
7e36284684 | ||
|
|
c448b4086a | ||
|
|
3200015c06 | ||
|
|
26d53c56a2 | ||
|
|
1439bb592e | ||
|
|
fc47e0dab2 | ||
|
|
87aa5d6b66 | ||
|
|
d1ad20c9ea | ||
|
|
ecca125f9a | ||
|
|
005e21a139 | ||
|
|
e1db036f90 | ||
|
|
9ad67b0758 | ||
|
|
af61abc747 | ||
|
|
ec575188c4 | ||
|
|
aea7500c1e | ||
|
|
58f5f27dc3 |
12
.github/dependabot.yml
vendored
12
.github/dependabot.yml
vendored
@@ -4,8 +4,10 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "monday"
|
||||
time: "12:00"
|
||||
timezone: "America/New_York"
|
||||
commit-message:
|
||||
prefix: "ci(deps)"
|
||||
labels: ["internal"]
|
||||
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels: ["internal"]
|
||||
|
||||
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: tj-actions/changed-files@v37
|
||||
- uses: tj-actions/changed-files@v38
|
||||
id: changed
|
||||
with:
|
||||
files_yaml: |
|
||||
|
||||
2
.github/workflows/docs.yaml
vendored
2
.github/workflows/docs.yaml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
run: mkdocs build --strict -f mkdocs.generated.yml
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@v3.1.0
|
||||
uses: cloudflare/wrangler-action@v3.1.1
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
|
||||
2
.github/workflows/playground.yaml
vendored
2
.github/workflows/playground.yaml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
working-directory: playground
|
||||
- name: "Deploy to Cloudflare Pages"
|
||||
if: ${{ env.CF_API_TOKEN_EXISTS == 'true' }}
|
||||
uses: cloudflare/wrangler-action@v3.1.0
|
||||
uses: cloudflare/wrangler-action@v3.1.1
|
||||
with:
|
||||
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||
accountId: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
|
||||
@@ -129,6 +129,7 @@ At time of writing, the repository includes the following crates:
|
||||
intermediate representation. The backend for `ruff_python_formatter`.
|
||||
- `crates/ruff_index`: library crate inspired by `rustc_index`.
|
||||
- `crates/ruff_macros`: proc macro crate containing macros used by Ruff.
|
||||
- `crates/ruff_notebook`: library crate for parsing and manipulating Jupyter notebooks.
|
||||
- `crates/ruff_python_ast`: library crate containing Python-specific AST types and utilities.
|
||||
- `crates/ruff_python_codegen`: library crate containing utilities for generating Python source code.
|
||||
- `crates/ruff_python_formatter`: library crate implementing the Python formatter. Emits an
|
||||
|
||||
589
Cargo.lock
generated
589
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -54,7 +54,7 @@ uuid = { version = "1.4.1", features = ["v4", "fast-rng", "macro-diagnostics", "
|
||||
wsl = { version = "0.1.0" }
|
||||
|
||||
# v1.0.1
|
||||
libcst = { git = "https://github.com/Instagram/LibCST.git", rev = "3cacca1a1029f05707e50703b49fe3dd860aa839", default-features = false }
|
||||
libcst = { git = "https://github.com/Instagram/LibCST.git", rev = "9c263aa8977962a870ce2770d2aa18ee0dacb344", default-features = false }
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
|
||||
@@ -140,7 +140,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.0.286
|
||||
rev: v0.0.287
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -398,6 +398,7 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [Pydantic](https://github.com/pydantic/pydantic)
|
||||
- [Pylint](https://github.com/PyCQA/pylint)
|
||||
- [Reflex](https://github.com/reflex-dev/reflex)
|
||||
- [Rippling](https://rippling.com)
|
||||
- [Robyn](https://github.com/sansyrox/robyn)
|
||||
- Scale AI ([Launch SDK](https://github.com/scaleapi/launch-python-client))
|
||||
- Snowflake ([SnowCLI](https://github.com/Snowflake-Labs/snowcli))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.286"
|
||||
version = "0.0.287"
|
||||
description = """
|
||||
Convert Flake8 configuration files to Ruff configuration files.
|
||||
"""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.286"
|
||||
version = "0.0.287"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -18,6 +18,7 @@ name = "ruff"
|
||||
ruff_cache = { path = "../ruff_cache" }
|
||||
ruff_diagnostics = { path = "../ruff_diagnostics", features = ["serde"] }
|
||||
ruff_index = { path = "../ruff_index" }
|
||||
ruff_notebook = { path = "../ruff_notebook" }
|
||||
ruff_macros = { path = "../ruff_macros" }
|
||||
ruff_python_ast = { path = "../ruff_python_ast", features = ["serde"] }
|
||||
ruff_python_codegen = { path = "../ruff_python_codegen" }
|
||||
@@ -64,17 +65,15 @@ schemars = { workspace = true, optional = true }
|
||||
semver = { version = "1.0.16" }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
serde_with = { version = "3.0.0" }
|
||||
similar = { workspace = true }
|
||||
smallvec = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
strum_macros = { workspace = true }
|
||||
thiserror = { version = "1.0.43" }
|
||||
thiserror = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
typed-arena = { version = "2.0.2" }
|
||||
unicode-width = { workspace = true }
|
||||
unicode_names2 = { version = "0.6.0", git = "https://github.com/youknowone/unicode_names2.git", rev = "4ce16aa85cbcdd9cc830410f1a72ef9a235f2fde" }
|
||||
uuid = { workspace = true, features = ["v4", "fast-rng", "macro-diagnostics", "js"] }
|
||||
wsl = { version = "0.1.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -308,3 +308,7 @@ def single_line_func_wrong(value: dict[str, str] = {
|
||||
def single_line_func_wrong(value: dict[str, str] = {}) \
|
||||
: \
|
||||
"""Docstring"""
|
||||
|
||||
|
||||
def single_line_func_wrong(value: dict[str, str] = {}):
|
||||
"""Docstring without newline"""
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Should emit:
|
||||
B009 - Line 19, 20, 21, 22, 23, 24
|
||||
B010 - Line 40, 41, 42, 43, 44, 45
|
||||
B009 - Lines 19-31
|
||||
B010 - Lines 40-45
|
||||
"""
|
||||
|
||||
# Valid getattr usage
|
||||
@@ -24,6 +24,16 @@ getattr(foo, r"abc123")
|
||||
_ = lambda x: getattr(x, "bar")
|
||||
if getattr(x, "bar"):
|
||||
pass
|
||||
getattr(1, "real")
|
||||
getattr(1., "real")
|
||||
getattr(1.0, "real")
|
||||
getattr(1j, "real")
|
||||
getattr(True, "real")
|
||||
getattr(x := 1, "real")
|
||||
getattr(x + y, "real")
|
||||
getattr("foo"
|
||||
"bar", "real")
|
||||
|
||||
|
||||
# Valid setattr usage
|
||||
setattr(foo, bar, None)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
retriable_exceptions = (FileExistsError, FileNotFoundError)
|
||||
|
||||
try:
|
||||
pass
|
||||
except (ValueError,):
|
||||
@@ -6,3 +8,5 @@ except AttributeError:
|
||||
pass
|
||||
except (ImportError, TypeError):
|
||||
pass
|
||||
except (*retriable_exceptions,):
|
||||
pass
|
||||
|
||||
@@ -5,6 +5,7 @@ map(lambda x: str(x), nums)
|
||||
list(map(lambda x: x * 2, nums))
|
||||
set(map(lambda x: x % 2 == 0, nums))
|
||||
dict(map(lambda v: (v, v**2), nums))
|
||||
dict(map(lambda v: [v, v**2], nums))
|
||||
map(lambda: "const", nums)
|
||||
map(lambda _: 3.0, nums)
|
||||
_ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
@@ -37,3 +38,6 @@ map(lambda x: lambda x: x, range(4))
|
||||
map(lambda x=1: x, nums)
|
||||
map(lambda *args: len(args), range(4))
|
||||
map(lambda **kwargs: len(kwargs), range(4))
|
||||
|
||||
# Ok because multiple arguments are allowed.
|
||||
dict(map(lambda k, v: (k, v), keys, values))
|
||||
|
||||
@@ -3,16 +3,20 @@ def bar():
|
||||
|
||||
|
||||
def foo():
|
||||
"""foo""" # OK
|
||||
"""foo""" # OK, docstrings are handled by another rule
|
||||
|
||||
|
||||
def buzz():
|
||||
print("buzz") # OK, not in stub file
|
||||
print("buzz") # ERROR PYI010
|
||||
|
||||
|
||||
def foo2():
|
||||
123 # OK, not in a stub file
|
||||
123 # ERROR PYI010
|
||||
|
||||
|
||||
def bizz():
|
||||
x = 123 # OK, not in a stub file
|
||||
x = 123 # ERROR PYI010
|
||||
|
||||
|
||||
def foo3():
|
||||
pass # OK, pass is handled by another rule
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
def bar(): ... # OK
|
||||
def foo():
|
||||
"""foo""" # OK, strings are handled by another rule
|
||||
"""foo""" # OK, docstrings are handled by another rule
|
||||
|
||||
def buzz():
|
||||
print("buzz") # ERROR PYI010
|
||||
@@ -10,3 +10,6 @@ def foo2():
|
||||
|
||||
def bizz():
|
||||
x = 123 # ERROR PYI010
|
||||
|
||||
def foo3():
|
||||
pass # OK, pass is handled by another rule
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
def bar(): # OK
|
||||
...
|
||||
def bar():
|
||||
... # OK
|
||||
|
||||
|
||||
def oof(): # OK, docstrings are handled by another rule
|
||||
def bar():
|
||||
pass # OK
|
||||
|
||||
|
||||
def bar():
|
||||
"""oof""" # OK
|
||||
|
||||
|
||||
def oof(): # ERROR PYI048
|
||||
"""oof"""
|
||||
print("foo")
|
||||
|
||||
|
||||
def foo(): # Ok not in Stub file
|
||||
def foo(): # ERROR PYI048
|
||||
"""foo"""
|
||||
print("foo")
|
||||
print("foo")
|
||||
|
||||
|
||||
def buzz(): # Ok not in Stub file
|
||||
def buzz(): # ERROR PYI048
|
||||
print("fizz")
|
||||
print("buzz")
|
||||
print("test")
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
def bar(): ... # OK
|
||||
def bar():
|
||||
... # OK
|
||||
pass # OK
|
||||
|
||||
def bar():
|
||||
"""oof""" # OK
|
||||
|
||||
def oof(): # OK, docstrings are handled by another rule
|
||||
"""oof"""
|
||||
print("foo")
|
||||
def oof(): # ERROR PYI048
|
||||
"""oof"""
|
||||
print("foo")
|
||||
|
||||
|
||||
|
||||
def foo(): # ERROR PYI048
|
||||
def foo(): # ERROR PYI048
|
||||
"""foo"""
|
||||
print("foo")
|
||||
print("foo")
|
||||
|
||||
|
||||
def buzz(): # ERROR PYI048
|
||||
def buzz(): # ERROR PYI048
|
||||
print("fizz")
|
||||
print("buzz")
|
||||
print("test")
|
||||
|
||||
@@ -357,3 +357,9 @@ def foo():
|
||||
def foo():
|
||||
a = 1 # Comment
|
||||
return a
|
||||
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7098
|
||||
def mavko_debari(P_kbar):
|
||||
D=0.4853881 + 3.6006116*P - 0.0117368*(P-1.3822)**2
|
||||
return D
|
||||
|
||||
@@ -13,3 +13,8 @@ def f():
|
||||
return False
|
||||
|
||||
a = True if b else False
|
||||
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7076
|
||||
samesld = True if (psl.privatesuffix(urlparse(response.url).netloc) ==
|
||||
psl.privatesuffix(src.netloc)) else False
|
||||
|
||||
@@ -152,3 +152,11 @@ if (a or [1] or True or [2]) == (a or [1]): # SIM222
|
||||
|
||||
if f(a or [1] or True or [2]): # SIM222
|
||||
pass
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7099
|
||||
def secondToTime(s0: int) -> (int, int, int) or str:
|
||||
m, s = divmod(s0, 60)
|
||||
|
||||
|
||||
def secondToTime(s0: int) -> ((int, int, int) or str):
|
||||
m, s = divmod(s0, 60)
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import numpy as np
|
||||
def func():
|
||||
import numpy as np
|
||||
|
||||
np.round_(np.random.rand(5, 5), 2)
|
||||
np.product(np.random.rand(5, 5))
|
||||
np.cumproduct(np.random.rand(5, 5))
|
||||
np.sometrue(np.random.rand(5, 5))
|
||||
np.alltrue(np.random.rand(5, 5))
|
||||
np.round_(np.random.rand(5, 5), 2)
|
||||
np.product(np.random.rand(5, 5))
|
||||
np.cumproduct(np.random.rand(5, 5))
|
||||
np.sometrue(np.random.rand(5, 5))
|
||||
np.alltrue(np.random.rand(5, 5))
|
||||
|
||||
from numpy import round_, product, cumproduct, sometrue, alltrue
|
||||
|
||||
round_(np.random.rand(5, 5), 2)
|
||||
product(np.random.rand(5, 5))
|
||||
cumproduct(np.random.rand(5, 5))
|
||||
sometrue(np.random.rand(5, 5))
|
||||
alltrue(np.random.rand(5, 5))
|
||||
def func():
|
||||
from numpy import round_, product, cumproduct, sometrue, alltrue
|
||||
|
||||
round_(np.random.rand(5, 5), 2)
|
||||
product(np.random.rand(5, 5))
|
||||
cumproduct(np.random.rand(5, 5))
|
||||
sometrue(np.random.rand(5, 5))
|
||||
alltrue(np.random.rand(5, 5))
|
||||
|
||||
@@ -29,3 +29,5 @@ x.apply(lambda x: x.sort_values("a", inplace=True))
|
||||
import torch
|
||||
|
||||
torch.m.ReLU(inplace=True) # safe because this isn't a pandas call
|
||||
|
||||
(x.drop(["a"], axis=1, inplace=True))
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import collections
|
||||
from collections import namedtuple
|
||||
from typing import TypeVar
|
||||
from typing import NewType
|
||||
from typing import NamedTuple, TypedDict
|
||||
from typing import TypeAlias, TypeVar, NewType, NamedTuple, TypedDict
|
||||
|
||||
GLOBAL: str = "foo"
|
||||
|
||||
@@ -21,9 +19,11 @@ def assign():
|
||||
T = TypeVar("T")
|
||||
UserId = NewType("UserId", int)
|
||||
|
||||
Employee = NamedTuple('Employee', [('name', str), ('id', int)])
|
||||
Employee = NamedTuple("Employee", [("name", str), ("id", int)])
|
||||
|
||||
Point2D = TypedDict('Point2D', {'in': int, 'x-y': int})
|
||||
Point2D = TypedDict("Point2D", {"in": int, "x-y": int})
|
||||
|
||||
IntOrStr: TypeAlias = int | str
|
||||
|
||||
|
||||
def aug_assign(rank, world_size):
|
||||
|
||||
@@ -18,11 +18,11 @@ def f():
|
||||
result = []
|
||||
for i in items:
|
||||
if i % 2:
|
||||
result.append(i) # PERF401
|
||||
result.append(i) # Ok
|
||||
elif i % 2:
|
||||
result.append(i) # PERF401
|
||||
result.append(i)
|
||||
else:
|
||||
result.append(i) # PERF401
|
||||
result.append(i)
|
||||
|
||||
|
||||
def f():
|
||||
@@ -60,3 +60,15 @@ def f():
|
||||
for i in range(20):
|
||||
foo.fibonacci.append(sum(foo.fibonacci[-2:])) # OK
|
||||
print(foo.fibonacci)
|
||||
|
||||
|
||||
class Foo:
|
||||
def append(self, x):
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = Foo()
|
||||
for i in items:
|
||||
result.append(i) # Ok
|
||||
|
||||
@@ -24,3 +24,22 @@ def f():
|
||||
result = {}
|
||||
for i in items:
|
||||
result[i].append(i * i) # OK
|
||||
|
||||
|
||||
class Foo:
|
||||
def append(self, x):
|
||||
pass
|
||||
|
||||
|
||||
def f():
|
||||
items = [1, 2, 3, 4]
|
||||
result = Foo()
|
||||
for i in items:
|
||||
result.append(i) # OK
|
||||
|
||||
|
||||
def f():
|
||||
import sys
|
||||
|
||||
for path in ("foo", "bar"):
|
||||
sys.path.append(path) # OK
|
||||
|
||||
@@ -9,3 +9,8 @@ hidden = {"a": "!"}
|
||||
"%(a)s" % {'a': 1, u"b": "!"} # F504 ("b" not used)
|
||||
|
||||
'' % {'a''b' : ''} # F504 ("ab" not used)
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/4899
|
||||
"" % {
|
||||
'test1': '',
|
||||
'test2': '',
|
||||
|
||||
@@ -165,3 +165,9 @@ def f():
|
||||
x = 1
|
||||
|
||||
y = 2
|
||||
|
||||
|
||||
def f():
|
||||
(x) = foo()
|
||||
((x)) = foo()
|
||||
(x) = (y.z) = foo()
|
||||
|
||||
@@ -91,3 +91,7 @@ def f(x: Optional[int : float]) -> None:
|
||||
|
||||
def f(x: Optional[str, int : float]) -> None:
|
||||
...
|
||||
|
||||
|
||||
def f(x: Optional[int, float]) -> None:
|
||||
...
|
||||
|
||||
@@ -22,3 +22,4 @@ MyType = typing.NamedTuple("MyType", a=int, b=tuple[str, ...])
|
||||
# unfixable
|
||||
MyType = typing.NamedTuple("MyType", [("a", int)], [("b", str)])
|
||||
MyType = typing.NamedTuple("MyType", [("a", int)], b=str)
|
||||
MyType = typing.NamedTuple(typename="MyType", a=int, b=str)
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import subprocess
|
||||
import subprocess as somename
|
||||
from subprocess import run
|
||||
from subprocess import run as anothername
|
||||
|
||||
# Errors
|
||||
subprocess.run(["foo"], universal_newlines=True, check=True)
|
||||
somename.run(["foo"], universal_newlines=True)
|
||||
|
||||
subprocess.run(["foo"], universal_newlines=True, text=True)
|
||||
run(["foo"], universal_newlines=True, check=False)
|
||||
anothername(["foo"], universal_newlines=True)
|
||||
|
||||
# OK
|
||||
subprocess.run(["foo"], check=True)
|
||||
|
||||
@@ -35,6 +35,13 @@ if output:
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
output = subprocess.run(
|
||||
["foo"], stdout=subprocess.PIPE, capture_output=True, stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
output = subprocess.run(
|
||||
["foo"], stdout=subprocess.PIPE, capture_output=False, stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
# Examples that should NOT trigger the rule
|
||||
from foo import PIPE
|
||||
|
||||
@@ -72,3 +72,12 @@ def f():
|
||||
for x, y in z():
|
||||
yield x, y
|
||||
x = 1
|
||||
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7103
|
||||
def _serve_method(fn):
|
||||
for h in (
|
||||
TaggedText.from_file(args.input)
|
||||
.markup(highlight=args.region)
|
||||
):
|
||||
yield h
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# These should NOT change
|
||||
# OK
|
||||
def f():
|
||||
for x in z:
|
||||
yield
|
||||
|
||||
169
crates/ruff/resources/test/fixtures/refurb/FURB113.py
vendored
Normal file
169
crates/ruff/resources/test/fixtures/refurb/FURB113.py
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
from typing import List
|
||||
|
||||
|
||||
def func():
|
||||
pass
|
||||
|
||||
|
||||
nums = []
|
||||
nums2 = []
|
||||
nums3: list[int] = func()
|
||||
nums4: List[int]
|
||||
|
||||
|
||||
class C:
|
||||
def append(self, x):
|
||||
pass
|
||||
|
||||
|
||||
# Errors.
|
||||
|
||||
|
||||
# FURB113
|
||||
nums.append(1)
|
||||
nums.append(2)
|
||||
pass
|
||||
|
||||
|
||||
# FURB113
|
||||
nums3.append(1)
|
||||
nums3.append(2)
|
||||
pass
|
||||
|
||||
|
||||
# FURB113
|
||||
nums4.append(1)
|
||||
nums4.append(2)
|
||||
pass
|
||||
|
||||
|
||||
# FURB113
|
||||
nums.append(1)
|
||||
nums2.append(1)
|
||||
nums.append(2)
|
||||
nums.append(3)
|
||||
pass
|
||||
|
||||
|
||||
# FURB113
|
||||
nums.append(1)
|
||||
nums2.append(1)
|
||||
nums.append(2)
|
||||
# FURB113
|
||||
nums3.append(1)
|
||||
nums.append(3)
|
||||
# FURB113
|
||||
nums4.append(1)
|
||||
nums4.append(2)
|
||||
nums3.append(2)
|
||||
pass
|
||||
|
||||
# FURB113
|
||||
nums.append(1)
|
||||
nums.append(2)
|
||||
nums.append(3)
|
||||
|
||||
|
||||
if True:
|
||||
# FURB113
|
||||
nums.append(1)
|
||||
nums.append(2)
|
||||
|
||||
|
||||
if True:
|
||||
# FURB113
|
||||
nums.append(1)
|
||||
nums.append(2)
|
||||
pass
|
||||
|
||||
|
||||
if True:
|
||||
# FURB113
|
||||
nums.append(1)
|
||||
nums2.append(1)
|
||||
nums.append(2)
|
||||
nums.append(3)
|
||||
|
||||
|
||||
def yes_one(x: list[int]):
|
||||
# FURB113
|
||||
x.append(1)
|
||||
x.append(2)
|
||||
|
||||
|
||||
def yes_two(x: List[int]):
|
||||
# FURB113
|
||||
x.append(1)
|
||||
x.append(2)
|
||||
|
||||
|
||||
def yes_three(*, x: list[int]):
|
||||
# FURB113
|
||||
x.append(1)
|
||||
x.append(2)
|
||||
|
||||
|
||||
def yes_four(x: list[int], /):
|
||||
# FURB113
|
||||
x.append(1)
|
||||
x.append(2)
|
||||
|
||||
|
||||
def yes_five(x: list[int], y: list[int]):
|
||||
# FURB113
|
||||
x.append(1)
|
||||
x.append(2)
|
||||
y.append(1)
|
||||
x.append(3)
|
||||
|
||||
|
||||
def yes_six(x: list):
|
||||
# FURB113
|
||||
x.append(1)
|
||||
x.append(2)
|
||||
|
||||
|
||||
# Non-errors.
|
||||
|
||||
nums.append(1)
|
||||
pass
|
||||
nums.append(2)
|
||||
|
||||
|
||||
if True:
|
||||
nums.append(1)
|
||||
pass
|
||||
nums.append(2)
|
||||
|
||||
|
||||
nums.append(1)
|
||||
pass
|
||||
|
||||
|
||||
nums.append(1)
|
||||
nums2.append(2)
|
||||
|
||||
|
||||
nums.copy()
|
||||
nums.copy()
|
||||
|
||||
|
||||
c = C()
|
||||
c.append(1)
|
||||
c.append(2)
|
||||
|
||||
|
||||
def not_one(x):
|
||||
x.append(1)
|
||||
x.append(2)
|
||||
|
||||
|
||||
def not_two(x: C):
|
||||
x.append(1)
|
||||
x.append(2)
|
||||
|
||||
|
||||
# redefining a list variable with a new type shouldn't confuse ruff.
|
||||
nums2 = C()
|
||||
nums2.append(1)
|
||||
nums2.append(2)
|
||||
64
crates/ruff/resources/test/fixtures/refurb/FURB131.py
vendored
Normal file
64
crates/ruff/resources/test/fixtures/refurb/FURB131.py
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
from typing import Dict, List
|
||||
|
||||
names = {"key": "value"}
|
||||
nums = [1, 2, 3]
|
||||
x = 42
|
||||
y = "hello"
|
||||
|
||||
# these should match
|
||||
|
||||
# FURB131
|
||||
del nums[:]
|
||||
|
||||
|
||||
# FURB131
|
||||
del names[:]
|
||||
|
||||
|
||||
# FURB131
|
||||
del x, nums[:]
|
||||
|
||||
|
||||
# FURB131
|
||||
del y, names[:], x
|
||||
|
||||
|
||||
def yes_one(x: list[int]):
|
||||
# FURB131
|
||||
del x[:]
|
||||
|
||||
|
||||
def yes_two(x: dict[int, str]):
|
||||
# FURB131
|
||||
del x[:]
|
||||
|
||||
|
||||
def yes_three(x: List[int]):
|
||||
# FURB131
|
||||
del x[:]
|
||||
|
||||
|
||||
def yes_four(x: Dict[int, str]):
|
||||
# FURB131
|
||||
del x[:]
|
||||
|
||||
|
||||
# these should not
|
||||
|
||||
del names["key"]
|
||||
del nums[0]
|
||||
|
||||
x = 1
|
||||
del x
|
||||
|
||||
|
||||
del nums[1:2]
|
||||
|
||||
|
||||
del nums[:2]
|
||||
del nums[1:]
|
||||
del nums[::2]
|
||||
|
||||
|
||||
def no_one(param):
|
||||
del param[:]
|
||||
80
crates/ruff/resources/test/fixtures/refurb/FURB132.py
vendored
Normal file
80
crates/ruff/resources/test/fixtures/refurb/FURB132.py
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
from typing import Set
|
||||
from some.package.name import foo, bar
|
||||
|
||||
|
||||
s = set()
|
||||
s2 = {}
|
||||
s3: set[int] = foo()
|
||||
|
||||
# these should match
|
||||
|
||||
# FURB132
|
||||
if "x" in s:
|
||||
s.remove("x")
|
||||
|
||||
|
||||
# FURB132
|
||||
if "x" in s2:
|
||||
s2.remove("x")
|
||||
|
||||
|
||||
# FURB132
|
||||
if "x" in s3:
|
||||
s3.remove("x")
|
||||
|
||||
|
||||
var = "y"
|
||||
# FURB132
|
||||
if var in s:
|
||||
s.remove(var)
|
||||
|
||||
|
||||
if f"{var}:{var}" in s:
|
||||
s.remove(f"{var}:{var}")
|
||||
|
||||
|
||||
def identity(x):
|
||||
return x
|
||||
|
||||
|
||||
# TODO: FURB132
|
||||
if identity("x") in s2:
|
||||
s2.remove(identity("x"))
|
||||
|
||||
|
||||
# these should not
|
||||
|
||||
if "x" in s:
|
||||
s.remove("y")
|
||||
|
||||
s.discard("x")
|
||||
|
||||
s2 = set()
|
||||
|
||||
if "x" in s:
|
||||
s2.remove("x")
|
||||
|
||||
if "x" in s:
|
||||
s.remove("x")
|
||||
print("removed item")
|
||||
|
||||
if bar() in s:
|
||||
# bar() might have a side effect
|
||||
s.remove(bar())
|
||||
|
||||
if "x" in s:
|
||||
s.remove("x")
|
||||
else:
|
||||
print("not found")
|
||||
|
||||
class Container:
|
||||
def remove(self, item) -> None:
|
||||
return
|
||||
|
||||
def __contains__(self, other) -> bool:
|
||||
return True
|
||||
|
||||
c = Container()
|
||||
|
||||
if "x" in c:
|
||||
c.remove("x")
|
||||
@@ -12,3 +12,10 @@ sum([[1, 2, 3], [4, 5, 6]],
|
||||
# OK
|
||||
sum([x, y])
|
||||
sum([[1, 2, 3], [4, 5, 6]])
|
||||
|
||||
|
||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7059
|
||||
def func():
|
||||
import functools, operator
|
||||
|
||||
sum([x, y], [])
|
||||
|
||||
@@ -8,7 +8,6 @@ use libcst_native::{
|
||||
use ruff_python_ast::Stmt;
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::cst::helpers::compose_module_path;
|
||||
use crate::cst::matchers::match_statement;
|
||||
@@ -39,7 +38,7 @@ pub(crate) fn remove_imports<'a>(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
) -> Result<Option<String>> {
|
||||
let module_text = locator.slice(stmt.range());
|
||||
let module_text = locator.slice(stmt);
|
||||
let mut tree = match_statement(module_text)?;
|
||||
|
||||
let Statement::Simple(body) = &mut tree else {
|
||||
@@ -118,7 +117,7 @@ pub(crate) fn retain_imports(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
) -> Result<String> {
|
||||
let module_text = locator.slice(stmt.range());
|
||||
let module_text = locator.slice(stmt);
|
||||
let mut tree = match_statement(module_text)?;
|
||||
|
||||
let Statement::Simple(body) = &mut tree else {
|
||||
|
||||
@@ -4,17 +4,15 @@ use std::collections::BTreeSet;
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, IsolationLevel};
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, IsolationLevel, SourceMap};
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::autofix::source_map::SourceMap;
|
||||
use crate::linter::FixTable;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
|
||||
pub(crate) mod codemods;
|
||||
pub(crate) mod edits;
|
||||
pub(crate) mod snippet;
|
||||
pub(crate) mod source_map;
|
||||
|
||||
pub(crate) struct FixResult {
|
||||
/// The resulting source code, after applying all fixes.
|
||||
@@ -140,10 +138,9 @@ fn cmp_fix(rule1: Rule, rule2: Rule, fix1: &Fix, fix2: &Fix) -> std::cmp::Orderi
|
||||
mod tests {
|
||||
use ruff_text_size::{Ranged, TextSize};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix};
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, SourceMarker};
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::autofix::source_map::SourceMarker;
|
||||
use crate::autofix::{apply_fixes, FixResult};
|
||||
use crate::rules::pycodestyle::rules::MissingNewlineAtEndOfFile;
|
||||
|
||||
@@ -207,14 +204,8 @@ print("hello world")
|
||||
assert_eq!(
|
||||
source_map.markers(),
|
||||
&[
|
||||
SourceMarker {
|
||||
source: 10.into(),
|
||||
dest: 10.into(),
|
||||
},
|
||||
SourceMarker {
|
||||
source: 10.into(),
|
||||
dest: 21.into(),
|
||||
},
|
||||
SourceMarker::new(10.into(), 10.into(),),
|
||||
SourceMarker::new(10.into(), 21.into(),),
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -250,14 +241,8 @@ class A(Bar):
|
||||
assert_eq!(
|
||||
source_map.markers(),
|
||||
&[
|
||||
SourceMarker {
|
||||
source: 8.into(),
|
||||
dest: 8.into(),
|
||||
},
|
||||
SourceMarker {
|
||||
source: 14.into(),
|
||||
dest: 11.into(),
|
||||
},
|
||||
SourceMarker::new(8.into(), 8.into(),),
|
||||
SourceMarker::new(14.into(), 11.into(),),
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -289,14 +274,8 @@ class A:
|
||||
assert_eq!(
|
||||
source_map.markers(),
|
||||
&[
|
||||
SourceMarker {
|
||||
source: 7.into(),
|
||||
dest: 7.into()
|
||||
},
|
||||
SourceMarker {
|
||||
source: 15.into(),
|
||||
dest: 7.into()
|
||||
}
|
||||
SourceMarker::new(7.into(), 7.into()),
|
||||
SourceMarker::new(15.into(), 7.into()),
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -332,22 +311,10 @@ class A(object):
|
||||
assert_eq!(
|
||||
source_map.markers(),
|
||||
&[
|
||||
SourceMarker {
|
||||
source: 8.into(),
|
||||
dest: 8.into()
|
||||
},
|
||||
SourceMarker {
|
||||
source: 16.into(),
|
||||
dest: 8.into()
|
||||
},
|
||||
SourceMarker {
|
||||
source: 22.into(),
|
||||
dest: 14.into(),
|
||||
},
|
||||
SourceMarker {
|
||||
source: 30.into(),
|
||||
dest: 14.into(),
|
||||
}
|
||||
SourceMarker::new(8.into(), 8.into()),
|
||||
SourceMarker::new(16.into(), 8.into()),
|
||||
SourceMarker::new(22.into(), 14.into(),),
|
||||
SourceMarker::new(30.into(), 14.into(),),
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -382,14 +349,8 @@ class A:
|
||||
assert_eq!(
|
||||
source_map.markers(),
|
||||
&[
|
||||
SourceMarker {
|
||||
source: 7.into(),
|
||||
dest: 7.into(),
|
||||
},
|
||||
SourceMarker {
|
||||
source: 15.into(),
|
||||
dest: 7.into(),
|
||||
}
|
||||
SourceMarker::new(7.into(), 7.into(),),
|
||||
SourceMarker::new(15.into(), 7.into(),),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@ impl SourceCodeSnippet {
|
||||
Self(source_code)
|
||||
}
|
||||
|
||||
pub(crate) fn from_str(source_code: &str) -> Self {
|
||||
Self(source_code.to_string())
|
||||
}
|
||||
|
||||
/// Return the full snippet for user-facing display, or `None` if the snippet should be
|
||||
/// truncated.
|
||||
pub(crate) fn full_display(&self) -> Option<&str> {
|
||||
|
||||
@@ -163,9 +163,9 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
continue;
|
||||
};
|
||||
|
||||
let contents = checker.locator.slice(expr.range());
|
||||
let contents = checker.locator().slice(expr);
|
||||
|
||||
let indentation = checker.locator.slice(TextRange::new(
|
||||
let indentation = checker.locator().slice(TextRange::new(
|
||||
checker.locator.line_start(expr.start()),
|
||||
expr.start(),
|
||||
));
|
||||
|
||||
@@ -1039,12 +1039,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::PrintfStringFormatting) {
|
||||
pyupgrade::rules::printf_string_formatting(
|
||||
checker,
|
||||
expr,
|
||||
right,
|
||||
checker.locator,
|
||||
);
|
||||
pyupgrade::rules::printf_string_formatting(checker, expr, right);
|
||||
}
|
||||
if checker.enabled(Rule::BadStringFormatCharacter) {
|
||||
pylint::rules::bad_string_format_character::percent(checker, expr);
|
||||
@@ -1258,14 +1253,10 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
range: _,
|
||||
}) => {
|
||||
if checker.enabled(Rule::IfExprWithTrueFalse) {
|
||||
flake8_simplify::rules::explicit_true_false_in_ifexpr(
|
||||
checker, expr, test, body, orelse,
|
||||
);
|
||||
flake8_simplify::rules::if_expr_with_true_false(checker, expr, test, body, orelse);
|
||||
}
|
||||
if checker.enabled(Rule::IfExprWithFalseTrue) {
|
||||
flake8_simplify::rules::explicit_false_true_in_ifexpr(
|
||||
checker, expr, test, body, orelse,
|
||||
);
|
||||
flake8_simplify::rules::if_expr_with_false_true(checker, expr, test, body, orelse);
|
||||
}
|
||||
if checker.enabled(Rule::IfExprWithTwistedArms) {
|
||||
flake8_simplify::rules::twisted_arms_in_ifexpr(checker, expr, test, body, orelse);
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::rules::{
|
||||
flake8_django, flake8_errmsg, flake8_import_conventions, flake8_pie, flake8_pyi,
|
||||
flake8_pytest_style, flake8_raise, flake8_return, flake8_simplify, flake8_slots,
|
||||
flake8_tidy_imports, flake8_type_checking, mccabe, pandas_vet, pep8_naming, perflint,
|
||||
pycodestyle, pyflakes, pygrep_hooks, pylint, pyupgrade, ruff, tryceratops,
|
||||
pycodestyle, pyflakes, pygrep_hooks, pylint, pyupgrade, refurb, ruff, tryceratops,
|
||||
};
|
||||
use crate::settings::types::PythonVersion;
|
||||
|
||||
@@ -1056,6 +1056,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::CheckAndRemoveFromSet) {
|
||||
refurb::rules::check_and_remove_from_set(checker, if_);
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.any_enabled(&[
|
||||
Rule::UnrecognizedVersionInfoCheck,
|
||||
@@ -1459,7 +1462,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::Delete(ast::StmtDelete { targets, range: _ }) => {
|
||||
Stmt::Delete(delete @ ast::StmtDelete { targets, range: _ }) => {
|
||||
if checker.enabled(Rule::GlobalStatement) {
|
||||
for target in targets {
|
||||
if let Expr::Name(ast::ExprName { id, .. }) = target {
|
||||
@@ -1467,6 +1470,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if checker.enabled(Rule::DeleteFullSlice) {
|
||||
refurb::rules::delete_full_slice(checker, delete);
|
||||
}
|
||||
}
|
||||
Stmt::Expr(ast::StmtExpr { value, range: _ }) => {
|
||||
if checker.enabled(Rule::UselessComparison) {
|
||||
@@ -1484,6 +1490,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::AsyncioDanglingTask) {
|
||||
ruff::rules::asyncio_dangling_task(checker, value);
|
||||
}
|
||||
if checker.enabled(Rule::RepeatedAppend) {
|
||||
refurb::rules::repeated_append(checker, stmt);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ pub(crate) fn check_imports(
|
||||
stylist: &Stylist,
|
||||
path: &Path,
|
||||
package: Option<&Path>,
|
||||
source_kind: Option<&SourceKind>,
|
||||
source_kind: &SourceKind,
|
||||
source_type: PySourceType,
|
||||
) -> (Vec<Diagnostic>, Option<ImportMap>) {
|
||||
// Extract all import blocks from the AST.
|
||||
|
||||
@@ -110,7 +110,7 @@ pub(crate) fn check_noqa(
|
||||
Diagnostic::new(UnusedNOQA { codes: None }, directive.range());
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
diagnostic
|
||||
.set_fix(Fix::automatic(delete_noqa(directive.range(), locator)));
|
||||
.set_fix(Fix::suggested(delete_noqa(directive.range(), locator)));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
@@ -174,12 +174,12 @@ pub(crate) fn check_noqa(
|
||||
);
|
||||
if settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
if valid_codes.is_empty() {
|
||||
diagnostic.set_fix(Fix::automatic(delete_noqa(
|
||||
diagnostic.set_fix(Fix::suggested(delete_noqa(
|
||||
directive.range(),
|
||||
locator,
|
||||
)));
|
||||
} else {
|
||||
diagnostic.set_fix(Fix::automatic(Edit::range_replacement(
|
||||
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
||||
format!("# noqa: {}", valid_codes.join(", ")),
|
||||
directive.range(),
|
||||
)));
|
||||
|
||||
@@ -865,6 +865,11 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Flake8Slots, "001") => (RuleGroup::Unspecified, rules::flake8_slots::rules::NoSlotsInTupleSubclass),
|
||||
(Flake8Slots, "002") => (RuleGroup::Unspecified, rules::flake8_slots::rules::NoSlotsInNamedtupleSubclass),
|
||||
|
||||
// refurb
|
||||
(Refurb, "113") => (RuleGroup::Nursery, rules::refurb::rules::RepeatedAppend),
|
||||
(Refurb, "131") => (RuleGroup::Nursery, rules::refurb::rules::DeleteFullSlice),
|
||||
(Refurb, "132") => (RuleGroup::Nursery, rules::refurb::rules::CheckAndRemoveFromSet),
|
||||
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ impl StatementVisitor<'_> for StringLinesVisitor<'_> {
|
||||
}) = value.as_ref()
|
||||
{
|
||||
for line in UniversalNewlineIterator::with_offset(
|
||||
self.locator.slice(value.range()),
|
||||
self.locator.slice(value.as_ref()),
|
||||
value.start(),
|
||||
) {
|
||||
self.string_lines.push(line.start());
|
||||
|
||||
@@ -171,10 +171,8 @@ impl<'a> Importer<'a> {
|
||||
at: TextSize,
|
||||
semantic: &SemanticModel,
|
||||
) -> Result<(Edit, String), ResolutionError> {
|
||||
match self.get_symbol(symbol, at, semantic) {
|
||||
Some(result) => result,
|
||||
None => self.import_symbol(symbol, at, semantic),
|
||||
}
|
||||
self.get_symbol(symbol, at, semantic)?
|
||||
.map_or_else(|| self.import_symbol(symbol, at, semantic), Ok)
|
||||
}
|
||||
|
||||
/// Return an [`Edit`] to reference an existing symbol, if it's present in the given [`SemanticModel`].
|
||||
@@ -183,9 +181,13 @@ impl<'a> Importer<'a> {
|
||||
symbol: &ImportRequest,
|
||||
at: TextSize,
|
||||
semantic: &SemanticModel,
|
||||
) -> Option<Result<(Edit, String), ResolutionError>> {
|
||||
) -> Result<Option<(Edit, String)>, ResolutionError> {
|
||||
// If the symbol is already available in the current scope, use it.
|
||||
let imported_name = semantic.resolve_qualified_import_name(symbol.module, symbol.member)?;
|
||||
let Some(imported_name) =
|
||||
semantic.resolve_qualified_import_name(symbol.module, symbol.member)
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// If the symbol source (i.e., the import statement) comes after the current location,
|
||||
// abort. For example, we could be generating an edit within a function, and the import
|
||||
@@ -195,13 +197,13 @@ impl<'a> Importer<'a> {
|
||||
// unclear whether should add an import statement at the start of the file, since it could
|
||||
// be shadowed between the import and the current location.
|
||||
if imported_name.start() > at {
|
||||
return Some(Err(ResolutionError::ImportAfterUsage));
|
||||
return Err(ResolutionError::ImportAfterUsage);
|
||||
}
|
||||
|
||||
// If the symbol source (i.e., the import statement) is in a typing-only context, but we're
|
||||
// in a runtime context, abort.
|
||||
if imported_name.context().is_typing() && semantic.execution_context().is_runtime() {
|
||||
return Some(Err(ResolutionError::IncompatibleContext));
|
||||
return Err(ResolutionError::IncompatibleContext);
|
||||
}
|
||||
|
||||
// We also add a no-op edit to force conflicts with any other fixes that might try to
|
||||
@@ -224,7 +226,7 @@ impl<'a> Importer<'a> {
|
||||
self.locator.slice(imported_name.range()).to_string(),
|
||||
imported_name.range(),
|
||||
);
|
||||
Some(Ok((import_edit, imported_name.into_name())))
|
||||
Ok(Some((import_edit, imported_name.into_name())))
|
||||
}
|
||||
|
||||
/// Generate an [`Edit`] to reference the given symbol. Returns the [`Edit`] necessary to make
|
||||
@@ -319,7 +321,7 @@ impl<'a> Importer<'a> {
|
||||
|
||||
/// Add the given member to an existing `Stmt::ImportFrom` statement.
|
||||
fn add_member(&self, stmt: &Stmt, member: &str) -> Result<Edit> {
|
||||
let mut statement = match_statement(self.locator.slice(stmt.range()))?;
|
||||
let mut statement = match_statement(self.locator.slice(stmt))?;
|
||||
let import_from = match_import_from(&mut statement)?;
|
||||
let aliases = match_aliases(import_from)?;
|
||||
aliases.push(ImportAlias {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//! [Ruff]: https://github.com/astral-sh/ruff
|
||||
|
||||
pub use rule_selector::RuleSelector;
|
||||
pub use rules::pycodestyle::rules::IOError;
|
||||
pub use rules::pycodestyle::rules::{IOError, SyntaxError};
|
||||
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
@@ -20,7 +20,6 @@ mod doc_lines;
|
||||
mod docstrings;
|
||||
pub mod fs;
|
||||
mod importer;
|
||||
pub mod jupyter;
|
||||
mod lex;
|
||||
pub mod line_width;
|
||||
pub mod linter;
|
||||
|
||||
@@ -6,8 +6,6 @@ use anyhow::{anyhow, Result};
|
||||
use colored::Colorize;
|
||||
use itertools::Itertools;
|
||||
use log::error;
|
||||
use ruff_python_parser::lexer::LexResult;
|
||||
use ruff_python_parser::{AsMode, ParseError};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
@@ -15,7 +13,8 @@ use ruff_python_ast::imports::ImportMap;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_codegen::Stylist;
|
||||
use ruff_python_index::Indexer;
|
||||
|
||||
use ruff_python_parser::lexer::LexResult;
|
||||
use ruff_python_parser::{AsMode, ParseError};
|
||||
use ruff_source_file::{Locator, SourceFileBuilder};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
@@ -82,7 +81,7 @@ pub fn check_path(
|
||||
directives: &Directives,
|
||||
settings: &Settings,
|
||||
noqa: flags::Noqa,
|
||||
source_kind: Option<&SourceKind>,
|
||||
source_kind: &SourceKind,
|
||||
source_type: PySourceType,
|
||||
) -> LinterResult<(Vec<Diagnostic>, Option<ImportMap>)> {
|
||||
// Aggregate all diagnostics.
|
||||
@@ -268,17 +267,20 @@ pub fn check_path(
|
||||
const MAX_ITERATIONS: usize = 100;
|
||||
|
||||
/// Add any missing `# noqa` pragmas to the source code at the given `Path`.
|
||||
pub fn add_noqa_to_path(path: &Path, package: Option<&Path>, settings: &Settings) -> Result<usize> {
|
||||
let source_type = PySourceType::from(path);
|
||||
|
||||
// Read the file from disk.
|
||||
let contents = std::fs::read_to_string(path)?;
|
||||
pub fn add_noqa_to_path(
|
||||
path: &Path,
|
||||
package: Option<&Path>,
|
||||
source_kind: &SourceKind,
|
||||
source_type: PySourceType,
|
||||
settings: &Settings,
|
||||
) -> Result<usize> {
|
||||
let contents = source_kind.source_code();
|
||||
|
||||
// Tokenize once.
|
||||
let tokens: Vec<LexResult> = ruff_python_parser::tokenize(&contents, source_type.as_mode());
|
||||
let tokens: Vec<LexResult> = ruff_python_parser::tokenize(contents, source_type.as_mode());
|
||||
|
||||
// Map row and column locations to byte slices (lazily).
|
||||
let locator = Locator::new(&contents);
|
||||
let locator = Locator::new(contents);
|
||||
|
||||
// Detect the current code style (lazily).
|
||||
let stylist = Stylist::from_tokens(&tokens, &locator);
|
||||
@@ -308,21 +310,20 @@ pub fn add_noqa_to_path(path: &Path, package: Option<&Path>, settings: &Settings
|
||||
&directives,
|
||||
settings,
|
||||
flags::Noqa::Disabled,
|
||||
None,
|
||||
source_kind,
|
||||
source_type,
|
||||
);
|
||||
|
||||
// Log any parse errors.
|
||||
if let Some(err) = error {
|
||||
// TODO(dhruvmanila): This should use `SourceKind`, update when
|
||||
// `--add-noqa` is supported for Jupyter notebooks.
|
||||
error!(
|
||||
"{}",
|
||||
DisplayParseError::new(err, locator.to_source_code(), None)
|
||||
DisplayParseError::new(err, locator.to_source_code(), source_kind)
|
||||
);
|
||||
}
|
||||
|
||||
// Add any missing `# noqa` pragmas.
|
||||
// TODO(dhruvmanila): Add support for Jupyter Notebooks
|
||||
add_noqa(
|
||||
path,
|
||||
&diagnostics.0,
|
||||
@@ -375,7 +376,7 @@ pub fn lint_only(
|
||||
&directives,
|
||||
settings,
|
||||
noqa,
|
||||
Some(source_kind),
|
||||
source_kind,
|
||||
source_type,
|
||||
);
|
||||
|
||||
@@ -469,7 +470,7 @@ pub fn lint_fix<'a>(
|
||||
&directives,
|
||||
settings,
|
||||
noqa,
|
||||
Some(source_kind),
|
||||
source_kind,
|
||||
source_type,
|
||||
);
|
||||
|
||||
@@ -606,3 +607,133 @@ This indicates a bug in `{}`. If you could open an issue at:
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use test_case::test_case;
|
||||
|
||||
use ruff_notebook::{Notebook, NotebookError};
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::source_kind::SourceKind;
|
||||
use crate::test::{test_contents, test_notebook_path, TestedNotebook};
|
||||
use crate::{assert_messages, settings};
|
||||
|
||||
/// Construct a path to a Jupyter notebook in the `resources/test/fixtures/jupyter` directory.
|
||||
fn notebook_path(path: impl AsRef<Path>) -> std::path::PathBuf {
|
||||
Path::new("../ruff_notebook/resources/test/fixtures/jupyter").join(path)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_import_sorting() -> Result<(), NotebookError> {
|
||||
let actual = notebook_path("isort.ipynb");
|
||||
let expected = notebook_path("isort_expected.ipynb");
|
||||
let TestedNotebook {
|
||||
messages,
|
||||
source_notebook,
|
||||
..
|
||||
} = test_notebook_path(
|
||||
&actual,
|
||||
expected,
|
||||
&settings::Settings::for_rule(Rule::UnsortedImports),
|
||||
)?;
|
||||
assert_messages!(messages, actual, source_notebook);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ipy_escape_command() -> Result<(), NotebookError> {
|
||||
let actual = notebook_path("ipy_escape_command.ipynb");
|
||||
let expected = notebook_path("ipy_escape_command_expected.ipynb");
|
||||
let TestedNotebook {
|
||||
messages,
|
||||
source_notebook,
|
||||
..
|
||||
} = test_notebook_path(
|
||||
&actual,
|
||||
expected,
|
||||
&settings::Settings::for_rule(Rule::UnusedImport),
|
||||
)?;
|
||||
assert_messages!(messages, actual, source_notebook);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unused_variable() -> Result<(), NotebookError> {
|
||||
let actual = notebook_path("unused_variable.ipynb");
|
||||
let expected = notebook_path("unused_variable_expected.ipynb");
|
||||
let TestedNotebook {
|
||||
messages,
|
||||
source_notebook,
|
||||
..
|
||||
} = test_notebook_path(
|
||||
&actual,
|
||||
expected,
|
||||
&settings::Settings::for_rule(Rule::UnusedVariable),
|
||||
)?;
|
||||
assert_messages!(messages, actual, source_notebook);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_consistency() -> Result<()> {
|
||||
let actual_path = notebook_path("before_fix.ipynb");
|
||||
let expected_path = notebook_path("after_fix.ipynb");
|
||||
|
||||
let TestedNotebook {
|
||||
linted_notebook: fixed_notebook,
|
||||
..
|
||||
} = test_notebook_path(
|
||||
actual_path,
|
||||
&expected_path,
|
||||
&settings::Settings::for_rule(Rule::UnusedImport),
|
||||
)?;
|
||||
let mut writer = Vec::new();
|
||||
fixed_notebook.write(&mut writer)?;
|
||||
let actual = String::from_utf8(writer)?;
|
||||
let expected = std::fs::read_to_string(expected_path)?;
|
||||
assert_eq!(actual, expected);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Path::new("before_fix.ipynb"), true; "trailing_newline")]
|
||||
#[test_case(Path::new("no_trailing_newline.ipynb"), false; "no_trailing_newline")]
|
||||
fn test_trailing_newline(path: &Path, trailing_newline: bool) -> Result<()> {
|
||||
let notebook = Notebook::from_path(¬ebook_path(path))?;
|
||||
assert_eq!(notebook.trailing_newline(), trailing_newline);
|
||||
|
||||
let mut writer = Vec::new();
|
||||
notebook.write(&mut writer)?;
|
||||
let string = String::from_utf8(writer)?;
|
||||
assert_eq!(string.ends_with('\n'), trailing_newline);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Version <4.5, don't emit cell ids
|
||||
#[test_case(Path::new("no_cell_id.ipynb"), false; "no_cell_id")]
|
||||
// Version 4.5, cell ids are missing and need to be added
|
||||
#[test_case(Path::new("add_missing_cell_id.ipynb"), true; "add_missing_cell_id")]
|
||||
fn test_cell_id(path: &Path, has_id: bool) -> Result<()> {
|
||||
let source_notebook = Notebook::from_path(¬ebook_path(path))?;
|
||||
let source_kind = SourceKind::IpyNotebook(source_notebook);
|
||||
let (_, transformed) = test_contents(
|
||||
&source_kind,
|
||||
path,
|
||||
&settings::Settings::for_rule(Rule::UnusedImport),
|
||||
);
|
||||
let linted_notebook = transformed.into_owned().expect_ipy_notebook();
|
||||
let mut writer = Vec::new();
|
||||
linted_notebook.write(&mut writer)?;
|
||||
let actual = String::from_utf8(writer)?;
|
||||
if has_id {
|
||||
assert!(actual.contains(r#""id": ""#));
|
||||
} else {
|
||||
assert!(!actual.contains(r#""id":"#));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ use ruff_python_parser::{ParseError, ParseErrorType};
|
||||
use ruff_source_file::{OneIndexed, SourceCode, SourceLocation};
|
||||
|
||||
use crate::fs;
|
||||
use crate::jupyter::Notebook;
|
||||
use crate::source_kind::SourceKind;
|
||||
use ruff_notebook::Notebook;
|
||||
|
||||
pub static WARNINGS: Lazy<Mutex<Vec<&'static str>>> = Lazy::new(Mutex::default);
|
||||
|
||||
@@ -139,14 +139,14 @@ pub fn set_up_logging(level: &LogLevel) -> Result<()> {
|
||||
pub struct DisplayParseError<'a> {
|
||||
error: ParseError,
|
||||
source_code: SourceCode<'a, 'a>,
|
||||
source_kind: Option<&'a SourceKind>,
|
||||
source_kind: &'a SourceKind,
|
||||
}
|
||||
|
||||
impl<'a> DisplayParseError<'a> {
|
||||
pub fn new(
|
||||
error: ParseError,
|
||||
source_code: SourceCode<'a, 'a>,
|
||||
source_kind: Option<&'a SourceKind>,
|
||||
source_kind: &'a SourceKind,
|
||||
) -> Self {
|
||||
Self {
|
||||
error,
|
||||
@@ -171,32 +171,29 @@ impl Display for DisplayParseError<'_> {
|
||||
// If we're working on a Jupyter notebook, translate the positions
|
||||
// with respect to the cell and row in the cell. This is the same
|
||||
// format as the `TextEmitter`.
|
||||
let error_location = if let Some(jupyter_index) = self
|
||||
.source_kind
|
||||
.and_then(SourceKind::notebook)
|
||||
.map(Notebook::index)
|
||||
{
|
||||
write!(
|
||||
f,
|
||||
"cell {cell}{colon}",
|
||||
cell = jupyter_index
|
||||
.cell(source_location.row.get())
|
||||
.unwrap_or_default(),
|
||||
colon = ":".cyan(),
|
||||
)?;
|
||||
let error_location =
|
||||
if let Some(jupyter_index) = self.source_kind.as_ipy_notebook().map(Notebook::index) {
|
||||
write!(
|
||||
f,
|
||||
"cell {cell}{colon}",
|
||||
cell = jupyter_index
|
||||
.cell(source_location.row.get())
|
||||
.unwrap_or_default(),
|
||||
colon = ":".cyan(),
|
||||
)?;
|
||||
|
||||
SourceLocation {
|
||||
row: OneIndexed::new(
|
||||
jupyter_index
|
||||
.cell_row(source_location.row.get())
|
||||
.unwrap_or(1) as usize,
|
||||
)
|
||||
.unwrap(),
|
||||
column: source_location.column,
|
||||
}
|
||||
} else {
|
||||
source_location
|
||||
};
|
||||
SourceLocation {
|
||||
row: OneIndexed::new(
|
||||
jupyter_index
|
||||
.cell_row(source_location.row.get())
|
||||
.unwrap_or(1) as usize,
|
||||
)
|
||||
.unwrap(),
|
||||
column: source_location.column,
|
||||
}
|
||||
} else {
|
||||
source_location
|
||||
};
|
||||
|
||||
write!(
|
||||
f,
|
||||
|
||||
@@ -4,10 +4,10 @@ use std::num::NonZeroUsize;
|
||||
|
||||
use colored::Colorize;
|
||||
|
||||
use ruff_notebook::{Notebook, NotebookIndex};
|
||||
use ruff_source_file::OneIndexed;
|
||||
|
||||
use crate::fs::relativize_path;
|
||||
use crate::jupyter::{Notebook, NotebookIndex};
|
||||
use crate::message::diff::calculate_print_width;
|
||||
use crate::message::text::{MessageCodeFrame, RuleCodeAndBody};
|
||||
use crate::message::{
|
||||
|
||||
@@ -14,12 +14,11 @@ pub use json_lines::JsonLinesEmitter;
|
||||
pub use junit::JunitEmitter;
|
||||
pub use pylint::PylintEmitter;
|
||||
use ruff_diagnostics::{Diagnostic, DiagnosticKind, Fix};
|
||||
use ruff_notebook::Notebook;
|
||||
use ruff_source_file::{SourceFile, SourceLocation};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
pub use text::TextEmitter;
|
||||
|
||||
use crate::jupyter::Notebook;
|
||||
|
||||
mod azure;
|
||||
mod diff;
|
||||
mod github;
|
||||
|
||||
@@ -7,11 +7,11 @@ use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, Sou
|
||||
use bitflags::bitflags;
|
||||
use colored::Colorize;
|
||||
|
||||
use ruff_notebook::{Notebook, NotebookIndex};
|
||||
use ruff_source_file::{OneIndexed, SourceLocation};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::fs::relativize_path;
|
||||
use crate::jupyter::{Notebook, NotebookIndex};
|
||||
use crate::line_width::{LineWidthBuilder, TabSize};
|
||||
use crate::message::diff::Diff;
|
||||
use crate::message::{Emitter, EmitterContext, Message};
|
||||
|
||||
@@ -196,6 +196,9 @@ pub enum Linter {
|
||||
/// [Perflint](https://pypi.org/project/perflint/)
|
||||
#[prefix = "PERF"]
|
||||
Perflint,
|
||||
/// [refurb](https://pypi.org/project/refurb/)
|
||||
#[prefix = "FURB"]
|
||||
Refurb,
|
||||
/// Ruff-specific rules
|
||||
#[prefix = "RUF"]
|
||||
Ruff,
|
||||
|
||||
@@ -23,6 +23,10 @@ use ruff_text_size::Ranged;
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// if not x > 0:
|
||||
/// raise ValueError("Expected positive value.")
|
||||
///
|
||||
/// # or even better:
|
||||
/// if x <= 0:
|
||||
/// raise ValueError("Expected positive value.")
|
||||
/// ```
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
||||
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
||||
use ruff_python_stdlib::identifiers::{is_identifier, is_mangled_private};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
@@ -81,7 +80,17 @@ pub(crate) fn getattr_with_constant(
|
||||
let mut diagnostic = Diagnostic::new(GetAttrWithConstant, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
||||
format!("{}.{}", checker.locator().slice(obj.range()), value),
|
||||
if matches!(
|
||||
obj,
|
||||
Expr::Name(_) | Expr::Attribute(_) | Expr::Subscript(_) | Expr::Call(_)
|
||||
) {
|
||||
format!("{}.{}", checker.locator().slice(obj), value)
|
||||
} else {
|
||||
// Defensively parenthesize any other expressions. For example, attribute accesses
|
||||
// on `int` literals must be parenthesized, e.g., `getattr(1, "real")` becomes
|
||||
// `(1).real`. The same is true for named expressions and others.
|
||||
format!("({}).{}", checker.locator().slice(obj), value)
|
||||
},
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -175,8 +175,15 @@ fn move_initialization(
|
||||
return None;
|
||||
}
|
||||
Edit::insertion(content, locator.line_start(statement.start()))
|
||||
} else if statement.end() == locator.text_len() {
|
||||
// If the statement is at the end of the file, without a trailing newline, insert
|
||||
// _after_ it with an extra newline.
|
||||
Edit::insertion(
|
||||
format!("{}{}", stylist.line_ending().as_str(), content),
|
||||
locator.full_line_end(statement.end()),
|
||||
)
|
||||
} else {
|
||||
// If the docstring is the only statement, insert _before_ it.
|
||||
// If the docstring is the only statement, insert _after_ it.
|
||||
Edit::insertion(content, locator.full_line_end(statement.end()))
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -2,6 +2,7 @@ use ruff_python_ast::{self as ast, ExceptHandler, Expr};
|
||||
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::map_starred;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -41,11 +42,7 @@ pub struct RedundantTupleInExceptionHandler {
|
||||
impl AlwaysAutofixableViolation for RedundantTupleInExceptionHandler {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let RedundantTupleInExceptionHandler { name } = self;
|
||||
format!(
|
||||
"A length-one tuple literal is redundant. Write `except {name}` instead of `except \
|
||||
({name},)`."
|
||||
)
|
||||
format!("A length-one tuple literal is redundant in exception handlers")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
@@ -70,9 +67,10 @@ pub(crate) fn redundant_tuple_in_exception_handler(
|
||||
let Expr::Tuple(ast::ExprTuple { elts, .. }) = type_.as_ref() else {
|
||||
continue;
|
||||
};
|
||||
let [elt] = &elts[..] else {
|
||||
let [elt] = elts.as_slice() else {
|
||||
continue;
|
||||
};
|
||||
let elt = map_starred(elt);
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
RedundantTupleInExceptionHandler {
|
||||
name: checker.generator().expr(elt),
|
||||
|
||||
@@ -84,7 +84,7 @@ pub(crate) fn unreliable_callable_check(
|
||||
if id == "hasattr" {
|
||||
if checker.semantic().is_builtin("callable") {
|
||||
diagnostic.set_fix(Fix::automatic(Edit::range_replacement(
|
||||
format!("callable({})", checker.locator().slice(obj.range())),
|
||||
format!("callable({})", checker.locator().slice(obj)),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -476,4 +476,23 @@ B006_B008.py:308:52: B006 Do not use mutable data structures for argument defaul
|
||||
|
|
||||
= help: Replace with `None`; initialize within function
|
||||
|
||||
B006_B008.py:313:52: B006 [*] Do not use mutable data structures for argument defaults
|
||||
|
|
||||
313 | def single_line_func_wrong(value: dict[str, str] = {}):
|
||||
| ^^ B006
|
||||
314 | """Docstring without newline"""
|
||||
|
|
||||
= help: Replace with `None`; initialize within function
|
||||
|
||||
ℹ Possible fix
|
||||
310 310 | """Docstring"""
|
||||
311 311 |
|
||||
312 312 |
|
||||
313 |-def single_line_func_wrong(value: dict[str, str] = {}):
|
||||
314 |- """Docstring without newline"""
|
||||
313 |+def single_line_func_wrong(value: dict[str, str] = None):
|
||||
314 |+ """Docstring without newline"""
|
||||
315 |+ if value is None:
|
||||
316 |+ value = {}
|
||||
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ B009_B010.py:24:15: B009 [*] Do not call `getattr` with a constant attribute val
|
||||
24 |+_ = lambda x: x.bar
|
||||
25 25 | if getattr(x, "bar"):
|
||||
26 26 | pass
|
||||
27 27 |
|
||||
27 27 | getattr(1, "real")
|
||||
|
||||
B009_B010.py:25:4: B009 [*] Do not call `getattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
@@ -133,6 +133,7 @@ B009_B010.py:25:4: B009 [*] Do not call `getattr` with a constant attribute valu
|
||||
25 | if getattr(x, "bar"):
|
||||
| ^^^^^^^^^^^^^^^^^ B009
|
||||
26 | pass
|
||||
27 | getattr(1, "real")
|
||||
|
|
||||
= help: Replace `getattr` with attribute access
|
||||
|
||||
@@ -143,7 +144,176 @@ B009_B010.py:25:4: B009 [*] Do not call `getattr` with a constant attribute valu
|
||||
25 |-if getattr(x, "bar"):
|
||||
25 |+if x.bar:
|
||||
26 26 | pass
|
||||
27 27 |
|
||||
28 28 | # Valid setattr usage
|
||||
27 27 | getattr(1, "real")
|
||||
28 28 | getattr(1., "real")
|
||||
|
||||
B009_B010.py:27:1: B009 [*] Do not call `getattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
25 | if getattr(x, "bar"):
|
||||
26 | pass
|
||||
27 | getattr(1, "real")
|
||||
| ^^^^^^^^^^^^^^^^^^ B009
|
||||
28 | getattr(1., "real")
|
||||
29 | getattr(1.0, "real")
|
||||
|
|
||||
= help: Replace `getattr` with attribute access
|
||||
|
||||
ℹ Suggested fix
|
||||
24 24 | _ = lambda x: getattr(x, "bar")
|
||||
25 25 | if getattr(x, "bar"):
|
||||
26 26 | pass
|
||||
27 |-getattr(1, "real")
|
||||
27 |+(1).real
|
||||
28 28 | getattr(1., "real")
|
||||
29 29 | getattr(1.0, "real")
|
||||
30 30 | getattr(1j, "real")
|
||||
|
||||
B009_B010.py:28:1: B009 [*] Do not call `getattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
26 | pass
|
||||
27 | getattr(1, "real")
|
||||
28 | getattr(1., "real")
|
||||
| ^^^^^^^^^^^^^^^^^^^ B009
|
||||
29 | getattr(1.0, "real")
|
||||
30 | getattr(1j, "real")
|
||||
|
|
||||
= help: Replace `getattr` with attribute access
|
||||
|
||||
ℹ Suggested fix
|
||||
25 25 | if getattr(x, "bar"):
|
||||
26 26 | pass
|
||||
27 27 | getattr(1, "real")
|
||||
28 |-getattr(1., "real")
|
||||
28 |+(1.).real
|
||||
29 29 | getattr(1.0, "real")
|
||||
30 30 | getattr(1j, "real")
|
||||
31 31 | getattr(True, "real")
|
||||
|
||||
B009_B010.py:29:1: B009 [*] Do not call `getattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
27 | getattr(1, "real")
|
||||
28 | getattr(1., "real")
|
||||
29 | getattr(1.0, "real")
|
||||
| ^^^^^^^^^^^^^^^^^^^^ B009
|
||||
30 | getattr(1j, "real")
|
||||
31 | getattr(True, "real")
|
||||
|
|
||||
= help: Replace `getattr` with attribute access
|
||||
|
||||
ℹ Suggested fix
|
||||
26 26 | pass
|
||||
27 27 | getattr(1, "real")
|
||||
28 28 | getattr(1., "real")
|
||||
29 |-getattr(1.0, "real")
|
||||
29 |+(1.0).real
|
||||
30 30 | getattr(1j, "real")
|
||||
31 31 | getattr(True, "real")
|
||||
32 32 | getattr(x := 1, "real")
|
||||
|
||||
B009_B010.py:30:1: B009 [*] Do not call `getattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
28 | getattr(1., "real")
|
||||
29 | getattr(1.0, "real")
|
||||
30 | getattr(1j, "real")
|
||||
| ^^^^^^^^^^^^^^^^^^^ B009
|
||||
31 | getattr(True, "real")
|
||||
32 | getattr(x := 1, "real")
|
||||
|
|
||||
= help: Replace `getattr` with attribute access
|
||||
|
||||
ℹ Suggested fix
|
||||
27 27 | getattr(1, "real")
|
||||
28 28 | getattr(1., "real")
|
||||
29 29 | getattr(1.0, "real")
|
||||
30 |-getattr(1j, "real")
|
||||
30 |+(1j).real
|
||||
31 31 | getattr(True, "real")
|
||||
32 32 | getattr(x := 1, "real")
|
||||
33 33 | getattr(x + y, "real")
|
||||
|
||||
B009_B010.py:31:1: B009 [*] Do not call `getattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
29 | getattr(1.0, "real")
|
||||
30 | getattr(1j, "real")
|
||||
31 | getattr(True, "real")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ B009
|
||||
32 | getattr(x := 1, "real")
|
||||
33 | getattr(x + y, "real")
|
||||
|
|
||||
= help: Replace `getattr` with attribute access
|
||||
|
||||
ℹ Suggested fix
|
||||
28 28 | getattr(1., "real")
|
||||
29 29 | getattr(1.0, "real")
|
||||
30 30 | getattr(1j, "real")
|
||||
31 |-getattr(True, "real")
|
||||
31 |+(True).real
|
||||
32 32 | getattr(x := 1, "real")
|
||||
33 33 | getattr(x + y, "real")
|
||||
34 34 | getattr("foo"
|
||||
|
||||
B009_B010.py:32:1: B009 [*] Do not call `getattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
30 | getattr(1j, "real")
|
||||
31 | getattr(True, "real")
|
||||
32 | getattr(x := 1, "real")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ B009
|
||||
33 | getattr(x + y, "real")
|
||||
34 | getattr("foo"
|
||||
|
|
||||
= help: Replace `getattr` with attribute access
|
||||
|
||||
ℹ Suggested fix
|
||||
29 29 | getattr(1.0, "real")
|
||||
30 30 | getattr(1j, "real")
|
||||
31 31 | getattr(True, "real")
|
||||
32 |-getattr(x := 1, "real")
|
||||
32 |+(x := 1).real
|
||||
33 33 | getattr(x + y, "real")
|
||||
34 34 | getattr("foo"
|
||||
35 35 | "bar", "real")
|
||||
|
||||
B009_B010.py:33:1: B009 [*] Do not call `getattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
31 | getattr(True, "real")
|
||||
32 | getattr(x := 1, "real")
|
||||
33 | getattr(x + y, "real")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ B009
|
||||
34 | getattr("foo"
|
||||
35 | "bar", "real")
|
||||
|
|
||||
= help: Replace `getattr` with attribute access
|
||||
|
||||
ℹ Suggested fix
|
||||
30 30 | getattr(1j, "real")
|
||||
31 31 | getattr(True, "real")
|
||||
32 32 | getattr(x := 1, "real")
|
||||
33 |-getattr(x + y, "real")
|
||||
33 |+(x + y).real
|
||||
34 34 | getattr("foo"
|
||||
35 35 | "bar", "real")
|
||||
36 36 |
|
||||
|
||||
B009_B010.py:34:1: B009 [*] Do not call `getattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
32 | getattr(x := 1, "real")
|
||||
33 | getattr(x + y, "real")
|
||||
34 | / getattr("foo"
|
||||
35 | | "bar", "real")
|
||||
| |______________________^ B009
|
||||
|
|
||||
= help: Replace `getattr` with attribute access
|
||||
|
||||
ℹ Suggested fix
|
||||
31 31 | getattr(True, "real")
|
||||
32 32 | getattr(x := 1, "real")
|
||||
33 33 | getattr(x + y, "real")
|
||||
34 |-getattr("foo"
|
||||
35 |- "bar", "real")
|
||||
34 |+("foo"
|
||||
35 |+ "bar").real
|
||||
36 36 |
|
||||
37 37 |
|
||||
38 38 | # Valid setattr usage
|
||||
|
||||
|
||||
|
||||
@@ -1,120 +1,120 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_bugbear/mod.rs
|
||||
---
|
||||
B009_B010.py:40:1: B010 [*] Do not call `setattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
B009_B010.py:50:1: B010 [*] Do not call `setattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
39 | # Invalid usage
|
||||
40 | setattr(foo, "bar", None)
|
||||
49 | # Invalid usage
|
||||
50 | setattr(foo, "bar", None)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ B010
|
||||
41 | setattr(foo, "_123abc", None)
|
||||
42 | setattr(foo, "__123abc__", None)
|
||||
51 | setattr(foo, "_123abc", None)
|
||||
52 | setattr(foo, "__123abc__", None)
|
||||
|
|
||||
= help: Replace `setattr` with assignment
|
||||
|
||||
ℹ Suggested fix
|
||||
37 37 | pass
|
||||
38 38 |
|
||||
39 39 | # Invalid usage
|
||||
40 |-setattr(foo, "bar", None)
|
||||
40 |+foo.bar = None
|
||||
41 41 | setattr(foo, "_123abc", None)
|
||||
42 42 | setattr(foo, "__123abc__", None)
|
||||
43 43 | setattr(foo, "abc123", None)
|
||||
47 47 | pass
|
||||
48 48 |
|
||||
49 49 | # Invalid usage
|
||||
50 |-setattr(foo, "bar", None)
|
||||
50 |+foo.bar = None
|
||||
51 51 | setattr(foo, "_123abc", None)
|
||||
52 52 | setattr(foo, "__123abc__", None)
|
||||
53 53 | setattr(foo, "abc123", None)
|
||||
|
||||
B009_B010.py:41:1: B010 [*] Do not call `setattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
B009_B010.py:51:1: B010 [*] Do not call `setattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
39 | # Invalid usage
|
||||
40 | setattr(foo, "bar", None)
|
||||
41 | setattr(foo, "_123abc", None)
|
||||
49 | # Invalid usage
|
||||
50 | setattr(foo, "bar", None)
|
||||
51 | setattr(foo, "_123abc", None)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B010
|
||||
42 | setattr(foo, "__123abc__", None)
|
||||
43 | setattr(foo, "abc123", None)
|
||||
52 | setattr(foo, "__123abc__", None)
|
||||
53 | setattr(foo, "abc123", None)
|
||||
|
|
||||
= help: Replace `setattr` with assignment
|
||||
|
||||
ℹ Suggested fix
|
||||
38 38 |
|
||||
39 39 | # Invalid usage
|
||||
40 40 | setattr(foo, "bar", None)
|
||||
41 |-setattr(foo, "_123abc", None)
|
||||
41 |+foo._123abc = None
|
||||
42 42 | setattr(foo, "__123abc__", None)
|
||||
43 43 | setattr(foo, "abc123", None)
|
||||
44 44 | setattr(foo, r"abc123", None)
|
||||
48 48 |
|
||||
49 49 | # Invalid usage
|
||||
50 50 | setattr(foo, "bar", None)
|
||||
51 |-setattr(foo, "_123abc", None)
|
||||
51 |+foo._123abc = None
|
||||
52 52 | setattr(foo, "__123abc__", None)
|
||||
53 53 | setattr(foo, "abc123", None)
|
||||
54 54 | setattr(foo, r"abc123", None)
|
||||
|
||||
B009_B010.py:42:1: B010 [*] Do not call `setattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
B009_B010.py:52:1: B010 [*] Do not call `setattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
40 | setattr(foo, "bar", None)
|
||||
41 | setattr(foo, "_123abc", None)
|
||||
42 | setattr(foo, "__123abc__", None)
|
||||
50 | setattr(foo, "bar", None)
|
||||
51 | setattr(foo, "_123abc", None)
|
||||
52 | setattr(foo, "__123abc__", None)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B010
|
||||
43 | setattr(foo, "abc123", None)
|
||||
44 | setattr(foo, r"abc123", None)
|
||||
53 | setattr(foo, "abc123", None)
|
||||
54 | setattr(foo, r"abc123", None)
|
||||
|
|
||||
= help: Replace `setattr` with assignment
|
||||
|
||||
ℹ Suggested fix
|
||||
39 39 | # Invalid usage
|
||||
40 40 | setattr(foo, "bar", None)
|
||||
41 41 | setattr(foo, "_123abc", None)
|
||||
42 |-setattr(foo, "__123abc__", None)
|
||||
42 |+foo.__123abc__ = None
|
||||
43 43 | setattr(foo, "abc123", None)
|
||||
44 44 | setattr(foo, r"abc123", None)
|
||||
45 45 | setattr(foo.bar, r"baz", None)
|
||||
49 49 | # Invalid usage
|
||||
50 50 | setattr(foo, "bar", None)
|
||||
51 51 | setattr(foo, "_123abc", None)
|
||||
52 |-setattr(foo, "__123abc__", None)
|
||||
52 |+foo.__123abc__ = None
|
||||
53 53 | setattr(foo, "abc123", None)
|
||||
54 54 | setattr(foo, r"abc123", None)
|
||||
55 55 | setattr(foo.bar, r"baz", None)
|
||||
|
||||
B009_B010.py:43:1: B010 [*] Do not call `setattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
B009_B010.py:53:1: B010 [*] Do not call `setattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
41 | setattr(foo, "_123abc", None)
|
||||
42 | setattr(foo, "__123abc__", None)
|
||||
43 | setattr(foo, "abc123", None)
|
||||
51 | setattr(foo, "_123abc", None)
|
||||
52 | setattr(foo, "__123abc__", None)
|
||||
53 | setattr(foo, "abc123", None)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B010
|
||||
44 | setattr(foo, r"abc123", None)
|
||||
45 | setattr(foo.bar, r"baz", None)
|
||||
54 | setattr(foo, r"abc123", None)
|
||||
55 | setattr(foo.bar, r"baz", None)
|
||||
|
|
||||
= help: Replace `setattr` with assignment
|
||||
|
||||
ℹ Suggested fix
|
||||
40 40 | setattr(foo, "bar", None)
|
||||
41 41 | setattr(foo, "_123abc", None)
|
||||
42 42 | setattr(foo, "__123abc__", None)
|
||||
43 |-setattr(foo, "abc123", None)
|
||||
43 |+foo.abc123 = None
|
||||
44 44 | setattr(foo, r"abc123", None)
|
||||
45 45 | setattr(foo.bar, r"baz", None)
|
||||
50 50 | setattr(foo, "bar", None)
|
||||
51 51 | setattr(foo, "_123abc", None)
|
||||
52 52 | setattr(foo, "__123abc__", None)
|
||||
53 |-setattr(foo, "abc123", None)
|
||||
53 |+foo.abc123 = None
|
||||
54 54 | setattr(foo, r"abc123", None)
|
||||
55 55 | setattr(foo.bar, r"baz", None)
|
||||
|
||||
B009_B010.py:44:1: B010 [*] Do not call `setattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
B009_B010.py:54:1: B010 [*] Do not call `setattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
42 | setattr(foo, "__123abc__", None)
|
||||
43 | setattr(foo, "abc123", None)
|
||||
44 | setattr(foo, r"abc123", None)
|
||||
52 | setattr(foo, "__123abc__", None)
|
||||
53 | setattr(foo, "abc123", None)
|
||||
54 | setattr(foo, r"abc123", None)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B010
|
||||
45 | setattr(foo.bar, r"baz", None)
|
||||
55 | setattr(foo.bar, r"baz", None)
|
||||
|
|
||||
= help: Replace `setattr` with assignment
|
||||
|
||||
ℹ Suggested fix
|
||||
41 41 | setattr(foo, "_123abc", None)
|
||||
42 42 | setattr(foo, "__123abc__", None)
|
||||
43 43 | setattr(foo, "abc123", None)
|
||||
44 |-setattr(foo, r"abc123", None)
|
||||
44 |+foo.abc123 = None
|
||||
45 45 | setattr(foo.bar, r"baz", None)
|
||||
51 51 | setattr(foo, "_123abc", None)
|
||||
52 52 | setattr(foo, "__123abc__", None)
|
||||
53 53 | setattr(foo, "abc123", None)
|
||||
54 |-setattr(foo, r"abc123", None)
|
||||
54 |+foo.abc123 = None
|
||||
55 55 | setattr(foo.bar, r"baz", None)
|
||||
|
||||
B009_B010.py:45:1: B010 [*] Do not call `setattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
B009_B010.py:55:1: B010 [*] Do not call `setattr` with a constant attribute value. It is not any safer than normal property access.
|
||||
|
|
||||
43 | setattr(foo, "abc123", None)
|
||||
44 | setattr(foo, r"abc123", None)
|
||||
45 | setattr(foo.bar, r"baz", None)
|
||||
53 | setattr(foo, "abc123", None)
|
||||
54 | setattr(foo, r"abc123", None)
|
||||
55 | setattr(foo.bar, r"baz", None)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B010
|
||||
|
|
||||
= help: Replace `setattr` with assignment
|
||||
|
||||
ℹ Suggested fix
|
||||
42 42 | setattr(foo, "__123abc__", None)
|
||||
43 43 | setattr(foo, "abc123", None)
|
||||
44 44 | setattr(foo, r"abc123", None)
|
||||
45 |-setattr(foo.bar, r"baz", None)
|
||||
45 |+foo.bar.baz = None
|
||||
52 52 | setattr(foo, "__123abc__", None)
|
||||
53 53 | setattr(foo, "abc123", None)
|
||||
54 54 | setattr(foo, r"abc123", None)
|
||||
55 |-setattr(foo.bar, r"baz", None)
|
||||
55 |+foo.bar.baz = None
|
||||
|
||||
|
||||
|
||||
@@ -1,24 +1,43 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_bugbear/mod.rs
|
||||
---
|
||||
B013.py:3:8: B013 [*] A length-one tuple literal is redundant. Write `except ValueError` instead of `except (ValueError,)`.
|
||||
B013.py:5:8: B013 [*] A length-one tuple literal is redundant in exception handlers
|
||||
|
|
||||
1 | try:
|
||||
2 | pass
|
||||
3 | except (ValueError,):
|
||||
| ^^^^^^^^^^^^^ B013
|
||||
3 | try:
|
||||
4 | pass
|
||||
5 | except AttributeError:
|
||||
5 | except (ValueError,):
|
||||
| ^^^^^^^^^^^^^ B013
|
||||
6 | pass
|
||||
7 | except AttributeError:
|
||||
|
|
||||
= help: Replace with `except ValueError`
|
||||
|
||||
ℹ Fix
|
||||
1 1 | try:
|
||||
2 2 | pass
|
||||
3 |-except (ValueError,):
|
||||
3 |+except ValueError:
|
||||
2 2 |
|
||||
3 3 | try:
|
||||
4 4 | pass
|
||||
5 5 | except AttributeError:
|
||||
5 |-except (ValueError,):
|
||||
5 |+except ValueError:
|
||||
6 6 | pass
|
||||
7 7 | except AttributeError:
|
||||
8 8 | pass
|
||||
|
||||
B013.py:11:8: B013 [*] A length-one tuple literal is redundant in exception handlers
|
||||
|
|
||||
9 | except (ImportError, TypeError):
|
||||
10 | pass
|
||||
11 | except (*retriable_exceptions,):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ B013
|
||||
12 | pass
|
||||
|
|
||||
= help: Replace with `except retriable_exceptions`
|
||||
|
||||
ℹ Fix
|
||||
8 8 | pass
|
||||
9 9 | except (ImportError, TypeError):
|
||||
10 10 | pass
|
||||
11 |-except (*retriable_exceptions,):
|
||||
11 |+except retriable_exceptions:
|
||||
12 12 | pass
|
||||
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ pub(crate) fn fix_unnecessary_generator_list(
|
||||
expr: &Expr,
|
||||
) -> Result<Edit> {
|
||||
// Expr(Call(GeneratorExp)))) -> Expr(ListComp)))
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let call = match_call_mut(&mut tree)?;
|
||||
let arg = match_arg(call)?;
|
||||
@@ -63,7 +63,7 @@ pub(crate) fn fix_unnecessary_generator_set(checker: &Checker, expr: &Expr) -> R
|
||||
let stylist = checker.stylist();
|
||||
|
||||
// Expr(Call(GeneratorExp)))) -> Expr(SetComp)))
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let call = match_call_mut(&mut tree)?;
|
||||
let arg = match_arg(call)?;
|
||||
@@ -97,7 +97,7 @@ pub(crate) fn fix_unnecessary_generator_dict(checker: &Checker, expr: &Expr) ->
|
||||
let locator = checker.locator();
|
||||
let stylist = checker.stylist();
|
||||
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let call = match_call_mut(&mut tree)?;
|
||||
let arg = match_arg(call)?;
|
||||
@@ -141,7 +141,7 @@ pub(crate) fn fix_unnecessary_list_comprehension_set(
|
||||
let stylist = checker.stylist();
|
||||
// Expr(Call(ListComp)))) ->
|
||||
// Expr(SetComp)))
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let call = match_call_mut(&mut tree)?;
|
||||
let arg = match_arg(call)?;
|
||||
@@ -176,7 +176,7 @@ pub(crate) fn fix_unnecessary_list_comprehension_dict(
|
||||
let locator = checker.locator();
|
||||
let stylist = checker.stylist();
|
||||
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let call = match_call_mut(&mut tree)?;
|
||||
let arg = match_arg(call)?;
|
||||
@@ -261,7 +261,7 @@ pub(crate) fn fix_unnecessary_literal_set(checker: &Checker, expr: &Expr) -> Res
|
||||
let stylist = checker.stylist();
|
||||
|
||||
// Expr(Call(List|Tuple)))) -> Expr(Set)))
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let call = match_call_mut(&mut tree)?;
|
||||
let arg = match_arg(call)?;
|
||||
@@ -302,7 +302,7 @@ pub(crate) fn fix_unnecessary_literal_dict(checker: &Checker, expr: &Expr) -> Re
|
||||
let stylist = checker.stylist();
|
||||
|
||||
// Expr(Call(List|Tuple)))) -> Expr(Dict)))
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let call = match_call_mut(&mut tree)?;
|
||||
let arg = match_arg(call)?;
|
||||
@@ -371,7 +371,7 @@ pub(crate) fn fix_unnecessary_collection_call(checker: &Checker, expr: &Expr) ->
|
||||
let stylist = checker.stylist();
|
||||
|
||||
// Expr(Call("list" | "tuple" | "dict")))) -> Expr(List|Tuple|Dict)
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let call = match_call(&tree)?;
|
||||
let name = match_name(&call.func)?;
|
||||
@@ -522,7 +522,7 @@ pub(crate) fn fix_unnecessary_literal_within_tuple_call(
|
||||
stylist: &Stylist,
|
||||
expr: &Expr,
|
||||
) -> Result<Edit> {
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let call = match_call_mut(&mut tree)?;
|
||||
let arg = match_arg(call)?;
|
||||
@@ -572,7 +572,7 @@ pub(crate) fn fix_unnecessary_literal_within_list_call(
|
||||
stylist: &Stylist,
|
||||
expr: &Expr,
|
||||
) -> Result<Edit> {
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let call = match_call_mut(&mut tree)?;
|
||||
let arg = match_arg(call)?;
|
||||
@@ -625,7 +625,7 @@ pub(crate) fn fix_unnecessary_list_call(
|
||||
expr: &Expr,
|
||||
) -> Result<Edit> {
|
||||
// Expr(Call(List|Tuple)))) -> Expr(List|Tuple)))
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let call = match_call_mut(&mut tree)?;
|
||||
let arg = match_arg(call)?;
|
||||
@@ -646,7 +646,7 @@ pub(crate) fn fix_unnecessary_call_around_sorted(
|
||||
stylist: &Stylist,
|
||||
expr: &Expr,
|
||||
) -> Result<Edit> {
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let outer_call = match_call_mut(&mut tree)?;
|
||||
let inner_call = match &outer_call.args[..] {
|
||||
@@ -758,7 +758,7 @@ pub(crate) fn fix_unnecessary_double_cast_or_process(
|
||||
stylist: &Stylist,
|
||||
expr: &Expr,
|
||||
) -> Result<Edit> {
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let outer_call = match_call_mut(&mut tree)?;
|
||||
|
||||
@@ -789,7 +789,7 @@ pub(crate) fn fix_unnecessary_comprehension(
|
||||
stylist: &Stylist,
|
||||
expr: &Expr,
|
||||
) -> Result<Edit> {
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
|
||||
match &tree {
|
||||
@@ -878,7 +878,7 @@ pub(crate) fn fix_unnecessary_map(
|
||||
parent: Option<&Expr>,
|
||||
object_type: ObjectType,
|
||||
) -> Result<Edit> {
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let call = match_call_mut(&mut tree)?;
|
||||
let arg = match_arg(call)?;
|
||||
@@ -966,21 +966,21 @@ pub(crate) fn fix_unnecessary_map(
|
||||
}));
|
||||
}
|
||||
ObjectType::Dict => {
|
||||
let (key, value) = if let Expression::Tuple(tuple) = func_body.body.as_ref() {
|
||||
if tuple.elements.len() != 2 {
|
||||
bail!("Expected two elements")
|
||||
let elements = match func_body.body.as_ref() {
|
||||
Expression::Tuple(tuple) => &tuple.elements,
|
||||
Expression::List(list) => &list.elements,
|
||||
_ => {
|
||||
bail!("Expected tuple or list for dictionary comprehension")
|
||||
}
|
||||
|
||||
let Some(Element::Simple { value: key, .. }) = &tuple.elements.get(0) else {
|
||||
bail!("Expected tuple to contain a key as the first element");
|
||||
};
|
||||
let Some(Element::Simple { value, .. }) = &tuple.elements.get(1) else {
|
||||
bail!("Expected tuple to contain a key as the second element");
|
||||
};
|
||||
|
||||
(key, value)
|
||||
} else {
|
||||
bail!("Expected tuple for dict comprehension")
|
||||
};
|
||||
let [key, value] = elements.as_slice() else {
|
||||
bail!("Expected container to include two elements");
|
||||
};
|
||||
let Element::Simple { value: key, .. } = key else {
|
||||
bail!("Expected container to use a key as the first element");
|
||||
};
|
||||
let Element::Simple { value, .. } = value else {
|
||||
bail!("Expected container to use a value as the second element");
|
||||
};
|
||||
|
||||
tree = Expression::DictComp(Box::new(DictComp {
|
||||
@@ -1021,7 +1021,7 @@ pub(crate) fn fix_unnecessary_literal_within_dict_call(
|
||||
stylist: &Stylist,
|
||||
expr: &Expr,
|
||||
) -> Result<Edit> {
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let call = match_call_mut(&mut tree)?;
|
||||
let arg = match_arg(call)?;
|
||||
@@ -1041,7 +1041,7 @@ pub(crate) fn fix_unnecessary_comprehension_any_all(
|
||||
expr: &Expr,
|
||||
) -> Result<Fix> {
|
||||
// Expr(ListComp) -> Expr(GeneratorExp)
|
||||
let module_text = locator.slice(expr.range());
|
||||
let module_text = locator.slice(expr);
|
||||
let mut tree = match_expression(module_text)?;
|
||||
let call = match_call_mut(&mut tree)?;
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ pub(crate) fn unnecessary_map(
|
||||
ObjectType::Generator => {
|
||||
// Exclude the parent if already matched by other arms.
|
||||
if parent
|
||||
.and_then(ruff_python_ast::Expr::as_call_expr)
|
||||
.and_then(Expr::as_call_expr)
|
||||
.and_then(|call| call.func.as_name_expr())
|
||||
.is_some_and(|name| matches!(name.id.as_str(), "list" | "set" | "dict"))
|
||||
{
|
||||
@@ -122,7 +122,7 @@ pub(crate) fn unnecessary_map(
|
||||
// Only flag, e.g., `list(map(lambda x: x + 1, iterable))`.
|
||||
let [Expr::Call(ast::ExprCall {
|
||||
func,
|
||||
arguments: Arguments { args, .. },
|
||||
arguments: Arguments { args, keywords, .. },
|
||||
..
|
||||
})] = args
|
||||
else {
|
||||
@@ -133,6 +133,10 @@ pub(crate) fn unnecessary_map(
|
||||
return;
|
||||
}
|
||||
|
||||
if !keywords.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(argument) = helpers::first_argument_with_matching_function("map", func, args)
|
||||
else {
|
||||
return;
|
||||
@@ -163,13 +167,21 @@ pub(crate) fn unnecessary_map(
|
||||
// Only flag, e.g., `dict(map(lambda v: (v, v ** 2), values))`.
|
||||
let [Expr::Call(ast::ExprCall {
|
||||
func,
|
||||
arguments: Arguments { args, .. },
|
||||
arguments: Arguments { args, keywords, .. },
|
||||
..
|
||||
})] = args
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
if args.len() != 2 {
|
||||
return;
|
||||
}
|
||||
|
||||
if !keywords.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(argument) = helpers::first_argument_with_matching_function("map", func, args)
|
||||
else {
|
||||
return;
|
||||
|
||||
@@ -61,7 +61,7 @@ C417.py:5:1: C417 [*] Unnecessary `map` usage (rewrite using a `list` comprehens
|
||||
5 |+[x * 2 for x in nums]
|
||||
6 6 | set(map(lambda x: x % 2 == 0, nums))
|
||||
7 7 | dict(map(lambda v: (v, v**2), nums))
|
||||
8 8 | map(lambda: "const", nums)
|
||||
8 8 | dict(map(lambda v: [v, v**2], nums))
|
||||
|
||||
C417.py:6:1: C417 [*] Unnecessary `map` usage (rewrite using a `set` comprehension)
|
||||
|
|
||||
@@ -70,7 +70,7 @@ C417.py:6:1: C417 [*] Unnecessary `map` usage (rewrite using a `set` comprehensi
|
||||
6 | set(map(lambda x: x % 2 == 0, nums))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C417
|
||||
7 | dict(map(lambda v: (v, v**2), nums))
|
||||
8 | map(lambda: "const", nums)
|
||||
8 | dict(map(lambda v: [v, v**2], nums))
|
||||
|
|
||||
= help: Replace `map` with a `set` comprehension
|
||||
|
||||
@@ -81,8 +81,8 @@ C417.py:6:1: C417 [*] Unnecessary `map` usage (rewrite using a `set` comprehensi
|
||||
6 |-set(map(lambda x: x % 2 == 0, nums))
|
||||
6 |+{x % 2 == 0 for x in nums}
|
||||
7 7 | dict(map(lambda v: (v, v**2), nums))
|
||||
8 8 | map(lambda: "const", nums)
|
||||
9 9 | map(lambda _: 3.0, nums)
|
||||
8 8 | dict(map(lambda v: [v, v**2], nums))
|
||||
9 9 | map(lambda: "const", nums)
|
||||
|
||||
C417.py:7:1: C417 [*] Unnecessary `map` usage (rewrite using a `dict` comprehension)
|
||||
|
|
||||
@@ -90,8 +90,8 @@ C417.py:7:1: C417 [*] Unnecessary `map` usage (rewrite using a `dict` comprehens
|
||||
6 | set(map(lambda x: x % 2 == 0, nums))
|
||||
7 | dict(map(lambda v: (v, v**2), nums))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C417
|
||||
8 | map(lambda: "const", nums)
|
||||
9 | map(lambda _: 3.0, nums)
|
||||
8 | dict(map(lambda v: [v, v**2], nums))
|
||||
9 | map(lambda: "const", nums)
|
||||
|
|
||||
= help: Replace `map` with a `dict` comprehension
|
||||
|
||||
@@ -101,172 +101,193 @@ C417.py:7:1: C417 [*] Unnecessary `map` usage (rewrite using a `dict` comprehens
|
||||
6 6 | set(map(lambda x: x % 2 == 0, nums))
|
||||
7 |-dict(map(lambda v: (v, v**2), nums))
|
||||
7 |+{v: v**2 for v in nums}
|
||||
8 8 | map(lambda: "const", nums)
|
||||
9 9 | map(lambda _: 3.0, nums)
|
||||
10 10 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
8 8 | dict(map(lambda v: [v, v**2], nums))
|
||||
9 9 | map(lambda: "const", nums)
|
||||
10 10 | map(lambda _: 3.0, nums)
|
||||
|
||||
C417.py:8:1: C417 [*] Unnecessary `map` usage (rewrite using a generator expression)
|
||||
C417.py:8:1: C417 [*] Unnecessary `map` usage (rewrite using a `dict` comprehension)
|
||||
|
|
||||
6 | set(map(lambda x: x % 2 == 0, nums))
|
||||
7 | dict(map(lambda v: (v, v**2), nums))
|
||||
8 | map(lambda: "const", nums)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ C417
|
||||
9 | map(lambda _: 3.0, nums)
|
||||
10 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
8 | dict(map(lambda v: [v, v**2], nums))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C417
|
||||
9 | map(lambda: "const", nums)
|
||||
10 | map(lambda _: 3.0, nums)
|
||||
|
|
||||
= help: Replace `map` with a generator expression
|
||||
= help: Replace `map` with a `dict` comprehension
|
||||
|
||||
ℹ Suggested fix
|
||||
5 5 | list(map(lambda x: x * 2, nums))
|
||||
6 6 | set(map(lambda x: x % 2 == 0, nums))
|
||||
7 7 | dict(map(lambda v: (v, v**2), nums))
|
||||
8 |-map(lambda: "const", nums)
|
||||
8 |+("const" for _ in nums)
|
||||
9 9 | map(lambda _: 3.0, nums)
|
||||
10 10 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
11 11 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
8 |-dict(map(lambda v: [v, v**2], nums))
|
||||
8 |+{v: v**2 for v in nums}
|
||||
9 9 | map(lambda: "const", nums)
|
||||
10 10 | map(lambda _: 3.0, nums)
|
||||
11 11 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
|
||||
C417.py:9:1: C417 [*] Unnecessary `map` usage (rewrite using a generator expression)
|
||||
|
|
||||
7 | dict(map(lambda v: (v, v**2), nums))
|
||||
8 | map(lambda: "const", nums)
|
||||
9 | map(lambda _: 3.0, nums)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ C417
|
||||
10 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
11 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
8 | dict(map(lambda v: [v, v**2], nums))
|
||||
9 | map(lambda: "const", nums)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ C417
|
||||
10 | map(lambda _: 3.0, nums)
|
||||
11 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
|
|
||||
= help: Replace `map` with a generator expression
|
||||
|
||||
ℹ Suggested fix
|
||||
6 6 | set(map(lambda x: x % 2 == 0, nums))
|
||||
7 7 | dict(map(lambda v: (v, v**2), nums))
|
||||
8 8 | map(lambda: "const", nums)
|
||||
9 |-map(lambda _: 3.0, nums)
|
||||
9 |+(3.0 for _ in nums)
|
||||
10 10 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
11 11 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
12 12 | filter(func, map(lambda v: v, nums))
|
||||
8 8 | dict(map(lambda v: [v, v**2], nums))
|
||||
9 |-map(lambda: "const", nums)
|
||||
9 |+("const" for _ in nums)
|
||||
10 10 | map(lambda _: 3.0, nums)
|
||||
11 11 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
12 12 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
|
||||
C417.py:10:13: C417 [*] Unnecessary `map` usage (rewrite using a generator expression)
|
||||
C417.py:10:1: C417 [*] Unnecessary `map` usage (rewrite using a generator expression)
|
||||
|
|
||||
8 | map(lambda: "const", nums)
|
||||
9 | map(lambda _: 3.0, nums)
|
||||
10 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C417
|
||||
11 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
12 | filter(func, map(lambda v: v, nums))
|
||||
8 | dict(map(lambda v: [v, v**2], nums))
|
||||
9 | map(lambda: "const", nums)
|
||||
10 | map(lambda _: 3.0, nums)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ C417
|
||||
11 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
12 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
|
|
||||
= help: Replace `map` with a generator expression
|
||||
|
||||
ℹ Suggested fix
|
||||
7 7 | dict(map(lambda v: (v, v**2), nums))
|
||||
8 8 | map(lambda: "const", nums)
|
||||
9 9 | map(lambda _: 3.0, nums)
|
||||
10 |-_ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
10 |+_ = "".join((x in nums and "1" or "0" for x in range(123)))
|
||||
11 11 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
12 12 | filter(func, map(lambda v: v, nums))
|
||||
13 13 |
|
||||
8 8 | dict(map(lambda v: [v, v**2], nums))
|
||||
9 9 | map(lambda: "const", nums)
|
||||
10 |-map(lambda _: 3.0, nums)
|
||||
10 |+(3.0 for _ in nums)
|
||||
11 11 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
12 12 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
13 13 | filter(func, map(lambda v: v, nums))
|
||||
|
||||
C417.py:11:5: C417 [*] Unnecessary `map` usage (rewrite using a generator expression)
|
||||
C417.py:11:13: C417 [*] Unnecessary `map` usage (rewrite using a generator expression)
|
||||
|
|
||||
9 | map(lambda _: 3.0, nums)
|
||||
10 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
11 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
9 | map(lambda: "const", nums)
|
||||
10 | map(lambda _: 3.0, nums)
|
||||
11 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C417
|
||||
12 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
13 | filter(func, map(lambda v: v, nums))
|
||||
|
|
||||
= help: Replace `map` with a generator expression
|
||||
|
||||
ℹ Suggested fix
|
||||
8 8 | dict(map(lambda v: [v, v**2], nums))
|
||||
9 9 | map(lambda: "const", nums)
|
||||
10 10 | map(lambda _: 3.0, nums)
|
||||
11 |-_ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
11 |+_ = "".join((x in nums and "1" or "0" for x in range(123)))
|
||||
12 12 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
13 13 | filter(func, map(lambda v: v, nums))
|
||||
14 14 |
|
||||
|
||||
C417.py:12:5: C417 [*] Unnecessary `map` usage (rewrite using a generator expression)
|
||||
|
|
||||
10 | map(lambda _: 3.0, nums)
|
||||
11 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
12 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C417
|
||||
12 | filter(func, map(lambda v: v, nums))
|
||||
13 | filter(func, map(lambda v: v, nums))
|
||||
|
|
||||
= help: Replace `map` with a generator expression
|
||||
|
||||
ℹ Suggested fix
|
||||
8 8 | map(lambda: "const", nums)
|
||||
9 9 | map(lambda _: 3.0, nums)
|
||||
10 10 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
11 |-all(map(lambda v: isinstance(v, dict), nums))
|
||||
11 |+all((isinstance(v, dict) for v in nums))
|
||||
12 12 | filter(func, map(lambda v: v, nums))
|
||||
13 13 |
|
||||
14 14 | # When inside f-string, then the fix should be surrounded by whitespace
|
||||
9 9 | map(lambda: "const", nums)
|
||||
10 10 | map(lambda _: 3.0, nums)
|
||||
11 11 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
12 |-all(map(lambda v: isinstance(v, dict), nums))
|
||||
12 |+all((isinstance(v, dict) for v in nums))
|
||||
13 13 | filter(func, map(lambda v: v, nums))
|
||||
14 14 |
|
||||
15 15 | # When inside f-string, then the fix should be surrounded by whitespace
|
||||
|
||||
C417.py:12:14: C417 [*] Unnecessary `map` usage (rewrite using a generator expression)
|
||||
C417.py:13:14: C417 [*] Unnecessary `map` usage (rewrite using a generator expression)
|
||||
|
|
||||
10 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
11 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
12 | filter(func, map(lambda v: v, nums))
|
||||
11 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
12 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
13 | filter(func, map(lambda v: v, nums))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ C417
|
||||
13 |
|
||||
14 | # When inside f-string, then the fix should be surrounded by whitespace
|
||||
14 |
|
||||
15 | # When inside f-string, then the fix should be surrounded by whitespace
|
||||
|
|
||||
= help: Replace `map` with a generator expression
|
||||
|
||||
ℹ Suggested fix
|
||||
9 9 | map(lambda _: 3.0, nums)
|
||||
10 10 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
11 11 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
12 |-filter(func, map(lambda v: v, nums))
|
||||
12 |+filter(func, (v for v in nums))
|
||||
13 13 |
|
||||
14 14 | # When inside f-string, then the fix should be surrounded by whitespace
|
||||
15 15 | _ = f"{set(map(lambda x: x % 2 == 0, nums))}"
|
||||
10 10 | map(lambda _: 3.0, nums)
|
||||
11 11 | _ = "".join(map(lambda x: x in nums and "1" or "0", range(123)))
|
||||
12 12 | all(map(lambda v: isinstance(v, dict), nums))
|
||||
13 |-filter(func, map(lambda v: v, nums))
|
||||
13 |+filter(func, (v for v in nums))
|
||||
14 14 |
|
||||
15 15 | # When inside f-string, then the fix should be surrounded by whitespace
|
||||
16 16 | _ = f"{set(map(lambda x: x % 2 == 0, nums))}"
|
||||
|
||||
C417.py:15:8: C417 [*] Unnecessary `map` usage (rewrite using a `set` comprehension)
|
||||
C417.py:16:8: C417 [*] Unnecessary `map` usage (rewrite using a `set` comprehension)
|
||||
|
|
||||
14 | # When inside f-string, then the fix should be surrounded by whitespace
|
||||
15 | _ = f"{set(map(lambda x: x % 2 == 0, nums))}"
|
||||
15 | # When inside f-string, then the fix should be surrounded by whitespace
|
||||
16 | _ = f"{set(map(lambda x: x % 2 == 0, nums))}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C417
|
||||
16 | _ = f"{dict(map(lambda v: (v, v**2), nums))}"
|
||||
17 | _ = f"{dict(map(lambda v: (v, v**2), nums))}"
|
||||
|
|
||||
= help: Replace `map` with a `set` comprehension
|
||||
|
||||
ℹ Suggested fix
|
||||
12 12 | filter(func, map(lambda v: v, nums))
|
||||
13 13 |
|
||||
14 14 | # When inside f-string, then the fix should be surrounded by whitespace
|
||||
15 |-_ = f"{set(map(lambda x: x % 2 == 0, nums))}"
|
||||
15 |+_ = f"{ {x % 2 == 0 for x in nums} }"
|
||||
16 16 | _ = f"{dict(map(lambda v: (v, v**2), nums))}"
|
||||
17 17 |
|
||||
18 18 | # False negatives.
|
||||
13 13 | filter(func, map(lambda v: v, nums))
|
||||
14 14 |
|
||||
15 15 | # When inside f-string, then the fix should be surrounded by whitespace
|
||||
16 |-_ = f"{set(map(lambda x: x % 2 == 0, nums))}"
|
||||
16 |+_ = f"{ {x % 2 == 0 for x in nums} }"
|
||||
17 17 | _ = f"{dict(map(lambda v: (v, v**2), nums))}"
|
||||
18 18 |
|
||||
19 19 | # False negatives.
|
||||
|
||||
C417.py:16:8: C417 [*] Unnecessary `map` usage (rewrite using a `dict` comprehension)
|
||||
C417.py:17:8: C417 [*] Unnecessary `map` usage (rewrite using a `dict` comprehension)
|
||||
|
|
||||
14 | # When inside f-string, then the fix should be surrounded by whitespace
|
||||
15 | _ = f"{set(map(lambda x: x % 2 == 0, nums))}"
|
||||
16 | _ = f"{dict(map(lambda v: (v, v**2), nums))}"
|
||||
15 | # When inside f-string, then the fix should be surrounded by whitespace
|
||||
16 | _ = f"{set(map(lambda x: x % 2 == 0, nums))}"
|
||||
17 | _ = f"{dict(map(lambda v: (v, v**2), nums))}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C417
|
||||
17 |
|
||||
18 | # False negatives.
|
||||
18 |
|
||||
19 | # False negatives.
|
||||
|
|
||||
= help: Replace `map` with a `dict` comprehension
|
||||
|
||||
ℹ Suggested fix
|
||||
13 13 |
|
||||
14 14 | # When inside f-string, then the fix should be surrounded by whitespace
|
||||
15 15 | _ = f"{set(map(lambda x: x % 2 == 0, nums))}"
|
||||
16 |-_ = f"{dict(map(lambda v: (v, v**2), nums))}"
|
||||
16 |+_ = f"{ {v: v**2 for v in nums} }"
|
||||
17 17 |
|
||||
18 18 | # False negatives.
|
||||
19 19 | map(lambda x=2, y=1: x + y, nums, nums)
|
||||
14 14 |
|
||||
15 15 | # When inside f-string, then the fix should be surrounded by whitespace
|
||||
16 16 | _ = f"{set(map(lambda x: x % 2 == 0, nums))}"
|
||||
17 |-_ = f"{dict(map(lambda v: (v, v**2), nums))}"
|
||||
17 |+_ = f"{ {v: v**2 for v in nums} }"
|
||||
18 18 |
|
||||
19 19 | # False negatives.
|
||||
20 20 | map(lambda x=2, y=1: x + y, nums, nums)
|
||||
|
||||
C417.py:34:1: C417 [*] Unnecessary `map` usage (rewrite using a generator expression)
|
||||
C417.py:35:1: C417 [*] Unnecessary `map` usage (rewrite using a generator expression)
|
||||
|
|
||||
33 | # Error: the `x` is overridden by the inner lambda.
|
||||
34 | map(lambda x: lambda x: x, range(4))
|
||||
34 | # Error: the `x` is overridden by the inner lambda.
|
||||
35 | map(lambda x: lambda x: x, range(4))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C417
|
||||
35 |
|
||||
36 | # Ok because of the default parameters, and variadic arguments.
|
||||
36 |
|
||||
37 | # Ok because of the default parameters, and variadic arguments.
|
||||
|
|
||||
= help: Replace `map` with a generator expression
|
||||
|
||||
ℹ Suggested fix
|
||||
31 31 | map(lambda x: lambda: x, range(4))
|
||||
32 32 |
|
||||
33 33 | # Error: the `x` is overridden by the inner lambda.
|
||||
34 |-map(lambda x: lambda x: x, range(4))
|
||||
34 |+(lambda x: x for x in range(4))
|
||||
35 35 |
|
||||
36 36 | # Ok because of the default parameters, and variadic arguments.
|
||||
37 37 | map(lambda x=1: x, nums)
|
||||
32 32 | map(lambda x: lambda: x, range(4))
|
||||
33 33 |
|
||||
34 34 | # Error: the `x` is overridden by the inner lambda.
|
||||
35 |-map(lambda x: lambda x: x, range(4))
|
||||
35 |+(lambda x: x for x in range(4))
|
||||
36 36 |
|
||||
37 37 | # Ok because of the default parameters, and variadic arguments.
|
||||
38 38 | map(lambda x=1: x, nums)
|
||||
|
||||
|
||||
|
||||
@@ -149,6 +149,17 @@ import os
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
|
||||
# Copyright 2023
|
||||
"#
|
||||
.trim(),
|
||||
&settings::Settings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
assert_messages!(diagnostics);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn char_boundary() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"কককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককক
|
||||
"#
|
||||
.trim(),
|
||||
&settings::Settings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use crate::settings::Settings;
|
||||
|
||||
@@ -33,11 +32,7 @@ pub(crate) fn missing_copyright_notice(
|
||||
}
|
||||
|
||||
// Only search the first 1024 bytes in the file.
|
||||
let contents = if locator.len() < 1024 {
|
||||
locator.contents()
|
||||
} else {
|
||||
locator.up_to(TextSize::from(1024))
|
||||
};
|
||||
let contents = locator.up_to(locator.floor_char_boundary(TextSize::new(1024)));
|
||||
|
||||
// Locate the copyright notice.
|
||||
if let Some(match_) = settings.flake8_copyright.notice_rgx.find(contents) {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_copyright/mod.rs
|
||||
---
|
||||
<filename>:1:1: CPY001 Missing copyright notice at top of file
|
||||
|
|
||||
1 | কককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককক
|
||||
| CPY001
|
||||
|
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ pub(crate) fn duplicate_union_member<'a>(checker: &mut Checker, expr: &'a Expr)
|
||||
// Replace the parent with its non-duplicate child.
|
||||
let child = if expr == left.as_ref() { right } else { left };
|
||||
diagnostic.set_fix(Fix::automatic(Edit::range_replacement(
|
||||
checker.locator().slice(child.range()).to_string(),
|
||||
checker.locator().slice(child.as_ref()).to_string(),
|
||||
parent.range(),
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use ruff_python_ast::{self as ast, Constant, Expr, Stmt};
|
||||
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::is_docstring_stmt;
|
||||
use ruff_python_ast::{self as ast, Expr, Stmt};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -23,18 +23,35 @@ impl AlwaysAutofixableViolation for NonEmptyStubBody {
|
||||
|
||||
/// PYI010
|
||||
pub(crate) fn non_empty_stub_body(checker: &mut Checker, body: &[Stmt]) {
|
||||
if let [Stmt::Expr(ast::StmtExpr { value, range: _ })] = body {
|
||||
// Ignore multi-statement bodies (covered by PYI048).
|
||||
let [stmt] = body else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Ignore `pass` statements (covered by PYI009).
|
||||
if stmt.is_pass_stmt() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore docstrings (covered by PYI021).
|
||||
if is_docstring_stmt(stmt) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore `...` (the desired case).
|
||||
if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt {
|
||||
if let Expr::Constant(ast::ExprConstant { value, .. }) = value.as_ref() {
|
||||
if matches!(value, Constant::Ellipsis | Constant::Str(_)) {
|
||||
if value.is_ellipsis() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut diagnostic = Diagnostic::new(NonEmptyStubBody, body[0].range());
|
||||
|
||||
let mut diagnostic = Diagnostic::new(NonEmptyStubBody, stmt.range());
|
||||
if checker.patch(Rule::NonEmptyStubBody) {
|
||||
diagnostic.set_fix(Fix::automatic(Edit::range_replacement(
|
||||
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
||||
format!("..."),
|
||||
body[0].range(),
|
||||
stmt.range(),
|
||||
)));
|
||||
};
|
||||
checker.diagnostics.push(diagnostic);
|
||||
|
||||
@@ -22,17 +22,16 @@ impl AlwaysAutofixableViolation for PassStatementStubBody {
|
||||
|
||||
/// PYI009
|
||||
pub(crate) fn pass_statement_stub_body(checker: &mut Checker, body: &[Stmt]) {
|
||||
let [stmt] = body else {
|
||||
let [Stmt::Pass(pass)] = body else {
|
||||
return;
|
||||
};
|
||||
if stmt.is_pass_stmt() {
|
||||
let mut diagnostic = Diagnostic::new(PassStatementStubBody, stmt.range());
|
||||
if checker.patch(Rule::PassStatementStubBody) {
|
||||
diagnostic.set_fix(Fix::automatic(Edit::range_replacement(
|
||||
format!("..."),
|
||||
stmt.range(),
|
||||
)));
|
||||
};
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(PassStatementStubBody, pass.range());
|
||||
if checker.patch(Rule::PassStatementStubBody) {
|
||||
diagnostic.set_fix(Fix::automatic(Edit::range_replacement(
|
||||
format!("..."),
|
||||
pass.range(),
|
||||
)));
|
||||
};
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::autofix::snippet::SourceCodeSnippet;
|
||||
use crate::{checkers::ast::Checker, rules::flake8_pyi::helpers::traverse_union};
|
||||
|
||||
/// ## What it does
|
||||
@@ -38,7 +39,7 @@ use crate::{checkers::ast::Checker, rules::flake8_pyi::helpers::traverse_union};
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct RedundantLiteralUnion {
|
||||
literal: String,
|
||||
literal: SourceCodeSnippet,
|
||||
builtin_type: ExprType,
|
||||
}
|
||||
|
||||
@@ -49,7 +50,11 @@ impl Violation for RedundantLiteralUnion {
|
||||
literal,
|
||||
builtin_type,
|
||||
} = self;
|
||||
format!("`Literal[{literal}]` is redundant in a union with `{builtin_type}`",)
|
||||
if let Some(literal) = literal.full_display() {
|
||||
format!("`Literal[{literal}]` is redundant in a union with `{builtin_type}`")
|
||||
} else {
|
||||
format!("`Literal` is redundant in a union with `{builtin_type}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +93,7 @@ pub(crate) fn redundant_literal_union<'a>(checker: &mut Checker, union: &'a Expr
|
||||
if builtin_types_in_union.contains(&constant_type) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
RedundantLiteralUnion {
|
||||
literal: checker.locator().slice(literal_expr.range()).to_string(),
|
||||
literal: SourceCodeSnippet::from_str(checker.locator().slice(literal_expr)),
|
||||
builtin_type: constant_type,
|
||||
},
|
||||
literal_expr.range(),
|
||||
|
||||
@@ -249,7 +249,7 @@ fn is_valid_default_value_with_annotation(
|
||||
..
|
||||
}) = left.as_ref()
|
||||
{
|
||||
return locator.slice(left.range()).len() <= 10;
|
||||
return locator.slice(left.as_ref()).len() <= 10;
|
||||
} else if let Expr::UnaryOp(ast::ExprUnaryOp {
|
||||
op: UnaryOp::USub,
|
||||
operand,
|
||||
@@ -262,7 +262,7 @@ fn is_valid_default_value_with_annotation(
|
||||
..
|
||||
}) = operand.as_ref()
|
||||
{
|
||||
return locator.slice(operand.range()).len() <= 10;
|
||||
return locator.slice(operand.as_ref()).len() <= 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use ruff_python_ast::Stmt;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::is_docstring_stmt;
|
||||
use ruff_python_ast::identifier::Identifier;
|
||||
use ruff_python_ast::Stmt;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
@@ -17,21 +15,12 @@ impl Violation for StubBodyMultipleStatements {
|
||||
}
|
||||
}
|
||||
|
||||
/// PYI010
|
||||
/// PYI048
|
||||
pub(crate) fn stub_body_multiple_statements(checker: &mut Checker, stmt: &Stmt, body: &[Stmt]) {
|
||||
// If the function body consists of exactly one statement, abort.
|
||||
if body.len() == 1 {
|
||||
return;
|
||||
if body.len() > 1 {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
StubBodyMultipleStatements,
|
||||
stmt.identifier(),
|
||||
));
|
||||
}
|
||||
|
||||
// If the function body consists of exactly two statements, and the first is a
|
||||
// docstring, abort (this is covered by PYI021).
|
||||
if body.len() == 2 && is_docstring_stmt(&body[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
StubBodyMultipleStatements,
|
||||
stmt.identifier(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ pub(crate) fn unnecessary_literal_union<'a>(checker: &mut Checker, expr: &'a Exp
|
||||
UnnecessaryLiteralUnion {
|
||||
members: literal_exprs
|
||||
.into_iter()
|
||||
.map(|literal_expr| checker.locator().slice(literal_expr.range()).to_string())
|
||||
.map(|expr| checker.locator().slice(expr.as_ref()).to_string())
|
||||
.collect(),
|
||||
},
|
||||
expr.range(),
|
||||
|
||||
@@ -78,7 +78,7 @@ pub(crate) fn unnecessary_type_union<'a>(checker: &mut Checker, union: &'a Expr)
|
||||
UnnecessaryTypeUnion {
|
||||
members: type_exprs
|
||||
.into_iter()
|
||||
.map(|type_expr| checker.locator().slice(type_expr.range()).to_string())
|
||||
.map(|type_expr| checker.locator().slice(type_expr.as_ref()).to_string())
|
||||
.collect(),
|
||||
is_pep604_union,
|
||||
},
|
||||
|
||||
@@ -11,8 +11,8 @@ PYI010.pyi:6:5: PYI010 [*] Function body must contain only `...`
|
||||
|
|
||||
= help: Replace function body with `...`
|
||||
|
||||
ℹ Fix
|
||||
3 3 | """foo""" # OK, strings are handled by another rule
|
||||
ℹ Suggested fix
|
||||
3 3 | """foo""" # OK, docstrings are handled by another rule
|
||||
4 4 |
|
||||
5 5 | def buzz():
|
||||
6 |- print("buzz") # ERROR PYI010
|
||||
@@ -31,7 +31,7 @@ PYI010.pyi:9:5: PYI010 [*] Function body must contain only `...`
|
||||
|
|
||||
= help: Replace function body with `...`
|
||||
|
||||
ℹ Fix
|
||||
ℹ Suggested fix
|
||||
6 6 | print("buzz") # ERROR PYI010
|
||||
7 7 |
|
||||
8 8 | def foo2():
|
||||
@@ -46,14 +46,19 @@ PYI010.pyi:12:5: PYI010 [*] Function body must contain only `...`
|
||||
11 | def bizz():
|
||||
12 | x = 123 # ERROR PYI010
|
||||
| ^^^^^^^ PYI010
|
||||
13 |
|
||||
14 | def foo3():
|
||||
|
|
||||
= help: Replace function body with `...`
|
||||
|
||||
ℹ Fix
|
||||
ℹ Suggested fix
|
||||
9 9 | 123 # ERROR PYI010
|
||||
10 10 |
|
||||
11 11 | def bizz():
|
||||
12 |- x = 123 # ERROR PYI010
|
||||
12 |+ ... # ERROR PYI010
|
||||
13 13 |
|
||||
14 14 | def foo3():
|
||||
15 15 | pass # OK, pass is handled by another rule
|
||||
|
||||
|
||||
|
||||
@@ -1,17 +1,31 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_pyi/mod.rs
|
||||
---
|
||||
PYI048.pyi:11:5: PYI048 Function body must contain exactly one statement
|
||||
PYI048.pyi:8:5: PYI048 Function body must contain exactly one statement
|
||||
|
|
||||
11 | def foo(): # ERROR PYI048
|
||||
6 | """oof""" # OK
|
||||
7 |
|
||||
8 | def oof(): # ERROR PYI048
|
||||
| ^^^ PYI048
|
||||
12 | """foo"""
|
||||
13 | print("foo")
|
||||
9 | """oof"""
|
||||
10 | print("foo")
|
||||
|
|
||||
|
||||
PYI048.pyi:12:5: PYI048 Function body must contain exactly one statement
|
||||
|
|
||||
10 | print("foo")
|
||||
11 |
|
||||
12 | def foo(): # ERROR PYI048
|
||||
| ^^^ PYI048
|
||||
13 | """foo"""
|
||||
14 | print("foo")
|
||||
|
|
||||
|
||||
PYI048.pyi:17:5: PYI048 Function body must contain exactly one statement
|
||||
|
|
||||
17 | def buzz(): # ERROR PYI048
|
||||
15 | print("foo")
|
||||
16 |
|
||||
17 | def buzz(): # ERROR PYI048
|
||||
| ^^^^ PYI048
|
||||
18 | print("fizz")
|
||||
19 | print("buzz")
|
||||
|
||||
@@ -411,7 +411,7 @@ fn to_pytest_raises_args<'a>(
|
||||
"assertRaises" | "failUnlessRaises" => {
|
||||
match (arguments.args.as_slice(), arguments.keywords.as_slice()) {
|
||||
// Ex) `assertRaises(Exception)`
|
||||
([arg], []) => Cow::Borrowed(checker.locator().slice(arg.range())),
|
||||
([arg], []) => Cow::Borrowed(checker.locator().slice(arg)),
|
||||
// Ex) `assertRaises(expected_exception=Exception)`
|
||||
([], [kwarg])
|
||||
if kwarg
|
||||
@@ -429,8 +429,8 @@ fn to_pytest_raises_args<'a>(
|
||||
// Ex) `assertRaisesRegex(Exception, regex)`
|
||||
([arg1, arg2], []) => Cow::Owned(format!(
|
||||
"{}, match={}",
|
||||
checker.locator().slice(arg1.range()),
|
||||
checker.locator().slice(arg2.range())
|
||||
checker.locator().slice(arg1),
|
||||
checker.locator().slice(arg2)
|
||||
)),
|
||||
// Ex) `assertRaisesRegex(Exception, expected_regex=regex)`
|
||||
([arg], [kwarg])
|
||||
@@ -441,7 +441,7 @@ fn to_pytest_raises_args<'a>(
|
||||
{
|
||||
Cow::Owned(format!(
|
||||
"{}, match={}",
|
||||
checker.locator().slice(arg.range()),
|
||||
checker.locator().slice(arg),
|
||||
checker.locator().slice(kwarg.value.range())
|
||||
))
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use ruff_python_ast::stmt_if::elif_else_range;
|
||||
use ruff_python_ast::visitor::Visitor;
|
||||
use ruff_python_ast::whitespace::indentation;
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
use ruff_python_trivia::is_python_whitespace;
|
||||
|
||||
use crate::autofix::edits;
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -537,7 +538,7 @@ fn unnecessary_assign(checker: &mut Checker, stack: &Stack) {
|
||||
edits::delete_stmt(stmt, None, checker.locator(), checker.indexer());
|
||||
|
||||
// Replace the `x = 1` statement with `return 1`.
|
||||
let content = checker.locator().slice(assign.range());
|
||||
let content = checker.locator().slice(assign);
|
||||
let equals_index = content
|
||||
.find('=')
|
||||
.ok_or(anyhow::anyhow!("expected '=' in assignment statement"))?;
|
||||
@@ -549,11 +550,11 @@ fn unnecessary_assign(checker: &mut Checker, stack: &Stack) {
|
||||
if content[after_equals..]
|
||||
.chars()
|
||||
.next()
|
||||
.is_some_and(char::is_alphabetic)
|
||||
.is_some_and(is_python_whitespace)
|
||||
{
|
||||
"return ".to_string()
|
||||
} else {
|
||||
"return".to_string()
|
||||
} else {
|
||||
"return ".to_string()
|
||||
},
|
||||
// Replace from the start of the assignment statement to the end of the equals
|
||||
// sign.
|
||||
|
||||
@@ -197,5 +197,25 @@ RET504.py:359:12: RET504 [*] Unnecessary assignment to `a` before `return` state
|
||||
358 |- a = 1 # Comment
|
||||
359 |- return a
|
||||
358 |+ return 1 # Comment
|
||||
360 359 |
|
||||
361 360 |
|
||||
362 361 | # Regression test for: https://github.com/astral-sh/ruff/issues/7098
|
||||
|
||||
RET504.py:365:12: RET504 [*] Unnecessary assignment to `D` before `return` statement
|
||||
|
|
||||
363 | def mavko_debari(P_kbar):
|
||||
364 | D=0.4853881 + 3.6006116*P - 0.0117368*(P-1.3822)**2
|
||||
365 | return D
|
||||
| ^ RET504
|
||||
|
|
||||
= help: Remove unnecessary assignment
|
||||
|
||||
ℹ Suggested fix
|
||||
361 361 |
|
||||
362 362 | # Regression test for: https://github.com/astral-sh/ruff/issues/7098
|
||||
363 363 | def mavko_debari(P_kbar):
|
||||
364 |- D=0.4853881 + 3.6006116*P - 0.0117368*(P-1.3822)**2
|
||||
365 |- return D
|
||||
364 |+ return 0.4853881 + 3.6006116*P - 0.0117368*(P-1.3822)**2
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ use ruff_diagnostics::{AlwaysAutofixableViolation, AutofixKind, Diagnostic, Edit
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::comparable::ComparableExpr;
|
||||
use ruff_python_ast::helpers::{contains_effect, Truthiness};
|
||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
use ruff_python_codegen::Generator;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
@@ -692,12 +694,12 @@ pub(crate) fn expr_or_not_expr(checker: &mut Checker, expr: &Expr) {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_short_circuit_edit(
|
||||
fn get_short_circuit_edit(
|
||||
expr: &Expr,
|
||||
range: TextRange,
|
||||
truthiness: Truthiness,
|
||||
in_boolean_test: bool,
|
||||
checker: &Checker,
|
||||
generator: Generator,
|
||||
) -> Edit {
|
||||
let content = if in_boolean_test {
|
||||
match truthiness {
|
||||
@@ -708,9 +710,17 @@ pub(crate) fn get_short_circuit_edit(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
checker.generator().expr(expr)
|
||||
generator.expr(expr)
|
||||
};
|
||||
Edit::range_replacement(content, range)
|
||||
Edit::range_replacement(
|
||||
if matches!(expr, Expr::Tuple(ast::ExprTuple { elts, ctx: _, range: _}) if !elts.is_empty())
|
||||
{
|
||||
format!("({content})")
|
||||
} else {
|
||||
content
|
||||
},
|
||||
range,
|
||||
)
|
||||
}
|
||||
|
||||
fn is_short_circuit(
|
||||
@@ -734,7 +744,7 @@ fn is_short_circuit(
|
||||
BoolOp::Or => Truthiness::Truthy,
|
||||
};
|
||||
|
||||
let mut location = expr.start();
|
||||
let mut furthest = expr;
|
||||
let mut edit = None;
|
||||
let mut remove = None;
|
||||
|
||||
@@ -749,7 +759,7 @@ fn is_short_circuit(
|
||||
&& (!checker.semantic().in_boolean_test()
|
||||
|| contains_effect(value, |id| checker.semantic().is_builtin(id)))
|
||||
{
|
||||
location = next_value.start();
|
||||
furthest = next_value;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -758,17 +768,19 @@ fn is_short_circuit(
|
||||
// short-circuit expression is the first expression in the list; otherwise, we'll see it
|
||||
// as `next_value` before we see it as `value`.
|
||||
if value_truthiness == short_circuit_truthiness {
|
||||
remove = Some(if location == value.start() {
|
||||
ContentAround::After
|
||||
} else {
|
||||
ContentAround::Both
|
||||
});
|
||||
remove = Some(ContentAround::After);
|
||||
|
||||
edit = Some(get_short_circuit_edit(
|
||||
value,
|
||||
TextRange::new(location, expr.end()),
|
||||
TextRange::new(
|
||||
parenthesized_range(furthest.into(), expr.into(), checker.locator().contents())
|
||||
.unwrap_or(furthest.range())
|
||||
.start(),
|
||||
expr.end(),
|
||||
),
|
||||
short_circuit_truthiness,
|
||||
checker.semantic().in_boolean_test(),
|
||||
checker,
|
||||
checker.generator(),
|
||||
));
|
||||
break;
|
||||
}
|
||||
@@ -776,17 +788,22 @@ fn is_short_circuit(
|
||||
// If the next expression is a constant, and it matches the short-circuit value, then
|
||||
// we can return the location of the expression.
|
||||
if next_value_truthiness == short_circuit_truthiness {
|
||||
remove = Some(if index == values.len() - 2 {
|
||||
remove = Some(if index + 1 == values.len() - 1 {
|
||||
ContentAround::Before
|
||||
} else {
|
||||
ContentAround::Both
|
||||
});
|
||||
edit = Some(get_short_circuit_edit(
|
||||
next_value,
|
||||
TextRange::new(location, expr.end()),
|
||||
TextRange::new(
|
||||
parenthesized_range(furthest.into(), expr.into(), checker.locator().contents())
|
||||
.unwrap_or(furthest.range())
|
||||
.start(),
|
||||
expr.end(),
|
||||
),
|
||||
short_circuit_truthiness,
|
||||
checker.semantic().in_boolean_test(),
|
||||
checker,
|
||||
checker.generator(),
|
||||
));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use ruff_python_ast::{self as ast, Arguments, Constant, Expr};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::autofix::snippet::SourceCodeSnippet;
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, AutofixKind, Diagnostic, Edit, Fix, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::is_const_none;
|
||||
@@ -35,8 +36,8 @@ use crate::registry::AsRule;
|
||||
/// - [Python documentation: `os.environ`](https://docs.python.org/3/library/os.html#os.environ)
|
||||
#[violation]
|
||||
pub struct UncapitalizedEnvironmentVariables {
|
||||
expected: String,
|
||||
original: String,
|
||||
expected: SourceCodeSnippet,
|
||||
actual: SourceCodeSnippet,
|
||||
}
|
||||
|
||||
impl Violation for UncapitalizedEnvironmentVariables {
|
||||
@@ -44,13 +45,21 @@ impl Violation for UncapitalizedEnvironmentVariables {
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let UncapitalizedEnvironmentVariables { expected, original } = self;
|
||||
format!("Use capitalized environment variable `{expected}` instead of `{original}`")
|
||||
let UncapitalizedEnvironmentVariables { expected, actual } = self;
|
||||
if let (Some(expected), Some(actual)) = (expected.full_display(), actual.full_display()) {
|
||||
format!("Use capitalized environment variable `{expected}` instead of `{actual}`")
|
||||
} else {
|
||||
format!("Use capitalized environment variable")
|
||||
}
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> Option<String> {
|
||||
let UncapitalizedEnvironmentVariables { expected, original } = self;
|
||||
Some(format!("Replace `{original}` with `{expected}`"))
|
||||
let UncapitalizedEnvironmentVariables { expected, actual } = self;
|
||||
if let (Some(expected), Some(actual)) = (expected.full_display(), actual.full_display()) {
|
||||
Some(format!("Replace `{actual}` with `{expected}`"))
|
||||
} else {
|
||||
Some("Capitalize environment variable".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,20 +86,28 @@ impl Violation for UncapitalizedEnvironmentVariables {
|
||||
/// - [Python documentation: `dict.get`](https://docs.python.org/3/library/stdtypes.html#dict.get)
|
||||
#[violation]
|
||||
pub struct DictGetWithNoneDefault {
|
||||
expected: String,
|
||||
original: String,
|
||||
expected: SourceCodeSnippet,
|
||||
actual: SourceCodeSnippet,
|
||||
}
|
||||
|
||||
impl AlwaysAutofixableViolation for DictGetWithNoneDefault {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let DictGetWithNoneDefault { expected, original } = self;
|
||||
format!("Use `{expected}` instead of `{original}`")
|
||||
let DictGetWithNoneDefault { expected, actual } = self;
|
||||
if let (Some(expected), Some(actual)) = (expected.full_display(), actual.full_display()) {
|
||||
format!("Use `{expected}` instead of `{actual}`")
|
||||
} else {
|
||||
format!("Use `dict.get()` without default value")
|
||||
}
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
let DictGetWithNoneDefault { expected, original } = self;
|
||||
format!("Replace `{original}` with `{expected}`")
|
||||
let DictGetWithNoneDefault { expected, actual } = self;
|
||||
if let (Some(expected), Some(actual)) = (expected.full_display(), actual.full_display()) {
|
||||
format!("Replace `{actual}` with `{expected}`")
|
||||
} else {
|
||||
"Remove default value".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,8 +158,8 @@ pub(crate) fn use_capital_environment_variables(checker: &mut Checker, expr: &Ex
|
||||
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
UncapitalizedEnvironmentVariables {
|
||||
expected: capital_env_var,
|
||||
original: env_var.clone(),
|
||||
expected: SourceCodeSnippet::new(capital_env_var),
|
||||
actual: SourceCodeSnippet::new(env_var.clone()),
|
||||
},
|
||||
arg.range(),
|
||||
));
|
||||
@@ -181,8 +198,8 @@ fn check_os_environ_subscript(checker: &mut Checker, expr: &Expr) {
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
UncapitalizedEnvironmentVariables {
|
||||
expected: capital_env_var.clone(),
|
||||
original: env_var.clone(),
|
||||
expected: SourceCodeSnippet::new(capital_env_var.clone()),
|
||||
actual: SourceCodeSnippet::new(env_var.clone()),
|
||||
},
|
||||
slice.range(),
|
||||
);
|
||||
@@ -238,15 +255,15 @@ pub(crate) fn dict_get_with_none_default(checker: &mut Checker, expr: &Expr) {
|
||||
|
||||
let expected = format!(
|
||||
"{}({})",
|
||||
checker.locator().slice(func.range()),
|
||||
checker.locator().slice(key.range())
|
||||
checker.locator().slice(func.as_ref()),
|
||||
checker.locator().slice(key)
|
||||
);
|
||||
let original = checker.locator().slice(expr.range()).to_string();
|
||||
let actual = checker.locator().slice(expr);
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
DictGetWithNoneDefault {
|
||||
expected: expected.clone(),
|
||||
original,
|
||||
expected: SourceCodeSnippet::new(expected.clone()),
|
||||
actual: SourceCodeSnippet::from_str(actual),
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ use ruff_text_size::{Ranged, TextRange};
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, AutofixKind, Diagnostic, Edit, Fix, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::{is_const_false, is_const_true};
|
||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
@@ -30,7 +31,7 @@ use crate::registry::AsRule;
|
||||
/// - [Python documentation: Truth Value Testing](https://docs.python.org/3/library/stdtypes.html#truth-value-testing)
|
||||
#[violation]
|
||||
pub struct IfExprWithTrueFalse {
|
||||
expr: String,
|
||||
is_compare: bool,
|
||||
}
|
||||
|
||||
impl Violation for IfExprWithTrueFalse {
|
||||
@@ -38,13 +39,21 @@ impl Violation for IfExprWithTrueFalse {
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let IfExprWithTrueFalse { expr } = self;
|
||||
format!("Use `bool({expr})` instead of `True if {expr} else False`")
|
||||
let IfExprWithTrueFalse { is_compare } = self;
|
||||
if *is_compare {
|
||||
format!("Remove unnecessary `True if ... else False`")
|
||||
} else {
|
||||
format!("Use `bool(...)` instead of `True if ... else False`")
|
||||
}
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> Option<String> {
|
||||
let IfExprWithTrueFalse { expr } = self;
|
||||
Some(format!("Replace with `not {expr}"))
|
||||
let IfExprWithTrueFalse { is_compare } = self;
|
||||
if *is_compare {
|
||||
Some(format!("Remove unnecessary `True if ... else False`"))
|
||||
} else {
|
||||
Some(format!("Replace with `bool(...)"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,20 +79,16 @@ impl Violation for IfExprWithTrueFalse {
|
||||
/// ## References
|
||||
/// - [Python documentation: Truth Value Testing](https://docs.python.org/3/library/stdtypes.html#truth-value-testing)
|
||||
#[violation]
|
||||
pub struct IfExprWithFalseTrue {
|
||||
expr: String,
|
||||
}
|
||||
pub struct IfExprWithFalseTrue;
|
||||
|
||||
impl AlwaysAutofixableViolation for IfExprWithFalseTrue {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let IfExprWithFalseTrue { expr } = self;
|
||||
format!("Use `not {expr}` instead of `False if {expr} else True`")
|
||||
format!("Use `not ...` instead of `False if ... else True`")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
let IfExprWithFalseTrue { expr } = self;
|
||||
format!("Replace with `bool({expr})")
|
||||
format!("Replace with `not ...`")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +140,7 @@ impl AlwaysAutofixableViolation for IfExprWithTwistedArms {
|
||||
}
|
||||
|
||||
/// SIM210
|
||||
pub(crate) fn explicit_true_false_in_ifexpr(
|
||||
pub(crate) fn if_expr_with_true_false(
|
||||
checker: &mut Checker,
|
||||
expr: &Expr,
|
||||
test: &Expr,
|
||||
@@ -148,33 +153,43 @@ pub(crate) fn explicit_true_false_in_ifexpr(
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
IfExprWithTrueFalse {
|
||||
expr: checker.generator().expr(test),
|
||||
is_compare: test.is_compare_expr(),
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if test.is_compare_expr() {
|
||||
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
||||
checker.locator().slice(test.range()).to_string(),
|
||||
checker
|
||||
.locator()
|
||||
.slice(
|
||||
parenthesized_range(test.into(), expr.into(), checker.locator().contents())
|
||||
.unwrap_or(test.range()),
|
||||
)
|
||||
.to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
} else if checker.semantic().is_builtin("bool") {
|
||||
let node = ast::ExprName {
|
||||
id: "bool".into(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
};
|
||||
let node1 = ast::ExprCall {
|
||||
func: Box::new(node.into()),
|
||||
arguments: Arguments {
|
||||
args: vec![test.clone()],
|
||||
keywords: vec![],
|
||||
range: TextRange::default(),
|
||||
},
|
||||
range: TextRange::default(),
|
||||
};
|
||||
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
||||
checker.generator().expr(&node1.into()),
|
||||
checker.generator().expr(
|
||||
&ast::ExprCall {
|
||||
func: Box::new(
|
||||
ast::ExprName {
|
||||
id: "bool".into(),
|
||||
ctx: ExprContext::Load,
|
||||
range: TextRange::default(),
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
arguments: Arguments {
|
||||
args: vec![test.clone()],
|
||||
keywords: vec![],
|
||||
range: TextRange::default(),
|
||||
},
|
||||
range: TextRange::default(),
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
expr.range(),
|
||||
)));
|
||||
};
|
||||
@@ -183,7 +198,7 @@ pub(crate) fn explicit_true_false_in_ifexpr(
|
||||
}
|
||||
|
||||
/// SIM211
|
||||
pub(crate) fn explicit_false_true_in_ifexpr(
|
||||
pub(crate) fn if_expr_with_false_true(
|
||||
checker: &mut Checker,
|
||||
expr: &Expr,
|
||||
test: &Expr,
|
||||
@@ -194,21 +209,17 @@ pub(crate) fn explicit_false_true_in_ifexpr(
|
||||
return;
|
||||
}
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
IfExprWithFalseTrue {
|
||||
expr: checker.generator().expr(test),
|
||||
},
|
||||
expr.range(),
|
||||
);
|
||||
let mut diagnostic = Diagnostic::new(IfExprWithFalseTrue, expr.range());
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let node = test.clone();
|
||||
let node1 = ast::ExprUnaryOp {
|
||||
op: UnaryOp::Not,
|
||||
operand: Box::new(node),
|
||||
range: TextRange::default(),
|
||||
};
|
||||
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
||||
checker.generator().expr(&node1.into()),
|
||||
checker.generator().expr(
|
||||
&ast::ExprUnaryOp {
|
||||
op: UnaryOp::Not,
|
||||
operand: Box::new(test.clone()),
|
||||
range: TextRange::default(),
|
||||
}
|
||||
.into(),
|
||||
),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ pub(crate) fn double_negation(checker: &mut Checker, expr: &Expr, op: UnaryOp, o
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
if checker.semantic().in_boolean_test() {
|
||||
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
||||
checker.locator().slice(operand.range()).to_string(),
|
||||
checker.locator().slice(operand.as_ref()).to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
} else if checker.semantic().is_builtin("bool") {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_simplify/mod.rs
|
||||
---
|
||||
SIM210.py:1:5: SIM210 [*] Use `bool(b)` instead of `True if b else False`
|
||||
SIM210.py:1:5: SIM210 [*] Use `bool(...)` instead of `True if ... else False`
|
||||
|
|
||||
1 | a = True if b else False # SIM210
|
||||
| ^^^^^^^^^^^^^^^^^^^^ SIM210
|
||||
2 |
|
||||
3 | a = True if b != c else False # SIM210
|
||||
|
|
||||
= help: Replace with `not b
|
||||
= help: Replace with `bool(...)
|
||||
|
||||
ℹ Suggested fix
|
||||
1 |-a = True if b else False # SIM210
|
||||
@@ -17,7 +17,7 @@ SIM210.py:1:5: SIM210 [*] Use `bool(b)` instead of `True if b else False`
|
||||
3 3 | a = True if b != c else False # SIM210
|
||||
4 4 |
|
||||
|
||||
SIM210.py:3:5: SIM210 [*] Use `bool(b != c)` instead of `True if b != c else False`
|
||||
SIM210.py:3:5: SIM210 [*] Remove unnecessary `True if ... else False`
|
||||
|
|
||||
1 | a = True if b else False # SIM210
|
||||
2 |
|
||||
@@ -26,7 +26,7 @@ SIM210.py:3:5: SIM210 [*] Use `bool(b != c)` instead of `True if b != c else Fal
|
||||
4 |
|
||||
5 | a = True if b + c else False # SIM210
|
||||
|
|
||||
= help: Replace with `not b != c
|
||||
= help: Remove unnecessary `True if ... else False`
|
||||
|
||||
ℹ Suggested fix
|
||||
1 1 | a = True if b else False # SIM210
|
||||
@@ -37,7 +37,7 @@ SIM210.py:3:5: SIM210 [*] Use `bool(b != c)` instead of `True if b != c else Fal
|
||||
5 5 | a = True if b + c else False # SIM210
|
||||
6 6 |
|
||||
|
||||
SIM210.py:5:5: SIM210 [*] Use `bool(b + c)` instead of `True if b + c else False`
|
||||
SIM210.py:5:5: SIM210 [*] Use `bool(...)` instead of `True if ... else False`
|
||||
|
|
||||
3 | a = True if b != c else False # SIM210
|
||||
4 |
|
||||
@@ -46,7 +46,7 @@ SIM210.py:5:5: SIM210 [*] Use `bool(b + c)` instead of `True if b + c else False
|
||||
6 |
|
||||
7 | a = False if b else True # OK
|
||||
|
|
||||
= help: Replace with `not b + c
|
||||
= help: Replace with `bool(...)
|
||||
|
||||
ℹ Suggested fix
|
||||
2 2 |
|
||||
@@ -58,13 +58,32 @@ SIM210.py:5:5: SIM210 [*] Use `bool(b + c)` instead of `True if b + c else False
|
||||
7 7 | a = False if b else True # OK
|
||||
8 8 |
|
||||
|
||||
SIM210.py:15:9: SIM210 Use `bool(b)` instead of `True if b else False`
|
||||
SIM210.py:15:9: SIM210 Use `bool(...)` instead of `True if ... else False`
|
||||
|
|
||||
13 | return False
|
||||
14 |
|
||||
15 | a = True if b else False
|
||||
| ^^^^^^^^^^^^^^^^^^^^ SIM210
|
||||
|
|
||||
= help: Replace with `not b
|
||||
= help: Replace with `bool(...)
|
||||
|
||||
SIM210.py:19:11: SIM210 [*] Remove unnecessary `True if ... else False`
|
||||
|
|
||||
18 | # Regression test for: https://github.com/astral-sh/ruff/issues/7076
|
||||
19 | samesld = True if (psl.privatesuffix(urlparse(response.url).netloc) ==
|
||||
| ___________^
|
||||
20 | | psl.privatesuffix(src.netloc)) else False
|
||||
| |____________________________________________________________________________^ SIM210
|
||||
|
|
||||
= help: Remove unnecessary `True if ... else False`
|
||||
|
||||
ℹ Suggested fix
|
||||
16 16 |
|
||||
17 17 |
|
||||
18 18 | # Regression test for: https://github.com/astral-sh/ruff/issues/7076
|
||||
19 |-samesld = True if (psl.privatesuffix(urlparse(response.url).netloc) ==
|
||||
20 |- psl.privatesuffix(src.netloc)) else False
|
||||
19 |+samesld = (psl.privatesuffix(urlparse(response.url).netloc) ==
|
||||
20 |+ psl.privatesuffix(src.netloc))
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_simplify/mod.rs
|
||||
---
|
||||
SIM211.py:1:5: SIM211 [*] Use `not b` instead of `False if b else True`
|
||||
SIM211.py:1:5: SIM211 [*] Use `not ...` instead of `False if ... else True`
|
||||
|
|
||||
1 | a = False if b else True # SIM211
|
||||
| ^^^^^^^^^^^^^^^^^^^^ SIM211
|
||||
2 |
|
||||
3 | a = False if b != c else True # SIM211
|
||||
|
|
||||
= help: Replace with `bool(b)
|
||||
= help: Replace with `not ...`
|
||||
|
||||
ℹ Suggested fix
|
||||
1 |-a = False if b else True # SIM211
|
||||
@@ -17,7 +17,7 @@ SIM211.py:1:5: SIM211 [*] Use `not b` instead of `False if b else True`
|
||||
3 3 | a = False if b != c else True # SIM211
|
||||
4 4 |
|
||||
|
||||
SIM211.py:3:5: SIM211 [*] Use `not b != c` instead of `False if b != c else True`
|
||||
SIM211.py:3:5: SIM211 [*] Use `not ...` instead of `False if ... else True`
|
||||
|
|
||||
1 | a = False if b else True # SIM211
|
||||
2 |
|
||||
@@ -26,7 +26,7 @@ SIM211.py:3:5: SIM211 [*] Use `not b != c` instead of `False if b != c else True
|
||||
4 |
|
||||
5 | a = False if b + c else True # SIM211
|
||||
|
|
||||
= help: Replace with `bool(b != c)
|
||||
= help: Replace with `not ...`
|
||||
|
||||
ℹ Suggested fix
|
||||
1 1 | a = False if b else True # SIM211
|
||||
@@ -37,7 +37,7 @@ SIM211.py:3:5: SIM211 [*] Use `not b != c` instead of `False if b != c else True
|
||||
5 5 | a = False if b + c else True # SIM211
|
||||
6 6 |
|
||||
|
||||
SIM211.py:5:5: SIM211 [*] Use `not b + c` instead of `False if b + c else True`
|
||||
SIM211.py:5:5: SIM211 [*] Use `not ...` instead of `False if ... else True`
|
||||
|
|
||||
3 | a = False if b != c else True # SIM211
|
||||
4 |
|
||||
@@ -46,7 +46,7 @@ SIM211.py:5:5: SIM211 [*] Use `not b + c` instead of `False if b + c else True`
|
||||
6 |
|
||||
7 | a = True if b else False # OK
|
||||
|
|
||||
= help: Replace with `bool(b + c)
|
||||
= help: Replace with `not ...`
|
||||
|
||||
ℹ Suggested fix
|
||||
2 2 |
|
||||
|
||||
@@ -534,7 +534,7 @@ SIM222.py:85:6: SIM222 [*] Use `True` instead of `... or True`
|
||||
87 87 | a or (1,) or True or (2,) # SIM222
|
||||
88 88 |
|
||||
|
||||
SIM222.py:87:6: SIM222 [*] Use `1,` instead of `1, or ...`
|
||||
SIM222.py:87:6: SIM222 [*] Use `(1,)` instead of `(1,) or ...`
|
||||
|
|
||||
85 | a or tuple(()) or True # SIM222
|
||||
86 |
|
||||
@@ -543,14 +543,14 @@ SIM222.py:87:6: SIM222 [*] Use `1,` instead of `1, or ...`
|
||||
88 |
|
||||
89 | a or tuple((1,)) or True or tuple((2,)) # SIM222
|
||||
|
|
||||
= help: Replace with `1,`
|
||||
= help: Replace with `(1,)`
|
||||
|
||||
ℹ Suggested fix
|
||||
84 84 |
|
||||
85 85 | a or tuple(()) or True # SIM222
|
||||
86 86 |
|
||||
87 |-a or (1,) or True or (2,) # SIM222
|
||||
87 |+a or 1, # SIM222
|
||||
87 |+a or (1,) # SIM222
|
||||
88 88 |
|
||||
89 89 | a or tuple((1,)) or True or tuple((2,)) # SIM222
|
||||
90 90 |
|
||||
@@ -1003,5 +1003,42 @@ SIM222.py:153:11: SIM222 [*] Use `[1]` instead of `[1] or ...`
|
||||
153 |-if f(a or [1] or True or [2]): # SIM222
|
||||
153 |+if f(a or [1]): # SIM222
|
||||
154 154 | pass
|
||||
155 155 |
|
||||
156 156 | # Regression test for: https://github.com/astral-sh/ruff/issues/7099
|
||||
|
||||
SIM222.py:157:30: SIM222 [*] Use `(int, int, int)` instead of `(int, int, int) or ...`
|
||||
|
|
||||
156 | # Regression test for: https://github.com/astral-sh/ruff/issues/7099
|
||||
157 | def secondToTime(s0: int) -> (int, int, int) or str:
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ SIM222
|
||||
158 | m, s = divmod(s0, 60)
|
||||
|
|
||||
= help: Replace with `(int, int, int)`
|
||||
|
||||
ℹ Suggested fix
|
||||
154 154 | pass
|
||||
155 155 |
|
||||
156 156 | # Regression test for: https://github.com/astral-sh/ruff/issues/7099
|
||||
157 |-def secondToTime(s0: int) -> (int, int, int) or str:
|
||||
157 |+def secondToTime(s0: int) -> (int, int, int):
|
||||
158 158 | m, s = divmod(s0, 60)
|
||||
159 159 |
|
||||
160 160 |
|
||||
|
||||
SIM222.py:161:31: SIM222 [*] Use `(int, int, int)` instead of `(int, int, int) or ...`
|
||||
|
|
||||
161 | def secondToTime(s0: int) -> ((int, int, int) or str):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ SIM222
|
||||
162 | m, s = divmod(s0, 60)
|
||||
|
|
||||
= help: Replace with `(int, int, int)`
|
||||
|
||||
ℹ Suggested fix
|
||||
158 158 | m, s = divmod(s0, 60)
|
||||
159 159 |
|
||||
160 160 |
|
||||
161 |-def secondToTime(s0: int) -> ((int, int, int) or str):
|
||||
161 |+def secondToTime(s0: int) -> ((int, int, int)):
|
||||
162 162 | m, s = divmod(s0, 60)
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use ruff_python_ast::{self as ast, ElifElseClause, ExceptHandler, MatchCase, Stmt};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
use std::iter::Peekable;
|
||||
use std::slice;
|
||||
|
||||
use ruff_notebook::Notebook;
|
||||
use ruff_python_ast::statement_visitor::StatementVisitor;
|
||||
use ruff_python_ast::{self as ast, ElifElseClause, ExceptHandler, MatchCase, Stmt};
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::directives::IsortDirectives;
|
||||
use crate::jupyter::Notebook;
|
||||
use crate::rules::isort::helpers;
|
||||
use crate::source_kind::SourceKind;
|
||||
|
||||
@@ -43,7 +43,7 @@ impl<'a> BlockBuilder<'a> {
|
||||
locator: &'a Locator<'a>,
|
||||
directives: &'a IsortDirectives,
|
||||
is_stub: bool,
|
||||
source_kind: Option<&'a SourceKind>,
|
||||
source_kind: &'a SourceKind,
|
||||
) -> Self {
|
||||
Self {
|
||||
locator,
|
||||
@@ -53,7 +53,7 @@ impl<'a> BlockBuilder<'a> {
|
||||
exclusions: &directives.exclusions,
|
||||
nested: false,
|
||||
cell_offsets: source_kind
|
||||
.and_then(SourceKind::notebook)
|
||||
.as_ipy_notebook()
|
||||
.map(Notebook::cell_offsets)
|
||||
.map(|offsets| offsets.iter().peekable()),
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pub(super) fn trailing_comma(
|
||||
locator: &Locator,
|
||||
source_type: PySourceType,
|
||||
) -> TrailingComma {
|
||||
let contents = locator.slice(stmt.range());
|
||||
let contents = locator.slice(stmt);
|
||||
let mut count = 0u32;
|
||||
let mut trailing_comma = TrailingComma::Absent;
|
||||
for (tok, _) in lexer::lex_starts_at(contents, source_type.as_mode(), stmt.start()).flatten() {
|
||||
|
||||
@@ -52,5 +52,6 @@ pub mod pyflakes;
|
||||
pub mod pygrep_hooks;
|
||||
pub mod pylint;
|
||||
pub mod pyupgrade;
|
||||
pub mod refurb;
|
||||
pub mod ruff;
|
||||
pub mod tryceratops;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
|
||||
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::Expr;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::importer::ImportRequest;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
@@ -77,21 +77,15 @@ pub(crate) fn deprecated_function(checker: &mut Checker, expr: &Expr) {
|
||||
expr.range(),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
match expr {
|
||||
Expr::Name(_) => {
|
||||
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
||||
replacement.to_string(),
|
||||
expr.range(),
|
||||
)));
|
||||
}
|
||||
Expr::Attribute(ast::ExprAttribute { attr, .. }) => {
|
||||
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
|
||||
replacement.to_string(),
|
||||
attr.range(),
|
||||
)));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import_from("numpy", replacement),
|
||||
expr.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let replacement_edit = Edit::range_replacement(binding, expr.range());
|
||||
Ok(Fix::suggested_edits(import_edit, [replacement_edit]))
|
||||
});
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -1,201 +1,225 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/numpy/mod.rs
|
||||
---
|
||||
NPY003.py:3:1: NPY003 [*] `np.round_` is deprecated; use `np.round` instead
|
||||
NPY003.py:4:5: NPY003 [*] `np.round_` is deprecated; use `np.round` instead
|
||||
|
|
||||
1 | import numpy as np
|
||||
2 |
|
||||
3 | np.round_(np.random.rand(5, 5), 2)
|
||||
| ^^^^^^^^^ NPY003
|
||||
4 | np.product(np.random.rand(5, 5))
|
||||
5 | np.cumproduct(np.random.rand(5, 5))
|
||||
2 | import numpy as np
|
||||
3 |
|
||||
4 | np.round_(np.random.rand(5, 5), 2)
|
||||
| ^^^^^^^^^ NPY003
|
||||
5 | np.product(np.random.rand(5, 5))
|
||||
6 | np.cumproduct(np.random.rand(5, 5))
|
||||
|
|
||||
= help: Replace with `np.round`
|
||||
|
||||
ℹ Suggested fix
|
||||
1 1 | import numpy as np
|
||||
2 2 |
|
||||
3 |-np.round_(np.random.rand(5, 5), 2)
|
||||
3 |+np.round(np.random.rand(5, 5), 2)
|
||||
4 4 | np.product(np.random.rand(5, 5))
|
||||
5 5 | np.cumproduct(np.random.rand(5, 5))
|
||||
6 6 | np.sometrue(np.random.rand(5, 5))
|
||||
1 1 | def func():
|
||||
2 2 | import numpy as np
|
||||
3 3 |
|
||||
4 |- np.round_(np.random.rand(5, 5), 2)
|
||||
4 |+ np.round(np.random.rand(5, 5), 2)
|
||||
5 5 | np.product(np.random.rand(5, 5))
|
||||
6 6 | np.cumproduct(np.random.rand(5, 5))
|
||||
7 7 | np.sometrue(np.random.rand(5, 5))
|
||||
|
||||
NPY003.py:4:1: NPY003 [*] `np.product` is deprecated; use `np.prod` instead
|
||||
NPY003.py:5:5: NPY003 [*] `np.product` is deprecated; use `np.prod` instead
|
||||
|
|
||||
3 | np.round_(np.random.rand(5, 5), 2)
|
||||
4 | np.product(np.random.rand(5, 5))
|
||||
| ^^^^^^^^^^ NPY003
|
||||
5 | np.cumproduct(np.random.rand(5, 5))
|
||||
6 | np.sometrue(np.random.rand(5, 5))
|
||||
4 | np.round_(np.random.rand(5, 5), 2)
|
||||
5 | np.product(np.random.rand(5, 5))
|
||||
| ^^^^^^^^^^ NPY003
|
||||
6 | np.cumproduct(np.random.rand(5, 5))
|
||||
7 | np.sometrue(np.random.rand(5, 5))
|
||||
|
|
||||
= help: Replace with `np.prod`
|
||||
|
||||
ℹ Suggested fix
|
||||
1 1 | import numpy as np
|
||||
2 2 |
|
||||
3 3 | np.round_(np.random.rand(5, 5), 2)
|
||||
4 |-np.product(np.random.rand(5, 5))
|
||||
4 |+np.prod(np.random.rand(5, 5))
|
||||
5 5 | np.cumproduct(np.random.rand(5, 5))
|
||||
6 6 | np.sometrue(np.random.rand(5, 5))
|
||||
7 7 | np.alltrue(np.random.rand(5, 5))
|
||||
2 2 | import numpy as np
|
||||
3 3 |
|
||||
4 4 | np.round_(np.random.rand(5, 5), 2)
|
||||
5 |- np.product(np.random.rand(5, 5))
|
||||
5 |+ np.prod(np.random.rand(5, 5))
|
||||
6 6 | np.cumproduct(np.random.rand(5, 5))
|
||||
7 7 | np.sometrue(np.random.rand(5, 5))
|
||||
8 8 | np.alltrue(np.random.rand(5, 5))
|
||||
|
||||
NPY003.py:5:1: NPY003 [*] `np.cumproduct` is deprecated; use `np.cumprod` instead
|
||||
NPY003.py:6:5: NPY003 [*] `np.cumproduct` is deprecated; use `np.cumprod` instead
|
||||
|
|
||||
3 | np.round_(np.random.rand(5, 5), 2)
|
||||
4 | np.product(np.random.rand(5, 5))
|
||||
5 | np.cumproduct(np.random.rand(5, 5))
|
||||
| ^^^^^^^^^^^^^ NPY003
|
||||
6 | np.sometrue(np.random.rand(5, 5))
|
||||
7 | np.alltrue(np.random.rand(5, 5))
|
||||
4 | np.round_(np.random.rand(5, 5), 2)
|
||||
5 | np.product(np.random.rand(5, 5))
|
||||
6 | np.cumproduct(np.random.rand(5, 5))
|
||||
| ^^^^^^^^^^^^^ NPY003
|
||||
7 | np.sometrue(np.random.rand(5, 5))
|
||||
8 | np.alltrue(np.random.rand(5, 5))
|
||||
|
|
||||
= help: Replace with `np.cumprod`
|
||||
|
||||
ℹ Suggested fix
|
||||
2 2 |
|
||||
3 3 | np.round_(np.random.rand(5, 5), 2)
|
||||
4 4 | np.product(np.random.rand(5, 5))
|
||||
5 |-np.cumproduct(np.random.rand(5, 5))
|
||||
5 |+np.cumprod(np.random.rand(5, 5))
|
||||
6 6 | np.sometrue(np.random.rand(5, 5))
|
||||
7 7 | np.alltrue(np.random.rand(5, 5))
|
||||
8 8 |
|
||||
3 3 |
|
||||
4 4 | np.round_(np.random.rand(5, 5), 2)
|
||||
5 5 | np.product(np.random.rand(5, 5))
|
||||
6 |- np.cumproduct(np.random.rand(5, 5))
|
||||
6 |+ np.cumprod(np.random.rand(5, 5))
|
||||
7 7 | np.sometrue(np.random.rand(5, 5))
|
||||
8 8 | np.alltrue(np.random.rand(5, 5))
|
||||
9 9 |
|
||||
|
||||
NPY003.py:6:1: NPY003 [*] `np.sometrue` is deprecated; use `np.any` instead
|
||||
NPY003.py:7:5: NPY003 [*] `np.sometrue` is deprecated; use `np.any` instead
|
||||
|
|
||||
4 | np.product(np.random.rand(5, 5))
|
||||
5 | np.cumproduct(np.random.rand(5, 5))
|
||||
6 | np.sometrue(np.random.rand(5, 5))
|
||||
| ^^^^^^^^^^^ NPY003
|
||||
7 | np.alltrue(np.random.rand(5, 5))
|
||||
5 | np.product(np.random.rand(5, 5))
|
||||
6 | np.cumproduct(np.random.rand(5, 5))
|
||||
7 | np.sometrue(np.random.rand(5, 5))
|
||||
| ^^^^^^^^^^^ NPY003
|
||||
8 | np.alltrue(np.random.rand(5, 5))
|
||||
|
|
||||
= help: Replace with `np.any`
|
||||
|
||||
ℹ Suggested fix
|
||||
3 3 | np.round_(np.random.rand(5, 5), 2)
|
||||
4 4 | np.product(np.random.rand(5, 5))
|
||||
5 5 | np.cumproduct(np.random.rand(5, 5))
|
||||
6 |-np.sometrue(np.random.rand(5, 5))
|
||||
6 |+np.any(np.random.rand(5, 5))
|
||||
7 7 | np.alltrue(np.random.rand(5, 5))
|
||||
8 8 |
|
||||
9 9 | from numpy import round_, product, cumproduct, sometrue, alltrue
|
||||
4 4 | np.round_(np.random.rand(5, 5), 2)
|
||||
5 5 | np.product(np.random.rand(5, 5))
|
||||
6 6 | np.cumproduct(np.random.rand(5, 5))
|
||||
7 |- np.sometrue(np.random.rand(5, 5))
|
||||
7 |+ np.any(np.random.rand(5, 5))
|
||||
8 8 | np.alltrue(np.random.rand(5, 5))
|
||||
9 9 |
|
||||
10 10 |
|
||||
|
||||
NPY003.py:7:1: NPY003 [*] `np.alltrue` is deprecated; use `np.all` instead
|
||||
NPY003.py:8:5: NPY003 [*] `np.alltrue` is deprecated; use `np.all` instead
|
||||
|
|
||||
5 | np.cumproduct(np.random.rand(5, 5))
|
||||
6 | np.sometrue(np.random.rand(5, 5))
|
||||
7 | np.alltrue(np.random.rand(5, 5))
|
||||
| ^^^^^^^^^^ NPY003
|
||||
8 |
|
||||
9 | from numpy import round_, product, cumproduct, sometrue, alltrue
|
||||
6 | np.cumproduct(np.random.rand(5, 5))
|
||||
7 | np.sometrue(np.random.rand(5, 5))
|
||||
8 | np.alltrue(np.random.rand(5, 5))
|
||||
| ^^^^^^^^^^ NPY003
|
||||
|
|
||||
= help: Replace with `np.all`
|
||||
|
||||
ℹ Suggested fix
|
||||
4 4 | np.product(np.random.rand(5, 5))
|
||||
5 5 | np.cumproduct(np.random.rand(5, 5))
|
||||
6 6 | np.sometrue(np.random.rand(5, 5))
|
||||
7 |-np.alltrue(np.random.rand(5, 5))
|
||||
7 |+np.all(np.random.rand(5, 5))
|
||||
8 8 |
|
||||
9 9 | from numpy import round_, product, cumproduct, sometrue, alltrue
|
||||
5 5 | np.product(np.random.rand(5, 5))
|
||||
6 6 | np.cumproduct(np.random.rand(5, 5))
|
||||
7 7 | np.sometrue(np.random.rand(5, 5))
|
||||
8 |- np.alltrue(np.random.rand(5, 5))
|
||||
8 |+ np.all(np.random.rand(5, 5))
|
||||
9 9 |
|
||||
10 10 |
|
||||
11 11 | def func():
|
||||
|
||||
NPY003.py:11:1: NPY003 [*] `np.round_` is deprecated; use `np.round` instead
|
||||
NPY003.py:14:5: NPY003 [*] `np.round_` is deprecated; use `np.round` instead
|
||||
|
|
||||
9 | from numpy import round_, product, cumproduct, sometrue, alltrue
|
||||
10 |
|
||||
11 | round_(np.random.rand(5, 5), 2)
|
||||
| ^^^^^^ NPY003
|
||||
12 | product(np.random.rand(5, 5))
|
||||
13 | cumproduct(np.random.rand(5, 5))
|
||||
12 | from numpy import round_, product, cumproduct, sometrue, alltrue
|
||||
13 |
|
||||
14 | round_(np.random.rand(5, 5), 2)
|
||||
| ^^^^^^ NPY003
|
||||
15 | product(np.random.rand(5, 5))
|
||||
16 | cumproduct(np.random.rand(5, 5))
|
||||
|
|
||||
= help: Replace with `np.round`
|
||||
|
||||
ℹ Suggested fix
|
||||
8 8 |
|
||||
9 9 | from numpy import round_, product, cumproduct, sometrue, alltrue
|
||||
10 10 |
|
||||
11 |-round_(np.random.rand(5, 5), 2)
|
||||
11 |+round(np.random.rand(5, 5), 2)
|
||||
12 12 | product(np.random.rand(5, 5))
|
||||
13 13 | cumproduct(np.random.rand(5, 5))
|
||||
14 14 | sometrue(np.random.rand(5, 5))
|
||||
1 |+from numpy import round
|
||||
1 2 | def func():
|
||||
2 3 | import numpy as np
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
11 12 | def func():
|
||||
12 13 | from numpy import round_, product, cumproduct, sometrue, alltrue
|
||||
13 14 |
|
||||
14 |- round_(np.random.rand(5, 5), 2)
|
||||
15 |+ round(np.random.rand(5, 5), 2)
|
||||
15 16 | product(np.random.rand(5, 5))
|
||||
16 17 | cumproduct(np.random.rand(5, 5))
|
||||
17 18 | sometrue(np.random.rand(5, 5))
|
||||
|
||||
NPY003.py:12:1: NPY003 [*] `np.product` is deprecated; use `np.prod` instead
|
||||
NPY003.py:15:5: NPY003 [*] `np.product` is deprecated; use `np.prod` instead
|
||||
|
|
||||
11 | round_(np.random.rand(5, 5), 2)
|
||||
12 | product(np.random.rand(5, 5))
|
||||
| ^^^^^^^ NPY003
|
||||
13 | cumproduct(np.random.rand(5, 5))
|
||||
14 | sometrue(np.random.rand(5, 5))
|
||||
14 | round_(np.random.rand(5, 5), 2)
|
||||
15 | product(np.random.rand(5, 5))
|
||||
| ^^^^^^^ NPY003
|
||||
16 | cumproduct(np.random.rand(5, 5))
|
||||
17 | sometrue(np.random.rand(5, 5))
|
||||
|
|
||||
= help: Replace with `np.prod`
|
||||
|
||||
ℹ Suggested fix
|
||||
9 9 | from numpy import round_, product, cumproduct, sometrue, alltrue
|
||||
10 10 |
|
||||
11 11 | round_(np.random.rand(5, 5), 2)
|
||||
12 |-product(np.random.rand(5, 5))
|
||||
12 |+prod(np.random.rand(5, 5))
|
||||
13 13 | cumproduct(np.random.rand(5, 5))
|
||||
14 14 | sometrue(np.random.rand(5, 5))
|
||||
15 15 | alltrue(np.random.rand(5, 5))
|
||||
1 |+from numpy import prod
|
||||
1 2 | def func():
|
||||
2 3 | import numpy as np
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
12 13 | from numpy import round_, product, cumproduct, sometrue, alltrue
|
||||
13 14 |
|
||||
14 15 | round_(np.random.rand(5, 5), 2)
|
||||
15 |- product(np.random.rand(5, 5))
|
||||
16 |+ prod(np.random.rand(5, 5))
|
||||
16 17 | cumproduct(np.random.rand(5, 5))
|
||||
17 18 | sometrue(np.random.rand(5, 5))
|
||||
18 19 | alltrue(np.random.rand(5, 5))
|
||||
|
||||
NPY003.py:13:1: NPY003 [*] `np.cumproduct` is deprecated; use `np.cumprod` instead
|
||||
NPY003.py:16:5: NPY003 [*] `np.cumproduct` is deprecated; use `np.cumprod` instead
|
||||
|
|
||||
11 | round_(np.random.rand(5, 5), 2)
|
||||
12 | product(np.random.rand(5, 5))
|
||||
13 | cumproduct(np.random.rand(5, 5))
|
||||
| ^^^^^^^^^^ NPY003
|
||||
14 | sometrue(np.random.rand(5, 5))
|
||||
15 | alltrue(np.random.rand(5, 5))
|
||||
14 | round_(np.random.rand(5, 5), 2)
|
||||
15 | product(np.random.rand(5, 5))
|
||||
16 | cumproduct(np.random.rand(5, 5))
|
||||
| ^^^^^^^^^^ NPY003
|
||||
17 | sometrue(np.random.rand(5, 5))
|
||||
18 | alltrue(np.random.rand(5, 5))
|
||||
|
|
||||
= help: Replace with `np.cumprod`
|
||||
|
||||
ℹ Suggested fix
|
||||
10 10 |
|
||||
11 11 | round_(np.random.rand(5, 5), 2)
|
||||
12 12 | product(np.random.rand(5, 5))
|
||||
13 |-cumproduct(np.random.rand(5, 5))
|
||||
13 |+cumprod(np.random.rand(5, 5))
|
||||
14 14 | sometrue(np.random.rand(5, 5))
|
||||
15 15 | alltrue(np.random.rand(5, 5))
|
||||
1 |+from numpy import cumprod
|
||||
1 2 | def func():
|
||||
2 3 | import numpy as np
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
13 14 |
|
||||
14 15 | round_(np.random.rand(5, 5), 2)
|
||||
15 16 | product(np.random.rand(5, 5))
|
||||
16 |- cumproduct(np.random.rand(5, 5))
|
||||
17 |+ cumprod(np.random.rand(5, 5))
|
||||
17 18 | sometrue(np.random.rand(5, 5))
|
||||
18 19 | alltrue(np.random.rand(5, 5))
|
||||
|
||||
NPY003.py:14:1: NPY003 [*] `np.sometrue` is deprecated; use `np.any` instead
|
||||
NPY003.py:17:5: NPY003 [*] `np.sometrue` is deprecated; use `np.any` instead
|
||||
|
|
||||
12 | product(np.random.rand(5, 5))
|
||||
13 | cumproduct(np.random.rand(5, 5))
|
||||
14 | sometrue(np.random.rand(5, 5))
|
||||
| ^^^^^^^^ NPY003
|
||||
15 | alltrue(np.random.rand(5, 5))
|
||||
15 | product(np.random.rand(5, 5))
|
||||
16 | cumproduct(np.random.rand(5, 5))
|
||||
17 | sometrue(np.random.rand(5, 5))
|
||||
| ^^^^^^^^ NPY003
|
||||
18 | alltrue(np.random.rand(5, 5))
|
||||
|
|
||||
= help: Replace with `np.any`
|
||||
|
||||
ℹ Suggested fix
|
||||
11 11 | round_(np.random.rand(5, 5), 2)
|
||||
12 12 | product(np.random.rand(5, 5))
|
||||
13 13 | cumproduct(np.random.rand(5, 5))
|
||||
14 |-sometrue(np.random.rand(5, 5))
|
||||
14 |+any(np.random.rand(5, 5))
|
||||
15 15 | alltrue(np.random.rand(5, 5))
|
||||
1 |+from numpy import any
|
||||
1 2 | def func():
|
||||
2 3 | import numpy as np
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
14 15 | round_(np.random.rand(5, 5), 2)
|
||||
15 16 | product(np.random.rand(5, 5))
|
||||
16 17 | cumproduct(np.random.rand(5, 5))
|
||||
17 |- sometrue(np.random.rand(5, 5))
|
||||
18 |+ any(np.random.rand(5, 5))
|
||||
18 19 | alltrue(np.random.rand(5, 5))
|
||||
|
||||
NPY003.py:15:1: NPY003 [*] `np.alltrue` is deprecated; use `np.all` instead
|
||||
NPY003.py:18:5: NPY003 [*] `np.alltrue` is deprecated; use `np.all` instead
|
||||
|
|
||||
13 | cumproduct(np.random.rand(5, 5))
|
||||
14 | sometrue(np.random.rand(5, 5))
|
||||
15 | alltrue(np.random.rand(5, 5))
|
||||
| ^^^^^^^ NPY003
|
||||
16 | cumproduct(np.random.rand(5, 5))
|
||||
17 | sometrue(np.random.rand(5, 5))
|
||||
18 | alltrue(np.random.rand(5, 5))
|
||||
| ^^^^^^^ NPY003
|
||||
|
|
||||
= help: Replace with `np.all`
|
||||
|
||||
ℹ Suggested fix
|
||||
12 12 | product(np.random.rand(5, 5))
|
||||
13 13 | cumproduct(np.random.rand(5, 5))
|
||||
14 14 | sometrue(np.random.rand(5, 5))
|
||||
15 |-alltrue(np.random.rand(5, 5))
|
||||
15 |+all(np.random.rand(5, 5))
|
||||
1 |+from numpy import all
|
||||
1 2 | def func():
|
||||
2 3 | import numpy as np
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
15 16 | product(np.random.rand(5, 5))
|
||||
16 17 | cumproduct(np.random.rand(5, 5))
|
||||
17 18 | sometrue(np.random.rand(5, 5))
|
||||
18 |- alltrue(np.random.rand(5, 5))
|
||||
19 |+ all(np.random.rand(5, 5))
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::is_const_true;
|
||||
use ruff_python_ast::{self as ast, Keyword};
|
||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
use ruff_python_ast::{self as ast, Keyword, Stmt};
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
@@ -75,13 +76,17 @@ pub(crate) fn inplace_argument(checker: &mut Checker, call: &ast::ExprCall) {
|
||||
// the star argument _doesn't_ contain an override).
|
||||
// 2. The call is part of a larger expression (we're converting an expression to a
|
||||
// statement, and expressions can't contain statements).
|
||||
let statement = checker.semantic().current_statement();
|
||||
if !seen_star
|
||||
&& checker.semantic().current_statement().is_expr_stmt()
|
||||
&& checker.semantic().current_expression_parent().is_none()
|
||||
&& statement.is_expr_stmt()
|
||||
{
|
||||
if let Some(fix) =
|
||||
convert_inplace_argument_to_assignment(call, keyword, checker.locator())
|
||||
{
|
||||
if let Some(fix) = convert_inplace_argument_to_assignment(
|
||||
call,
|
||||
keyword,
|
||||
statement,
|
||||
checker.locator(),
|
||||
) {
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
}
|
||||
@@ -101,13 +106,16 @@ pub(crate) fn inplace_argument(checker: &mut Checker, call: &ast::ExprCall) {
|
||||
fn convert_inplace_argument_to_assignment(
|
||||
call: &ast::ExprCall,
|
||||
keyword: &Keyword,
|
||||
statement: &Stmt,
|
||||
locator: &Locator,
|
||||
) -> Option<Fix> {
|
||||
// Add the assignment.
|
||||
let attr = call.func.as_attribute_expr()?;
|
||||
let insert_assignment = Edit::insertion(
|
||||
format!("{name} = ", name = locator.slice(attr.value.range())),
|
||||
call.start(),
|
||||
parenthesized_range(call.into(), statement.into(), locator.contents())
|
||||
.unwrap_or(call.range())
|
||||
.start(),
|
||||
);
|
||||
|
||||
// Remove the `inplace` argument.
|
||||
|
||||
@@ -158,4 +158,20 @@ PD002.py:28:38: PD002 `inplace=True` should be avoided; it has inconsistent beha
|
||||
|
|
||||
= help: Assign to variable; remove `inplace` arg
|
||||
|
||||
PD002.py:33:24: PD002 [*] `inplace=True` should be avoided; it has inconsistent behavior
|
||||
|
|
||||
31 | torch.m.ReLU(inplace=True) # safe because this isn't a pandas call
|
||||
32 |
|
||||
33 | (x.drop(["a"], axis=1, inplace=True))
|
||||
| ^^^^^^^^^^^^ PD002
|
||||
|
|
||||
= help: Assign to variable; remove `inplace` arg
|
||||
|
||||
ℹ Suggested fix
|
||||
30 30 |
|
||||
31 31 | torch.m.ReLU(inplace=True) # safe because this isn't a pandas call
|
||||
32 32 |
|
||||
33 |-(x.drop(["a"], axis=1, inplace=True))
|
||||
33 |+x = (x.drop(["a"], axis=1))
|
||||
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ pub(super) fn is_acronym(name: &str, asname: &str) -> bool {
|
||||
name.chars().filter(|c| c.is_uppercase()).join("") == asname
|
||||
}
|
||||
|
||||
/// Returns `true` if the statement is an assignment to a named tuple.
|
||||
pub(super) fn is_named_tuple_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool {
|
||||
let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else {
|
||||
return false;
|
||||
@@ -30,13 +31,12 @@ pub(super) fn is_named_tuple_assignment(stmt: &Stmt, semantic: &SemanticModel) -
|
||||
return false;
|
||||
};
|
||||
semantic.resolve_call_path(func).is_some_and(|call_path| {
|
||||
matches!(
|
||||
call_path.as_slice(),
|
||||
["collections", "namedtuple"] | ["typing", "NamedTuple"]
|
||||
)
|
||||
matches!(call_path.as_slice(), ["collections", "namedtuple"])
|
||||
|| semantic.match_typing_call_path(&call_path, "NamedTuple")
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `true` if the statement is an assignment to a `TypedDict`.
|
||||
pub(super) fn is_typed_dict_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool {
|
||||
let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else {
|
||||
return false;
|
||||
@@ -44,11 +44,10 @@ pub(super) fn is_typed_dict_assignment(stmt: &Stmt, semantic: &SemanticModel) ->
|
||||
let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else {
|
||||
return false;
|
||||
};
|
||||
semantic
|
||||
.resolve_call_path(func)
|
||||
.is_some_and(|call_path| matches!(call_path.as_slice(), ["typing", "TypedDict"]))
|
||||
semantic.match_typing_expr(func, "TypedDict")
|
||||
}
|
||||
|
||||
/// Returns `true` if the statement is an assignment to a `TypeVar` or `NewType`.
|
||||
pub(super) fn is_type_var_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool {
|
||||
let Stmt::Assign(ast::StmtAssign { value, .. }) = stmt else {
|
||||
return false;
|
||||
@@ -56,9 +55,18 @@ pub(super) fn is_type_var_assignment(stmt: &Stmt, semantic: &SemanticModel) -> b
|
||||
let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else {
|
||||
return false;
|
||||
};
|
||||
semantic
|
||||
.resolve_call_path(func)
|
||||
.is_some_and(|call_path| matches!(call_path.as_slice(), ["typing", "TypeVar" | "NewType"]))
|
||||
semantic.resolve_call_path(func).is_some_and(|call_path| {
|
||||
semantic.match_typing_call_path(&call_path, "TypeVar")
|
||||
|| semantic.match_typing_call_path(&call_path, "NewType")
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `true` if the statement is an assignment to a `TypeAlias`.
|
||||
pub(super) fn is_type_alias_assignment(stmt: &Stmt, semantic: &SemanticModel) -> bool {
|
||||
let Stmt::AnnAssign(ast::StmtAnnAssign { annotation, .. }) = stmt else {
|
||||
return false;
|
||||
};
|
||||
semantic.match_typing_expr(annotation, "TypeAlias")
|
||||
}
|
||||
|
||||
pub(super) fn is_typed_dict_class(arguments: Option<&Arguments>, semantic: &SemanticModel) -> bool {
|
||||
|
||||
@@ -3,6 +3,7 @@ use ruff_python_ast::Stmt;
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::identifier::Identifier;
|
||||
use ruff_python_semantic::analyze::visibility;
|
||||
use ruff_python_semantic::{Scope, ScopeKind};
|
||||
|
||||
use crate::settings::types::IdentifierPattern;
|
||||
@@ -51,7 +52,7 @@ pub(crate) fn dunder_function_name(
|
||||
if matches!(scope.kind, ScopeKind::Class(_)) {
|
||||
return None;
|
||||
}
|
||||
if !(name.starts_with("__") && name.ends_with("__")) {
|
||||
if !visibility::is_magic(name) {
|
||||
return None;
|
||||
}
|
||||
// Allowed under PEP 562 (https://peps.python.org/pep-0562/).
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user