Compare commits
8 Commits
charlie/no
...
C1901
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7460ca28dd | ||
|
|
56e8a4fd14 | ||
|
|
67444143b5 | ||
|
|
daeb3ff37e | ||
|
|
2f3734dd22 | ||
|
|
fc50d28fcf | ||
|
|
158dc5e7d4 | ||
|
|
9a42be8a90 |
40
.github/workflows/ci.yaml
vendored
40
.github/workflows/ci.yaml
vendored
@@ -79,10 +79,6 @@ jobs:
|
||||
env:
|
||||
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
|
||||
RUSTDOCFLAGS: "-D warnings"
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ruff
|
||||
path: target/debug/ruff
|
||||
|
||||
|
||||
cargo-test-wasm:
|
||||
@@ -127,39 +123,3 @@ jobs:
|
||||
- uses: crate-ci/typos@master
|
||||
with:
|
||||
files: .
|
||||
|
||||
ecosystem:
|
||||
name: "ecosystem"
|
||||
runs-on: ubuntu-latest
|
||||
needs: cargo-test
|
||||
# Only runs on pull requests, since that is the only we way we can find the base version for comparison.
|
||||
if: github.event_name == 'pull_request'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- uses: actions/download-artifact@v3
|
||||
id: ruff-target
|
||||
with:
|
||||
name: ruff
|
||||
path: target/debug
|
||||
- uses: dawidd6/action-download-artifact@v2
|
||||
with:
|
||||
name: ruff
|
||||
branch: ${{ github.event.pull_request.base_ref }}
|
||||
check_artifacts: true
|
||||
- name: Run ecosystem check
|
||||
run: |
|
||||
# Make executable, since artifact download doesn't preserve this
|
||||
chmod +x ruff ${{ steps.ruff-target.outputs.download-path }}/ruff
|
||||
|
||||
scripts/check_ecosystem.py ruff ${{ steps.ruff-target.outputs.download-path }}/ruff | tee ecosystem-result
|
||||
|
||||
echo ${{ github.event.number }} > pr-number
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ecosystem-result
|
||||
path: |
|
||||
ecosystem-result
|
||||
pr-number
|
||||
|
||||
31
.github/workflows/ecosystem-comment.yaml
vendored
31
.github/workflows/ecosystem-comment.yaml
vendored
@@ -1,31 +0,0 @@
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [CI]
|
||||
types: [completed]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dawidd6/action-download-artifact@v2
|
||||
id: download-result
|
||||
with:
|
||||
name: ecosystem-result
|
||||
workflow: ci.yaml
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
if_no_artifact_found: ignore
|
||||
- if: steps.download-result.outputs.found_artifact
|
||||
id: result
|
||||
run: |
|
||||
echo "pr-number=$(<pr-number)" >> $GITHUB_OUTPUT
|
||||
- name: Create comment
|
||||
if: steps.download-result.outputs.found_artifact
|
||||
uses: thollander/actions-comment-pull-request@v2
|
||||
with:
|
||||
pr_number: ${{ steps.result.outputs.pr-number }}
|
||||
filePath: ecosystem-result
|
||||
comment_tag: ecosystem-results
|
||||
47
Cargo.lock
generated
47
Cargo.lock
generated
@@ -769,7 +769,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.255"
|
||||
version = "0.0.254"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.1.8",
|
||||
@@ -1503,12 +1503,6 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
|
||||
|
||||
[[package]]
|
||||
name = "peg"
|
||||
version = "0.8.1"
|
||||
@@ -1536,18 +1530,6 @@ version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa00462b37ead6d11a82c9d568b26682d78e0477dc02d1966c013af80969739"
|
||||
|
||||
[[package]]
|
||||
name = "pep440_rs"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/konstin/pep440-rs.git?rev=a8fef4ec47f4c25b070b39cdbe6a0b9847e49941#a8fef4ec47f4c25b070b39cdbe6a0b9847e49941"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"regex",
|
||||
"serde",
|
||||
"tracing",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.2.0"
|
||||
@@ -1971,7 +1953,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.255"
|
||||
version = "0.0.254"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bisection",
|
||||
@@ -1997,9 +1979,6 @@ dependencies = [
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"path-absolutize",
|
||||
"pathdiff",
|
||||
"pep440_rs",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"result-like",
|
||||
"ruff_cache",
|
||||
@@ -2037,7 +2016,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.0.255"
|
||||
version = "0.0.254"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -2282,7 +2261,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-ast"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=c15f670f2c30cfae6b41a1874893590148c74bc4#c15f670f2c30cfae6b41a1874893590148c74bc4"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"rustpython-compiler-core",
|
||||
@@ -2291,7 +2270,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-common"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=c15f670f2c30cfae6b41a1874893590148c74bc4#c15f670f2c30cfae6b41a1874893590148c74bc4"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
|
||||
dependencies = [
|
||||
"ascii",
|
||||
"bitflags",
|
||||
@@ -2316,7 +2295,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-compiler-core"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=c15f670f2c30cfae6b41a1874893590148c74bc4#c15f670f2c30cfae6b41a1874893590148c74bc4"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bstr 0.2.17",
|
||||
@@ -2330,7 +2309,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rustpython-parser"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=c15f670f2c30cfae6b41a1874893590148c74bc4#c15f670f2c30cfae6b41a1874893590148c74bc4"
|
||||
source = "git+https://github.com/RustPython/RustPython.git?rev=1871a1632e310985414211222f5bf8069678892f#1871a1632e310985414211222f5bf8069678892f"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
@@ -2855,21 +2834,9 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.30"
|
||||
|
||||
@@ -26,11 +26,11 @@ proc-macro2 = { version = "1.0.51" }
|
||||
quote = { version = "1.0.23" }
|
||||
regex = { version = "1.7.1" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "c15f670f2c30cfae6b41a1874893590148c74bc4" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "1871a1632e310985414211222f5bf8069678892f" }
|
||||
rustpython-parser = { features = [
|
||||
"lalrpop",
|
||||
"serde",
|
||||
], git = "https://github.com/RustPython/RustPython.git", rev = "c15f670f2c30cfae6b41a1874893590148c74bc4" }
|
||||
], git = "https://github.com/RustPython/RustPython.git", rev = "1871a1632e310985414211222f5bf8069678892f" }
|
||||
schemars = { version = "0.8.12" }
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = { version = "1.0.93" }
|
||||
|
||||
@@ -137,7 +137,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||
```yaml
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: 'v0.0.255'
|
||||
rev: 'v0.0.254'
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -306,9 +306,6 @@ Ruff is used in a number of major open-source projects, including:
|
||||
- [meson-python](https://github.com/mesonbuild/meson-python)
|
||||
- [ZenML](https://github.com/zenml-io/zenml)
|
||||
- [delta-rs](https://github.com/delta-io/delta-rs)
|
||||
- [Starlite](https://github.com/starlite-api/starlite)
|
||||
- [telemetry-airflow (Mozilla)](https://github.com/mozilla/telemetry-airflow)
|
||||
- [Stable Baselines3](https://github.com/DLR-RM/stable-baselines3)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.255"
|
||||
version = "0.0.254"
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
|
||||
|
||||
@@ -46,15 +46,8 @@ fn main() -> Result<()> {
|
||||
.map(|tool| ExternalConfig {
|
||||
black: tool.black.as_ref(),
|
||||
isort: tool.isort.as_ref(),
|
||||
..Default::default()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let external_config = ExternalConfig {
|
||||
project: pyproject
|
||||
.as_ref()
|
||||
.and_then(|pyproject| pyproject.project.as_ref()),
|
||||
..external_config
|
||||
};
|
||||
|
||||
// Create Ruff's pyproject.toml section.
|
||||
let pyproject = flake8_to_ruff::convert(&config, &external_config, args.plugin)?;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.255"
|
||||
version = "0.0.254"
|
||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
@@ -12,6 +12,8 @@ license = "MIT"
|
||||
|
||||
[lib]
|
||||
name = "ruff"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
ruff_cache = { path = "../ruff_cache" }
|
||||
@@ -46,10 +48,6 @@ path-absolutize = { workspace = true, features = [
|
||||
"once_cell_cache",
|
||||
"use_unix_paths_on_wasm",
|
||||
] }
|
||||
pathdiff = { version = "0.2.1" }
|
||||
pep440_rs = { git = "https://github.com/konstin/pep440-rs.git", features = [
|
||||
"serde",
|
||||
], rev = "a8fef4ec47f4c25b070b39cdbe6a0b9847e49941" }
|
||||
regex = { workspace = true }
|
||||
result-like = { version = "0.4.6" }
|
||||
rustc-hash = { workspace = true }
|
||||
@@ -67,10 +65,9 @@ thiserror = { version = "1.0.38" }
|
||||
toml = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.4.0" }
|
||||
insta = { workspace = true, features = ["yaml", "redactions"] }
|
||||
pretty_assertions = "1.3.0"
|
||||
test-case = { workspace = true }
|
||||
criterion = { version = "0.4.0" }
|
||||
|
||||
|
||||
[features]
|
||||
|
||||
@@ -6,8 +6,6 @@ obj.endswith("foo") or obj.endswith("bar")
|
||||
obj.startswith(foo) or obj.startswith(bar)
|
||||
# error
|
||||
obj.startswith(foo) or obj.startswith("foo")
|
||||
# error
|
||||
obj.endswith(foo) or obj.startswith(foo) or obj.startswith("foo")
|
||||
|
||||
# ok
|
||||
obj.startswith(("foo", "bar"))
|
||||
|
||||
@@ -61,43 +61,3 @@ def f22(
|
||||
x: complex = -42.5j # Error PYI011 Only simple default values allowed for typed arguments
|
||||
+ 4.3j,
|
||||
) -> None: ...
|
||||
def f23(
|
||||
x: bool = True, # OK
|
||||
) -> None: ...
|
||||
def f24(
|
||||
x: float = 3.14, # OK
|
||||
) -> None: ...
|
||||
def f25(
|
||||
x: float = -3.14, # OK
|
||||
) -> None: ...
|
||||
def f26(
|
||||
x: complex = -3.14j, # OK
|
||||
) -> None: ...
|
||||
def f27(
|
||||
x: complex = -3 - 3.14j, # OK
|
||||
) -> None: ...
|
||||
def f28(
|
||||
x: float = math.tau, # OK
|
||||
) -> None: ...
|
||||
def f29(
|
||||
x: float = math.inf, # OK
|
||||
) -> None: ...
|
||||
def f30(
|
||||
x: float = -math.inf, # OK
|
||||
) -> None: ...
|
||||
def f31(
|
||||
x: float = inf, # Error PYI011 Only simple default values allowed for typed arguments
|
||||
) -> None: ...
|
||||
def f32(
|
||||
x: float = np.inf, # Error PYI011 Only simple default values allowed for typed arguments
|
||||
) -> None: ...
|
||||
def f33(
|
||||
x: float = math.nan, # OK
|
||||
) -> None: ...
|
||||
def f34(
|
||||
x: float = -math.nan, # Error PYI011 Only simple default values allowed for typed arguments
|
||||
) -> None: ...
|
||||
def f35(
|
||||
x: complex = math.inf # Error PYI011 Only simple default values allowed for typed arguments
|
||||
+ 1j,
|
||||
) -> None: ...
|
||||
|
||||
@@ -43,6 +43,3 @@ def f21(
|
||||
def f22(
|
||||
x=-42.5j + 4.3j, # Error PYI014
|
||||
) -> None: ...
|
||||
def f23(
|
||||
x=True, # OK
|
||||
) -> None: ...
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import os
|
||||
|
||||
tempVar = os.getenv("TEST", 12) # [invalid-envvar-default]
|
||||
goodVar = os.getenv("TESTING", None)
|
||||
dictVarBad = os.getenv("AAA", {"a", 7}) # [invalid-envvar-default]
|
||||
print(os.getenv("TEST", False)) # [invalid-envvar-default]
|
||||
os.getenv("AA", "GOOD")
|
||||
os.getenv("B", Z)
|
||||
@@ -1,12 +0,0 @@
|
||||
import os
|
||||
|
||||
os.getenv(1) # [invalid-envvar-value]
|
||||
os.getenv("a")
|
||||
os.getenv("test")
|
||||
os.getenv(key="testingAgain")
|
||||
os.getenv(key=11) # [invalid-envvar-value]
|
||||
os.getenv(["hello"]) # [invalid-envvar-value]
|
||||
os.getenv(key="foo", default="bar")
|
||||
|
||||
AA = "aa"
|
||||
os.getenv(AA)
|
||||
@@ -1,10 +0,0 @@
|
||||
import socket
|
||||
|
||||
from kombu import Connection, exceptions
|
||||
|
||||
try:
|
||||
conn = Connection(settings.CELERY_BROKER_URL)
|
||||
conn.ensure_connection(max_retries=2)
|
||||
conn._close()
|
||||
except (socket.error, exceptions.OperationalError):
|
||||
return HttpResponseServerError("cache: cannot connect to broker.")
|
||||
@@ -41,7 +41,7 @@ if True:
|
||||
Good,
|
||||
)
|
||||
|
||||
from typing import Callable, Match, Pattern, List, OrderedDict, AbstractSet
|
||||
from typing import Callable, Match, Pattern, List, OrderedDict
|
||||
|
||||
if True: from collections import (
|
||||
Mapping, Counter)
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# noqa
|
||||
# noqa # comment
|
||||
print() # noqa
|
||||
print() # noqa # comment
|
||||
print(a) # noqa
|
||||
print(a) # noqa # comment
|
||||
|
||||
# noqa: E501, F821
|
||||
# noqa: E501, F821 # comment
|
||||
print() # noqa: E501, F821
|
||||
print() # noqa: E501, F821 # comment
|
||||
print(a) # noqa: E501, F821
|
||||
print(a) # noqa: E501, F821 # comment
|
||||
@@ -9,8 +9,8 @@ use rustpython_parser::{lexer, Mode, Tok};
|
||||
use ruff_diagnostics::Fix;
|
||||
use ruff_python_ast::helpers;
|
||||
use ruff_python_ast::helpers::to_absolute;
|
||||
use ruff_python_ast::newlines::NewlineWithTrailingNewline;
|
||||
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
|
||||
use ruff_python_ast::whitespace::LinesWithTrailingNewline;
|
||||
|
||||
use crate::cst::helpers::compose_module_path;
|
||||
use crate::cst::matchers::match_module;
|
||||
@@ -100,7 +100,7 @@ fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool>
|
||||
/// of a multi-statement line.
|
||||
fn trailing_semicolon(stmt: &Stmt, locator: &Locator) -> Option<Location> {
|
||||
let contents = locator.skip(stmt.end_location.unwrap());
|
||||
for (row, line) in NewlineWithTrailingNewline::from(contents).enumerate() {
|
||||
for (row, line) in LinesWithTrailingNewline::from(contents).enumerate() {
|
||||
let trimmed = line.trim();
|
||||
if trimmed.starts_with(';') {
|
||||
let column = line
|
||||
@@ -123,7 +123,7 @@ fn trailing_semicolon(stmt: &Stmt, locator: &Locator) -> Option<Location> {
|
||||
fn next_stmt_break(semicolon: Location, locator: &Locator) -> Location {
|
||||
let start_location = Location::new(semicolon.row(), semicolon.column() + 1);
|
||||
let contents = locator.skip(start_location);
|
||||
for (row, line) in NewlineWithTrailingNewline::from(contents).enumerate() {
|
||||
for (row, line) in LinesWithTrailingNewline::from(contents).enumerate() {
|
||||
let trimmed = line.trim();
|
||||
// Skip past any continuations.
|
||||
if trimmed.starts_with('\\') {
|
||||
|
||||
@@ -1138,8 +1138,8 @@ where
|
||||
if self.settings.rules.enabled(&Rule::RewriteCElementTree) {
|
||||
pyupgrade::rules::replace_c_element_tree(self, stmt);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::DeprecatedImport) {
|
||||
pyupgrade::rules::deprecated_import(
|
||||
if self.settings.rules.enabled(&Rule::ImportReplacements) {
|
||||
pyupgrade::rules::import_replacements(
|
||||
self,
|
||||
stmt,
|
||||
names,
|
||||
@@ -1505,7 +1505,7 @@ where
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::OSErrorAlias) {
|
||||
if let Some(item) = exc {
|
||||
pyupgrade::rules::os_error_alias_raise(self, item);
|
||||
pyupgrade::rules::os_error_alias(self, &item);
|
||||
}
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::RaiseVanillaClass) {
|
||||
@@ -1757,7 +1757,7 @@ where
|
||||
flake8_bugbear::rules::redundant_tuple_in_exception_handler(self, handlers);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::OSErrorAlias) {
|
||||
pyupgrade::rules::os_error_alias_handlers(self, handlers);
|
||||
pyupgrade::rules::os_error_alias(self, &handlers);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::AssertInExcept) {
|
||||
self.diagnostics.extend(
|
||||
@@ -2484,7 +2484,7 @@ where
|
||||
pyupgrade::rules::replace_stdout_stderr(self, expr, func, keywords);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::OSErrorAlias) {
|
||||
pyupgrade::rules::os_error_alias_call(self, func);
|
||||
pyupgrade::rules::os_error_alias(self, &expr);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::IsinstanceWithTuple)
|
||||
&& self.settings.target_version >= PythonVersion::Py310
|
||||
@@ -2866,12 +2866,6 @@ where
|
||||
if self.settings.rules.enabled(&Rule::BadStrStripCall) {
|
||||
pylint::rules::bad_str_strip_call(self, func, args);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::InvalidEnvvarDefault) {
|
||||
pylint::rules::invalid_envvar_default(self, func, args, keywords);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::InvalidEnvvarValue) {
|
||||
pylint::rules::invalid_envvar_value(self, func, args, keywords);
|
||||
}
|
||||
|
||||
// flake8-pytest-style
|
||||
if self.settings.rules.enabled(&Rule::PatchWithLambda) {
|
||||
@@ -3441,7 +3435,7 @@ where
|
||||
pylint::rules::merge_isinstance(self, expr, op, values);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::SingleStartsEndsWith) {
|
||||
flake8_pie::rules::single_starts_ends_with(self, expr);
|
||||
flake8_pie::rules::single_starts_ends_with(self, values, op);
|
||||
}
|
||||
if self.settings.rules.enabled(&Rule::DuplicateIsinstanceCall) {
|
||||
flake8_simplify::rules::duplicate_isinstance_call(self, expr);
|
||||
@@ -4971,9 +4965,7 @@ impl<'a> Checker<'a> {
|
||||
if let Some(diagnostic) =
|
||||
flake8_type_checking::rules::runtime_import_in_type_checking_block(binding)
|
||||
{
|
||||
if self.settings.rules.enabled(diagnostic.kind.rule()) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
if let Some(diagnostic) =
|
||||
flake8_type_checking::rules::typing_only_runtime_import(
|
||||
|
||||
@@ -5,7 +5,6 @@ use nohash_hasher::IntMap;
|
||||
use rustpython_parser::ast::Location;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Fix};
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::codes::NoqaCode;
|
||||
@@ -23,7 +22,7 @@ pub fn check_noqa(
|
||||
noqa_line_for: &IntMap<usize, usize>,
|
||||
settings: &Settings,
|
||||
autofix: flags::Autofix,
|
||||
) -> Vec<usize> {
|
||||
) {
|
||||
let enforce_noqa = settings.rules.enabled(&Rule::UnusedNOQA);
|
||||
|
||||
// Whether the file is exempted from all checks.
|
||||
@@ -39,7 +38,7 @@ pub fn check_noqa(
|
||||
// Indices of diagnostics that were ignored by a `noqa` directive.
|
||||
let mut ignored_diagnostics = vec![];
|
||||
|
||||
let lines: Vec<&str> = contents.universal_newlines().collect();
|
||||
let lines: Vec<&str> = contents.lines().collect();
|
||||
for lineno in commented_lines {
|
||||
match extract_file_exemption(lines[lineno - 1]) {
|
||||
Exemption::All => {
|
||||
@@ -98,7 +97,7 @@ pub fn check_noqa(
|
||||
ignored_diagnostics.push(index);
|
||||
continue;
|
||||
}
|
||||
(Directive::Codes(.., codes, _), matches) => {
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if noqa::includes(diagnostic.kind.rule(), codes) {
|
||||
matches.push(diagnostic.kind.rule().noqa_code());
|
||||
ignored_diagnostics.push(index);
|
||||
@@ -125,7 +124,7 @@ pub fn check_noqa(
|
||||
ignored_diagnostics.push(index);
|
||||
continue;
|
||||
}
|
||||
(Directive::Codes(.., codes, _), matches) => {
|
||||
(Directive::Codes(.., codes), matches) => {
|
||||
if noqa::includes(diagnostic.kind.rule(), codes) {
|
||||
matches.push(diagnostic.kind.rule().noqa_code());
|
||||
ignored_diagnostics.push(index);
|
||||
@@ -141,7 +140,7 @@ pub fn check_noqa(
|
||||
if enforce_noqa {
|
||||
for (row, (directive, matches)) in noqa_directives {
|
||||
match directive {
|
||||
Directive::All(leading_spaces, start_byte, end_byte, trailing_spaces) => {
|
||||
Directive::All(spaces, start_byte, end_byte) => {
|
||||
if matches.is_empty() {
|
||||
let start = lines[row][..start_byte].chars().count();
|
||||
let end = start + lines[row][start_byte..end_byte].chars().count();
|
||||
@@ -151,27 +150,15 @@ pub fn check_noqa(
|
||||
Range::new(Location::new(row + 1, start), Location::new(row + 1, end)),
|
||||
);
|
||||
if autofix.into() && settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
if start - leading_spaces == 0 && end == lines[row].chars().count() {
|
||||
diagnostic.amend(Fix::deletion(
|
||||
Location::new(row + 1, 0),
|
||||
Location::new(row + 2, 0),
|
||||
));
|
||||
} else if end == lines[row].chars().count() {
|
||||
diagnostic.amend(Fix::deletion(
|
||||
Location::new(row + 1, start - leading_spaces),
|
||||
Location::new(row + 1, end + trailing_spaces),
|
||||
));
|
||||
} else {
|
||||
diagnostic.amend(Fix::deletion(
|
||||
Location::new(row + 1, start),
|
||||
Location::new(row + 1, end + trailing_spaces),
|
||||
));
|
||||
}
|
||||
diagnostic.amend(Fix::deletion(
|
||||
Location::new(row + 1, start - spaces),
|
||||
Location::new(row + 1, lines[row].chars().count()),
|
||||
));
|
||||
}
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
Directive::Codes(leading_spaces, start_byte, end_byte, codes, trailing_spaces) => {
|
||||
Directive::Codes(spaces, start_byte, end_byte, codes) => {
|
||||
let mut disabled_codes = vec![];
|
||||
let mut unknown_codes = vec![];
|
||||
let mut unmatched_codes = vec![];
|
||||
@@ -231,28 +218,15 @@ pub fn check_noqa(
|
||||
);
|
||||
if autofix.into() && settings.rules.should_fix(diagnostic.kind.rule()) {
|
||||
if valid_codes.is_empty() {
|
||||
if start - leading_spaces == 0 && end == lines[row].chars().count()
|
||||
{
|
||||
diagnostic.amend(Fix::deletion(
|
||||
Location::new(row + 1, 0),
|
||||
Location::new(row + 2, 0),
|
||||
));
|
||||
} else if end == lines[row].chars().count() {
|
||||
diagnostic.amend(Fix::deletion(
|
||||
Location::new(row + 1, start - leading_spaces),
|
||||
Location::new(row + 1, end + trailing_spaces),
|
||||
));
|
||||
} else {
|
||||
diagnostic.amend(Fix::deletion(
|
||||
Location::new(row + 1, start),
|
||||
Location::new(row + 1, end + trailing_spaces),
|
||||
));
|
||||
}
|
||||
diagnostic.amend(Fix::deletion(
|
||||
Location::new(row + 1, start - spaces),
|
||||
Location::new(row + 1, lines[row].chars().count()),
|
||||
));
|
||||
} else {
|
||||
diagnostic.amend(Fix::replacement(
|
||||
format!("# noqa: {}", valid_codes.join(", ")),
|
||||
Location::new(row + 1, start),
|
||||
Location::new(row + 1, end),
|
||||
Location::new(row + 1, lines[row].chars().count()),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -265,5 +239,7 @@ pub fn check_noqa(
|
||||
}
|
||||
|
||||
ignored_diagnostics.sort_unstable();
|
||||
ignored_diagnostics
|
||||
for index in ignored_diagnostics.iter().rev() {
|
||||
diagnostics.swap_remove(*index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
use std::path::Path;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::source_code::{Locator, Stylist};
|
||||
use ruff_python_ast::source_code::Stylist;
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::flake8_executable::helpers::{extract_shebang, ShebangDirective};
|
||||
@@ -22,8 +21,8 @@ use crate::settings::{flags, Settings};
|
||||
|
||||
pub fn check_physical_lines(
|
||||
path: &Path,
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
contents: &str,
|
||||
commented_lines: &[usize],
|
||||
doc_lines: &[usize],
|
||||
settings: &Settings,
|
||||
@@ -57,7 +56,7 @@ pub fn check_physical_lines(
|
||||
|
||||
let mut commented_lines_iter = commented_lines.iter().peekable();
|
||||
let mut doc_lines_iter = doc_lines.iter().peekable();
|
||||
for (index, line) in locator.contents().universal_newlines().enumerate() {
|
||||
for (index, line) in contents.lines().enumerate() {
|
||||
while commented_lines_iter
|
||||
.next_if(|lineno| &(index + 1) == *lineno)
|
||||
.is_some()
|
||||
@@ -163,8 +162,8 @@ pub fn check_physical_lines(
|
||||
|
||||
if enforce_no_newline_at_end_of_file {
|
||||
if let Some(diagnostic) = no_newline_at_end_of_file(
|
||||
locator,
|
||||
stylist,
|
||||
contents,
|
||||
autofix.into() && settings.rules.should_fix(&Rule::NoNewLineAtEndOfFile),
|
||||
) {
|
||||
diagnostics.push(diagnostic);
|
||||
@@ -200,8 +199,8 @@ mod tests {
|
||||
let check_with_max_line_length = |line_length: usize| {
|
||||
check_physical_lines(
|
||||
Path::new("foo.py"),
|
||||
&locator,
|
||||
&stylist,
|
||||
line,
|
||||
&[],
|
||||
&[],
|
||||
&Settings {
|
||||
|
||||
@@ -161,37 +161,35 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
|
||||
(Pyflakes, "901") => Rule::RaiseNotImplemented,
|
||||
|
||||
// pylint
|
||||
(Pylint, "C0414") => Rule::UselessImportAlias,
|
||||
(Pylint, "C1901") => Rule::CompareToEmptyString,
|
||||
(Pylint, "C3002") => Rule::UnnecessaryDirectLambdaCall,
|
||||
(Pylint, "E0100") => Rule::YieldInInit,
|
||||
(Pylint, "E0101") => Rule::ReturnInInit,
|
||||
(Pylint, "E0117") => Rule::NonlocalWithoutBinding,
|
||||
(Pylint, "E0118") => Rule::UsedPriorGlobalDeclaration,
|
||||
(Pylint, "E0604") => Rule::InvalidAllObject,
|
||||
(Pylint, "E0605") => Rule::InvalidAllFormat,
|
||||
(Pylint, "W1508") => Rule::InvalidEnvvarDefault,
|
||||
(Pylint, "E1142") => Rule::AwaitOutsideAsync,
|
||||
(Pylint, "E1205") => Rule::LoggingTooManyArgs,
|
||||
(Pylint, "E1206") => Rule::LoggingTooFewArgs,
|
||||
(Pylint, "E1307") => Rule::BadStringFormatType,
|
||||
(Pylint, "E1310") => Rule::BadStrStripCall,
|
||||
(Pylint, "E1507") => Rule::InvalidEnvvarValue,
|
||||
(Pylint, "E2502") => Rule::BidirectionalUnicode,
|
||||
(Pylint, "R0133") => Rule::ComparisonOfConstant,
|
||||
(Pylint, "E1310") => Rule::BadStrStripCall,
|
||||
(Pylint, "C0414") => Rule::UselessImportAlias,
|
||||
(Pylint, "C3002") => Rule::UnnecessaryDirectLambdaCall,
|
||||
(Pylint, "E0117") => Rule::NonlocalWithoutBinding,
|
||||
(Pylint, "E0118") => Rule::UsedPriorGlobalDeclaration,
|
||||
(Pylint, "E1142") => Rule::AwaitOutsideAsync,
|
||||
(Pylint, "R0206") => Rule::PropertyWithParameters,
|
||||
(Pylint, "R0402") => Rule::ConsiderUsingFromImport,
|
||||
(Pylint, "R0911") => Rule::TooManyReturnStatements,
|
||||
(Pylint, "R0912") => Rule::TooManyBranches,
|
||||
(Pylint, "R0913") => Rule::TooManyArguments,
|
||||
(Pylint, "R0915") => Rule::TooManyStatements,
|
||||
(Pylint, "C1901") => Rule::CompareToEmptyString,
|
||||
(Pylint, "R0133") => Rule::ComparisonOfConstant,
|
||||
(Pylint, "R1701") => Rule::ConsiderMergingIsinstance,
|
||||
(Pylint, "R1722") => Rule::ConsiderUsingSysExit,
|
||||
(Pylint, "R2004") => Rule::MagicValueComparison,
|
||||
(Pylint, "R5501") => Rule::CollapsibleElseIf,
|
||||
(Pylint, "R2004") => Rule::MagicValueComparison,
|
||||
(Pylint, "W0120") => Rule::UselessElseOnLoop,
|
||||
(Pylint, "W0602") => Rule::GlobalVariableNotAssigned,
|
||||
(Pylint, "W0603") => Rule::GlobalStatement,
|
||||
(Pylint, "R0911") => Rule::TooManyReturnStatements,
|
||||
(Pylint, "R0913") => Rule::TooManyArguments,
|
||||
(Pylint, "R0912") => Rule::TooManyBranches,
|
||||
(Pylint, "R0915") => Rule::TooManyStatements,
|
||||
(Pylint, "W2901") => Rule::RedefinedLoopName,
|
||||
|
||||
// flake8-builtins
|
||||
@@ -375,7 +373,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
|
||||
(Pyupgrade, "032") => Rule::FString,
|
||||
(Pyupgrade, "033") => Rule::FunctoolsCache,
|
||||
(Pyupgrade, "034") => Rule::ExtraneousParentheses,
|
||||
(Pyupgrade, "035") => Rule::DeprecatedImport,
|
||||
(Pyupgrade, "035") => Rule::ImportReplacements,
|
||||
(Pyupgrade, "036") => Rule::OutdatedVersionBlock,
|
||||
(Pyupgrade, "037") => Rule::QuotedAnnotation,
|
||||
(Pyupgrade, "038") => Rule::IsinstanceWithTuple,
|
||||
|
||||
@@ -20,7 +20,6 @@ use crate::rules::{
|
||||
};
|
||||
use crate::settings::options::Options;
|
||||
use crate::settings::pyproject::Pyproject;
|
||||
use crate::settings::types::PythonVersion;
|
||||
use crate::warn_user;
|
||||
|
||||
const DEFAULT_SELECTORS: &[RuleSelector] = &[
|
||||
@@ -425,15 +424,6 @@ pub fn convert(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(project) = &external_config.project {
|
||||
if let Some(requires_python) = &project.requires_python {
|
||||
if options.target_version.is_none() {
|
||||
options.target_version =
|
||||
PythonVersion::get_minimum_supported_version(requires_python);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the pyproject.toml.
|
||||
Ok(Pyproject::new(options))
|
||||
}
|
||||
@@ -449,17 +439,13 @@ fn resolve_select(plugins: &[Plugin]) -> HashSet<RuleSelector> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Result;
|
||||
use itertools::Itertools;
|
||||
use pep440_rs::VersionSpecifiers;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::super::plugin::Plugin;
|
||||
use super::convert;
|
||||
use crate::flake8_to_ruff::converter::DEFAULT_SELECTORS;
|
||||
use crate::flake8_to_ruff::pep621::Project;
|
||||
use crate::flake8_to_ruff::ExternalConfig;
|
||||
use crate::registry::Linter;
|
||||
use crate::rule_selector::RuleSelector;
|
||||
@@ -467,7 +453,6 @@ mod tests {
|
||||
use crate::rules::{flake8_quotes, pydocstyle};
|
||||
use crate::settings::options::Options;
|
||||
use crate::settings::pyproject::Pyproject;
|
||||
use crate::settings::types::PythonVersion;
|
||||
|
||||
fn default_options(plugins: impl IntoIterator<Item = RuleSelector>) -> Options {
|
||||
Options {
|
||||
@@ -624,25 +609,4 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_converts_project_requires_python() -> Result<()> {
|
||||
let actual = convert(
|
||||
&HashMap::from([("flake8".to_string(), HashMap::default())]),
|
||||
&ExternalConfig {
|
||||
project: Some(&Project {
|
||||
requires_python: Some(VersionSpecifiers::from_str(">=3.8.16, <3.11")?),
|
||||
}),
|
||||
..ExternalConfig::default()
|
||||
},
|
||||
Some(vec![]),
|
||||
)?;
|
||||
let expected = Pyproject::new(Options {
|
||||
target_version: Some(PythonVersion::Py38),
|
||||
..default_options([])
|
||||
});
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use super::black::Black;
|
||||
use super::isort::Isort;
|
||||
use super::pep621::Project;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ExternalConfig<'a> {
|
||||
pub black: Option<&'a Black>,
|
||||
pub isort: Option<&'a Isort>,
|
||||
pub project: Option<&'a Project>,
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ mod converter;
|
||||
mod external_config;
|
||||
mod isort;
|
||||
mod parser;
|
||||
pub mod pep621;
|
||||
mod plugin;
|
||||
mod pyproject;
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
//! Extract PEP 621 configuration settings from a pyproject.toml.
|
||||
|
||||
use pep440_rs::VersionSpecifiers;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
pub struct Project {
|
||||
#[serde(alias = "requires-python", alias = "requires_python")]
|
||||
pub requires_python: Option<VersionSpecifiers>,
|
||||
}
|
||||
@@ -5,7 +5,6 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::black::Black;
|
||||
use super::isort::Isort;
|
||||
use super::pep621::Project;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Tools {
|
||||
@@ -16,7 +15,6 @@ pub struct Tools {
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Pyproject {
|
||||
pub tool: Option<Tools>,
|
||||
pub project: Option<Project>,
|
||||
}
|
||||
|
||||
pub fn parse<P: AsRef<Path>>(path: P) -> Result<Pyproject> {
|
||||
|
||||
@@ -74,20 +74,10 @@ pub fn normalize_path_to<P: AsRef<Path>, R: AsRef<Path>>(path: P, project_root:
|
||||
}
|
||||
|
||||
/// Convert an absolute path to be relative to the current working directory.
|
||||
pub fn relativize_path<P: AsRef<Path>>(path: P) -> String {
|
||||
pub fn relativize_path(path: impl AsRef<Path>) -> String {
|
||||
let path = path.as_ref();
|
||||
if let Ok(path) = path.strip_prefix(&*path_dedot::CWD) {
|
||||
return format!("{}", path.display());
|
||||
}
|
||||
format!("{}", path.display())
|
||||
}
|
||||
|
||||
/// Convert an absolute path to be relative to the specified project root.
|
||||
pub fn relativize_path_to<P: AsRef<Path>, R: AsRef<Path>>(path: P, project_root: R) -> String {
|
||||
format!(
|
||||
"{}",
|
||||
pathdiff::diff_paths(&path, project_root)
|
||||
.expect("Could not diff paths")
|
||||
.display()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -191,8 +191,8 @@ pub fn check_path(
|
||||
{
|
||||
diagnostics.extend(check_physical_lines(
|
||||
path,
|
||||
locator,
|
||||
stylist,
|
||||
contents,
|
||||
indexer.commented_lines(),
|
||||
&doc_lines,
|
||||
settings,
|
||||
@@ -215,7 +215,7 @@ pub fn check_path(
|
||||
.iter_enabled()
|
||||
.any(|rule_code| rule_code.lint_source().is_noqa())
|
||||
{
|
||||
let ignored = check_noqa(
|
||||
check_noqa(
|
||||
&mut diagnostics,
|
||||
contents,
|
||||
indexer.commented_lines(),
|
||||
@@ -223,11 +223,6 @@ pub fn check_path(
|
||||
settings,
|
||||
error.as_ref().map_or(autofix, |_| flags::Autofix::Disabled),
|
||||
);
|
||||
if noqa.into() {
|
||||
for index in ignored.iter().rev() {
|
||||
diagnostics.swap_remove(*index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LinterResult::new(diagnostics, error)
|
||||
|
||||
@@ -12,7 +12,6 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustpython_parser::ast::Location;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::source_code::{LineEnding, Locator};
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
@@ -22,7 +21,7 @@ use crate::rule_redirects::get_redirect_target;
|
||||
|
||||
static NOQA_LINE_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||
Regex::new(
|
||||
r"(?P<leading_spaces>\s*)(?P<noqa>(?i:# noqa)(?::\s?(?P<codes>(?:[A-Z]+[0-9]+)(?:[,\s]+[A-Z]+[0-9]+)*))?)(?P<trailing_spaces>\s*)",
|
||||
r"(?P<spaces>\s*)(?P<noqa>(?i:# noqa)(?::\s?(?P<codes>([A-Z]+[0-9]+(?:[,\s]+)?)+))?)",
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
@@ -74,42 +73,35 @@ pub fn extract_file_exemption(line: &str) -> Exemption {
|
||||
#[derive(Debug)]
|
||||
pub enum Directive<'a> {
|
||||
None,
|
||||
All(usize, usize, usize, usize),
|
||||
Codes(usize, usize, usize, Vec<&'a str>, usize),
|
||||
All(usize, usize, usize),
|
||||
Codes(usize, usize, usize, Vec<&'a str>),
|
||||
}
|
||||
|
||||
/// Extract the noqa `Directive` from a line of Python source code.
|
||||
pub fn extract_noqa_directive(line: &str) -> Directive {
|
||||
match NOQA_LINE_REGEX.captures(line) {
|
||||
Some(caps) => match caps.name("leading_spaces") {
|
||||
Some(leading_spaces) => match caps.name("trailing_spaces") {
|
||||
Some(trailing_spaces) => match caps.name("noqa") {
|
||||
Some(noqa) => match caps.name("codes") {
|
||||
Some(codes) => {
|
||||
let codes: Vec<&str> = SPLIT_COMMA_REGEX
|
||||
.split(codes.as_str().trim())
|
||||
.map(str::trim)
|
||||
.filter(|code| !code.is_empty())
|
||||
.collect();
|
||||
if codes.is_empty() {
|
||||
warn!("Expected rule codes on `noqa` directive: \"{line}\"");
|
||||
}
|
||||
Directive::Codes(
|
||||
leading_spaces.as_str().chars().count(),
|
||||
noqa.start(),
|
||||
noqa.end(),
|
||||
codes,
|
||||
trailing_spaces.as_str().chars().count(),
|
||||
)
|
||||
Some(caps) => match caps.name("spaces") {
|
||||
Some(spaces) => match caps.name("noqa") {
|
||||
Some(noqa) => match caps.name("codes") {
|
||||
Some(codes) => {
|
||||
let codes: Vec<&str> = SPLIT_COMMA_REGEX
|
||||
.split(codes.as_str().trim())
|
||||
.map(str::trim)
|
||||
.filter(|code| !code.is_empty())
|
||||
.collect();
|
||||
if codes.is_empty() {
|
||||
warn!("Expected rule codes on `noqa` directive: \"{line}\"");
|
||||
}
|
||||
None => Directive::All(
|
||||
leading_spaces.as_str().chars().count(),
|
||||
Directive::Codes(
|
||||
spaces.as_str().chars().count(),
|
||||
noqa.start(),
|
||||
noqa.end(),
|
||||
trailing_spaces.as_str().chars().count(),
|
||||
),
|
||||
},
|
||||
None => Directive::None,
|
||||
codes,
|
||||
)
|
||||
}
|
||||
None => {
|
||||
Directive::All(spaces.as_str().chars().count(), noqa.start(), noqa.end())
|
||||
}
|
||||
},
|
||||
None => Directive::None,
|
||||
},
|
||||
@@ -143,7 +135,7 @@ pub fn rule_is_ignored(
|
||||
match extract_noqa_directive(line) {
|
||||
Directive::None => false,
|
||||
Directive::All(..) => true,
|
||||
Directive::Codes(.., codes, _) => includes(code, &codes),
|
||||
Directive::Codes(.., codes) => includes(code, &codes),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +174,7 @@ fn add_noqa_inner(
|
||||
// Codes that are globally exempted (within the current file).
|
||||
let mut file_exemptions: Vec<NoqaCode> = vec![];
|
||||
|
||||
let lines: Vec<&str> = contents.universal_newlines().collect();
|
||||
let lines: Vec<&str> = contents.lines().collect();
|
||||
for lineno in commented_lines {
|
||||
match extract_file_exemption(lines[lineno - 1]) {
|
||||
Exemption::All => {
|
||||
@@ -224,7 +216,7 @@ fn add_noqa_inner(
|
||||
Directive::All(..) => {
|
||||
continue;
|
||||
}
|
||||
Directive::Codes(.., codes, _) => {
|
||||
Directive::Codes(.., codes) => {
|
||||
if includes(diagnostic.kind.rule(), &codes) {
|
||||
continue;
|
||||
}
|
||||
@@ -244,7 +236,7 @@ fn add_noqa_inner(
|
||||
Directive::All(..) => {
|
||||
continue;
|
||||
}
|
||||
Directive::Codes(.., codes, _) => {
|
||||
Directive::Codes(.., codes) => {
|
||||
if includes(diagnostic.kind.rule(), &codes) {
|
||||
continue;
|
||||
}
|
||||
@@ -264,7 +256,7 @@ fn add_noqa_inner(
|
||||
|
||||
let mut count: usize = 0;
|
||||
let mut output = String::new();
|
||||
for (lineno, line) in lines.into_iter().enumerate() {
|
||||
for (lineno, line) in contents.lines().enumerate() {
|
||||
match matches_by_line.get(&lineno) {
|
||||
None => {
|
||||
output.push_str(line);
|
||||
@@ -289,7 +281,7 @@ fn add_noqa_inner(
|
||||
output.push_str(line);
|
||||
output.push_str(line_ending);
|
||||
}
|
||||
Directive::Codes(_, start_byte, _, existing, _) => {
|
||||
Directive::Codes(_, start_byte, _, existing) => {
|
||||
// Reconstruct the line based on the preserved rule codes.
|
||||
// This enables us to tally the number of edits.
|
||||
let mut formatted = String::with_capacity(line.len());
|
||||
|
||||
@@ -146,8 +146,6 @@ ruff_macros::register_rules!(
|
||||
rules::pylint::rules::YieldInInit,
|
||||
rules::pylint::rules::InvalidAllObject,
|
||||
rules::pylint::rules::InvalidAllFormat,
|
||||
rules::pylint::rules::InvalidEnvvarDefault,
|
||||
rules::pylint::rules::InvalidEnvvarValue,
|
||||
rules::pylint::rules::BadStringFormatType,
|
||||
rules::pylint::rules::BidirectionalUnicode,
|
||||
rules::pylint::rules::BadStrStripCall,
|
||||
@@ -341,7 +339,7 @@ ruff_macros::register_rules!(
|
||||
rules::pyupgrade::rules::FString,
|
||||
rules::pyupgrade::rules::FunctoolsCache,
|
||||
rules::pyupgrade::rules::ExtraneousParentheses,
|
||||
rules::pyupgrade::rules::DeprecatedImport,
|
||||
rules::pyupgrade::rules::ImportReplacements,
|
||||
rules::pyupgrade::rules::OutdatedVersionBlock,
|
||||
rules::pyupgrade::rules::QuotedAnnotation,
|
||||
rules::pyupgrade::rules::IsinstanceWithTuple,
|
||||
@@ -805,7 +803,7 @@ impl Linter {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(is_macro::Is, Copy, Clone)]
|
||||
#[derive(is_macro::Is)]
|
||||
pub enum LintSource {
|
||||
Ast,
|
||||
Io,
|
||||
@@ -820,9 +818,9 @@ pub enum LintSource {
|
||||
impl Rule {
|
||||
/// The source for the diagnostic (either the AST, the filesystem, or the
|
||||
/// physical lines).
|
||||
pub const fn lint_source(&self) -> LintSource {
|
||||
pub const fn lint_source(&self) -> &'static LintSource {
|
||||
match self {
|
||||
Rule::UnusedNOQA => LintSource::Noqa,
|
||||
Rule::UnusedNOQA => &LintSource::Noqa,
|
||||
Rule::BlanketNOQA
|
||||
| Rule::BlanketTypeIgnore
|
||||
| Rule::DocLineTooLong
|
||||
@@ -838,7 +836,7 @@ impl Rule {
|
||||
| Rule::ShebangWhitespace
|
||||
| Rule::TrailingWhitespace
|
||||
| Rule::IndentationContainsTabs
|
||||
| Rule::BlankLineContainsWhitespace => LintSource::PhysicalLines,
|
||||
| Rule::BlankLineContainsWhitespace => &LintSource::PhysicalLines,
|
||||
Rule::AmbiguousUnicodeCharacterComment
|
||||
| Rule::AmbiguousUnicodeCharacterDocstring
|
||||
| Rule::AmbiguousUnicodeCharacterString
|
||||
@@ -857,10 +855,10 @@ impl Rule {
|
||||
| Rule::UselessSemicolon
|
||||
| Rule::MultipleStatementsOnOneLineSemicolon
|
||||
| Rule::TrailingCommaProhibited
|
||||
| Rule::TypeCommentInStub => LintSource::Tokens,
|
||||
Rule::IOError => LintSource::Io,
|
||||
Rule::UnsortedImports | Rule::MissingRequiredImport => LintSource::Imports,
|
||||
Rule::ImplicitNamespacePackage | Rule::InvalidModuleName => LintSource::Filesystem,
|
||||
| Rule::TypeCommentInStub => &LintSource::Tokens,
|
||||
Rule::IOError => &LintSource::Io,
|
||||
Rule::UnsortedImports | Rule::MissingRequiredImport => &LintSource::Imports,
|
||||
Rule::ImplicitNamespacePackage | Rule::InvalidModuleName => &LintSource::Filesystem,
|
||||
#[cfg(feature = "logical_lines")]
|
||||
Rule::IndentationWithInvalidMultiple
|
||||
| Rule::IndentationWithInvalidMultipleComment
|
||||
@@ -892,8 +890,8 @@ impl Rule {
|
||||
| Rule::WhitespaceAfterOpenBracket
|
||||
| Rule::WhitespaceBeforeCloseBracket
|
||||
| Rule::WhitespaceBeforeParameters
|
||||
| Rule::WhitespaceBeforePunctuation => LintSource::LogicalLines,
|
||||
_ => LintSource::Ast,
|
||||
| Rule::WhitespaceBeforePunctuation => &LintSource::LogicalLines,
|
||||
_ => &LintSource::Ast,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@ static REDIRECTS: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||
// The following are here because we don't yet have the many-to-one mapping enabled.
|
||||
("SIM111", "SIM110"),
|
||||
// The following are deprecated.
|
||||
("C", "C4"),
|
||||
("C9", "C90"),
|
||||
("T", "T10"),
|
||||
("T1", "T10"),
|
||||
("T2", "T20"),
|
||||
// TODO(charlie): Remove by 2023-02-01.
|
||||
|
||||
@@ -15,17 +15,9 @@ use crate::rule_redirects::get_redirect;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum RuleSelector {
|
||||
/// Select all rules.
|
||||
/// All rules
|
||||
All,
|
||||
/// Legacy category to select both the `mccabe` and `flake8-comprehensions` linters
|
||||
/// via a single selector.
|
||||
C,
|
||||
/// Legacy category to select both the `flake8-debugger` and `flake8-print` linters
|
||||
/// via a single selector.
|
||||
T,
|
||||
/// Select all rules for a given linter.
|
||||
Linter(Linter),
|
||||
/// Select all rules for a given linter with a given prefix.
|
||||
Prefix {
|
||||
prefix: RuleCodePrefix,
|
||||
redirected_from: Option<&'static str>,
|
||||
@@ -44,10 +36,6 @@ impl FromStr for RuleSelector {
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s == "ALL" {
|
||||
Ok(Self::All)
|
||||
} else if s == "C" {
|
||||
Ok(Self::C)
|
||||
} else if s == "T" {
|
||||
Ok(Self::T)
|
||||
} else {
|
||||
let (s, redirected_from) = match get_redirect(s) {
|
||||
Some((from, target)) => (target, Some(from)),
|
||||
@@ -82,8 +70,6 @@ impl RuleSelector {
|
||||
pub fn prefix_and_code(&self) -> (&'static str, &'static str) {
|
||||
match self {
|
||||
RuleSelector::All => ("", "ALL"),
|
||||
RuleSelector::C => ("", "C"),
|
||||
RuleSelector::T => ("", "T"),
|
||||
RuleSelector::Prefix { prefix, .. } => {
|
||||
(prefix.linter().common_prefix(), prefix.short_code())
|
||||
}
|
||||
@@ -152,16 +138,6 @@ impl IntoIterator for &RuleSelector {
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
match self {
|
||||
RuleSelector::All => RuleSelectorIter::All(Rule::iter()),
|
||||
RuleSelector::C => RuleSelectorIter::Chain(
|
||||
Linter::Flake8Comprehensions
|
||||
.into_iter()
|
||||
.chain(Linter::McCabe.into_iter()),
|
||||
),
|
||||
RuleSelector::T => RuleSelectorIter::Chain(
|
||||
Linter::Flake8Debugger
|
||||
.into_iter()
|
||||
.chain(Linter::Flake8Print.into_iter()),
|
||||
),
|
||||
RuleSelector::Linter(linter) => RuleSelectorIter::Vec(linter.into_iter()),
|
||||
RuleSelector::Prefix { prefix, .. } => RuleSelectorIter::Vec(prefix.into_iter()),
|
||||
}
|
||||
@@ -170,7 +146,6 @@ impl IntoIterator for &RuleSelector {
|
||||
|
||||
pub enum RuleSelectorIter {
|
||||
All(RuleIter),
|
||||
Chain(std::iter::Chain<std::vec::IntoIter<Rule>, std::vec::IntoIter<Rule>>),
|
||||
Vec(std::vec::IntoIter<Rule>),
|
||||
}
|
||||
|
||||
@@ -180,14 +155,13 @@ impl Iterator for RuleSelectorIter {
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
RuleSelectorIter::All(iter) => iter.next(),
|
||||
RuleSelectorIter::Chain(iter) => iter.next(),
|
||||
RuleSelectorIter::Vec(iter) => iter.next(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A const alternative to the `impl From<RuleCodePrefix> for RuleSelector`
|
||||
/// to let us keep the fields of [`RuleSelector`] private.
|
||||
// to let us keep the fields of RuleSelector private.
|
||||
// Note that Rust doesn't yet support `impl const From<RuleCodePrefix> for
|
||||
// RuleSelector` (see https://github.com/rust-lang/rust/issues/67792).
|
||||
// TODO(martin): Remove once RuleSelector is an enum with Linter & Rule variants
|
||||
@@ -203,7 +177,7 @@ impl JsonSchema for RuleSelector {
|
||||
"RuleSelector".to_string()
|
||||
}
|
||||
|
||||
fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> Schema {
|
||||
fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
||||
Schema::Object(SchemaObject {
|
||||
instance_type: Some(InstanceType::String.into()),
|
||||
enum_values: Some(
|
||||
@@ -247,8 +221,6 @@ impl RuleSelector {
|
||||
pub(crate) fn specificity(&self) -> Specificity {
|
||||
match self {
|
||||
RuleSelector::All => Specificity::All,
|
||||
RuleSelector::T => Specificity::LinterGroup,
|
||||
RuleSelector::C => Specificity::LinterGroup,
|
||||
RuleSelector::Linter(..) => Specificity::Linter,
|
||||
RuleSelector::Prefix { prefix, .. } => {
|
||||
let prefix: &'static str = prefix.short_code();
|
||||
@@ -268,7 +240,6 @@ impl RuleSelector {
|
||||
#[derive(EnumIter, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) enum Specificity {
|
||||
All,
|
||||
LinterGroup,
|
||||
Linter,
|
||||
Code1Char,
|
||||
Code2Chars,
|
||||
|
||||
@@ -108,7 +108,7 @@ pub fn bad_file_permissions(
|
||||
.map_or(false, |call_path| call_path.as_slice() == ["os", "chmod"])
|
||||
{
|
||||
let call_args = SimpleCallArgs::new(args, keywords);
|
||||
if let Some(mode_arg) = call_args.argument("mode", 1) {
|
||||
if let Some(mode_arg) = call_args.get_argument("mode", Some(1)) {
|
||||
if let Some(int_value) = get_int_value(mode_arg) {
|
||||
if (int_value & WRITE_WORLD > 0) || (int_value & EXECUTE_GROUP > 0) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
|
||||
@@ -25,7 +25,7 @@ impl Violation for HashlibInsecureHashFunction {
|
||||
const WEAK_HASHES: [&str; 4] = ["md4", "md5", "sha", "sha1"];
|
||||
|
||||
fn is_used_for_security(call_args: &SimpleCallArgs) -> bool {
|
||||
match call_args.keyword_argument("usedforsecurity") {
|
||||
match call_args.get_argument("usedforsecurity", None) {
|
||||
Some(expr) => !matches!(
|
||||
&expr.node,
|
||||
ExprKind::Constant {
|
||||
@@ -67,7 +67,7 @@ pub fn hashlib_insecure_hash_functions(
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(name_arg) = call_args.argument("name", 0) {
|
||||
if let Some(name_arg) = call_args.get_argument("name", Some(0)) {
|
||||
if let Some(hash_func_name) = string_literal(name_arg) {
|
||||
if WEAK_HASHES.contains(&hash_func_name.to_lowercase().as_str()) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
|
||||
@@ -46,7 +46,7 @@ pub fn jinja2_autoescape_false(
|
||||
{
|
||||
let call_args = SimpleCallArgs::new(args, keywords);
|
||||
|
||||
if let Some(autoescape_arg) = call_args.keyword_argument("autoescape") {
|
||||
if let Some(autoescape_arg) = call_args.get_argument("autoescape", None) {
|
||||
match &autoescape_arg.node {
|
||||
ExprKind::Constant {
|
||||
value: Constant::Bool(true),
|
||||
|
||||
@@ -33,7 +33,7 @@ pub fn logging_config_insecure_listen(
|
||||
{
|
||||
let call_args = SimpleCallArgs::new(args, keywords);
|
||||
|
||||
if call_args.keyword_argument("verify").is_none() {
|
||||
if call_args.get_argument("verify", None).is_none() {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
LoggingConfigInsecureListen,
|
||||
Range::from(func),
|
||||
|
||||
@@ -56,7 +56,7 @@ pub fn request_with_no_cert_validation(
|
||||
None
|
||||
}) {
|
||||
let call_args = SimpleCallArgs::new(args, keywords);
|
||||
if let Some(verify_arg) = call_args.keyword_argument("verify") {
|
||||
if let Some(verify_arg) = call_args.get_argument("verify", None) {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Bool(false),
|
||||
..
|
||||
|
||||
@@ -44,7 +44,7 @@ pub fn request_without_timeout(
|
||||
})
|
||||
{
|
||||
let call_args = SimpleCallArgs::new(args, keywords);
|
||||
if let Some(timeout_arg) = call_args.keyword_argument("timeout") {
|
||||
if let Some(timeout_arg) = call_args.get_argument("timeout", None) {
|
||||
if let Some(timeout) = match &timeout_arg.node {
|
||||
ExprKind::Constant {
|
||||
value: value @ Constant::None,
|
||||
|
||||
@@ -33,7 +33,7 @@ pub fn snmp_insecure_version(
|
||||
})
|
||||
{
|
||||
let call_args = SimpleCallArgs::new(args, keywords);
|
||||
if let Some(mp_model_arg) = call_args.keyword_argument("mpModel") {
|
||||
if let Some(mp_model_arg) = call_args.get_argument("mpModel", None) {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Int(value),
|
||||
..
|
||||
|
||||
@@ -39,7 +39,7 @@ pub fn unsafe_yaml_load(checker: &mut Checker, func: &Expr, args: &[Expr], keywo
|
||||
.map_or(false, |call_path| call_path.as_slice() == ["yaml", "load"])
|
||||
{
|
||||
let call_args = SimpleCallArgs::new(args, keywords);
|
||||
if let Some(loader_arg) = call_args.argument("Loader", 1) {
|
||||
if let Some(loader_arg) = call_args.get_argument("Loader", Some(1)) {
|
||||
if !checker
|
||||
.ctx
|
||||
.resolve_call_path(loader_arg)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DebuggerUsingType {
|
||||
Call(String),
|
||||
Import(String),
|
||||
|
||||
@@ -146,7 +146,7 @@ pub fn logging_call(checker: &mut Checker, func: &Expr, args: &[Expr], keywords:
|
||||
);
|
||||
|
||||
// G001 - G004
|
||||
if let Some(format_arg) = call_args.argument("msg", 0) {
|
||||
if let Some(format_arg) = call_args.get_argument("msg", Some(0)) {
|
||||
check_msg(checker, format_arg);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
use itertools::Either::{Left, Right};
|
||||
use std::collections::BTreeMap;
|
||||
use std::iter;
|
||||
|
||||
use log::error;
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustpython_parser::ast::{
|
||||
Boolop, Constant, Expr, ExprContext, ExprKind, Keyword, Stmt, StmtKind,
|
||||
};
|
||||
use rustpython_parser::ast::{Boolop, Constant, Expr, ExprKind, Keyword, Stmt, StmtKind};
|
||||
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Violation};
|
||||
use ruff_diagnostics::{Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::comparable::ComparableExpr;
|
||||
use ruff_python_ast::helpers::{create_expr, match_trailing_comment, unparse_expr};
|
||||
use ruff_python_ast::helpers::{match_trailing_comment, unparse_expr};
|
||||
use ruff_python_ast::types::{Range, RefEquality};
|
||||
use ruff_python_stdlib::identifiers::is_identifier;
|
||||
use ruff_python_stdlib::keyword::KWLIST;
|
||||
@@ -126,17 +120,12 @@ pub struct SingleStartsEndsWith {
|
||||
pub attr: String,
|
||||
}
|
||||
|
||||
impl AlwaysAutofixableViolation for SingleStartsEndsWith {
|
||||
impl Violation for SingleStartsEndsWith {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let SingleStartsEndsWith { attr } = self;
|
||||
format!("Call `{attr}` once with a `tuple`")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
let SingleStartsEndsWith { attr } = self;
|
||||
format!("Merge into a single `{attr}` call")
|
||||
}
|
||||
}
|
||||
|
||||
#[violation]
|
||||
@@ -403,116 +392,39 @@ pub fn no_unnecessary_dict_kwargs(checker: &mut Checker, expr: &Expr, kwargs: &[
|
||||
}
|
||||
|
||||
/// PIE810
|
||||
pub fn single_starts_ends_with(checker: &mut Checker, expr: &Expr) {
|
||||
let ExprKind::BoolOp { op: Boolop::Or, values } = &expr.node else {
|
||||
pub fn single_starts_ends_with(checker: &mut Checker, values: &[Expr], node: &Boolop) {
|
||||
if *node != Boolop::Or {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
let mut duplicates = BTreeMap::new();
|
||||
for (index, call) in values.iter().enumerate() {
|
||||
let ExprKind::Call {
|
||||
// Given `foo.startswith`, insert ("foo", "startswith") into the set.
|
||||
let mut seen = FxHashSet::default();
|
||||
for expr in values {
|
||||
if let ExprKind::Call {
|
||||
func,
|
||||
args,
|
||||
keywords,
|
||||
..
|
||||
} = &call.node else {
|
||||
continue
|
||||
};
|
||||
|
||||
if !(args.len() == 1 && keywords.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let ExprKind::Attribute { value, attr, .. } = &func.node else {
|
||||
continue
|
||||
};
|
||||
|
||||
if attr != "startswith" && attr != "endswith" {
|
||||
continue;
|
||||
}
|
||||
|
||||
let ExprKind::Name { id: arg_name, .. } = &value.node else {
|
||||
continue
|
||||
};
|
||||
|
||||
duplicates
|
||||
.entry((attr.as_str(), arg_name.as_str()))
|
||||
.or_insert_with(Vec::new)
|
||||
.push(index);
|
||||
}
|
||||
|
||||
// Generate a `Diagnostic` for each duplicate.
|
||||
for ((attr_name, arg_name), indices) in duplicates {
|
||||
if indices.len() > 1 {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
SingleStartsEndsWith {
|
||||
attr: attr_name.to_string(),
|
||||
},
|
||||
Range::from(expr),
|
||||
);
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
let words: Vec<&Expr> = indices
|
||||
.iter()
|
||||
.map(|index| &values[*index])
|
||||
.map(|expr| {
|
||||
let ExprKind::Call { func: _, args, keywords: _} = &expr.node else {
|
||||
unreachable!("{}", format!("Indices should only contain `{attr_name}` calls"))
|
||||
};
|
||||
args.get(0)
|
||||
.unwrap_or_else(|| panic!("`{attr_name}` should have one argument"))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let call = create_expr(ExprKind::Call {
|
||||
func: Box::new(create_expr(ExprKind::Attribute {
|
||||
value: Box::new(create_expr(ExprKind::Name {
|
||||
id: arg_name.to_string(),
|
||||
ctx: ExprContext::Load,
|
||||
})),
|
||||
attr: attr_name.to_string(),
|
||||
ctx: ExprContext::Load,
|
||||
})),
|
||||
args: vec![create_expr(ExprKind::Tuple {
|
||||
elts: words
|
||||
.iter()
|
||||
.flat_map(|value| {
|
||||
if let ExprKind::Tuple { elts, .. } = &value.node {
|
||||
Left(elts.iter())
|
||||
} else {
|
||||
Right(iter::once(*value))
|
||||
}
|
||||
})
|
||||
.map(Clone::clone)
|
||||
.collect(),
|
||||
ctx: ExprContext::Load,
|
||||
})],
|
||||
keywords: vec![],
|
||||
});
|
||||
|
||||
// Generate the combined `BoolOp`.
|
||||
let mut call = Some(call);
|
||||
let bool_op = create_expr(ExprKind::BoolOp {
|
||||
op: Boolop::Or,
|
||||
values: values
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(index, elt)| {
|
||||
if indices.contains(&index) {
|
||||
std::mem::take(&mut call)
|
||||
} else {
|
||||
Some(elt.clone())
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
});
|
||||
|
||||
diagnostic.amend(Fix::replacement(
|
||||
unparse_expr(&bool_op, checker.stylist),
|
||||
expr.location,
|
||||
expr.end_location.unwrap(),
|
||||
));
|
||||
} = &expr.node
|
||||
{
|
||||
if !(args.len() == 1 && keywords.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
if let ExprKind::Attribute { value, attr, .. } = &func.node {
|
||||
if attr != "startswith" && attr != "endswith" {
|
||||
continue;
|
||||
}
|
||||
if let ExprKind::Name { id, .. } = &value.node {
|
||||
if !seen.insert((id, attr)) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
SingleStartsEndsWith {
|
||||
attr: attr.to_string(),
|
||||
},
|
||||
Range::from(value),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,101 +5,53 @@ expression: diagnostics
|
||||
- kind:
|
||||
name: SingleStartsEndsWith
|
||||
body: "Call `startswith` once with a `tuple`"
|
||||
suggestion: "Merge into a single `startswith` call"
|
||||
fixable: true
|
||||
suggestion: ~
|
||||
fixable: false
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
column: 25
|
||||
end_location:
|
||||
row: 2
|
||||
column: 46
|
||||
fix:
|
||||
content: "obj.startswith((\"foo\", \"bar\"))"
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 2
|
||||
column: 46
|
||||
column: 28
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
name: SingleStartsEndsWith
|
||||
body: "Call `endswith` once with a `tuple`"
|
||||
suggestion: "Merge into a single `endswith` call"
|
||||
fixable: true
|
||||
suggestion: ~
|
||||
fixable: false
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
column: 23
|
||||
end_location:
|
||||
row: 4
|
||||
column: 42
|
||||
fix:
|
||||
content: "obj.endswith((\"foo\", \"bar\"))"
|
||||
location:
|
||||
row: 4
|
||||
column: 0
|
||||
end_location:
|
||||
row: 4
|
||||
column: 42
|
||||
column: 26
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
name: SingleStartsEndsWith
|
||||
body: "Call `startswith` once with a `tuple`"
|
||||
suggestion: "Merge into a single `startswith` call"
|
||||
fixable: true
|
||||
suggestion: ~
|
||||
fixable: false
|
||||
location:
|
||||
row: 6
|
||||
column: 0
|
||||
column: 23
|
||||
end_location:
|
||||
row: 6
|
||||
column: 42
|
||||
fix:
|
||||
content: "obj.startswith((foo, bar))"
|
||||
location:
|
||||
row: 6
|
||||
column: 0
|
||||
end_location:
|
||||
row: 6
|
||||
column: 42
|
||||
column: 26
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
name: SingleStartsEndsWith
|
||||
body: "Call `startswith` once with a `tuple`"
|
||||
suggestion: "Merge into a single `startswith` call"
|
||||
fixable: true
|
||||
suggestion: ~
|
||||
fixable: false
|
||||
location:
|
||||
row: 8
|
||||
column: 0
|
||||
column: 23
|
||||
end_location:
|
||||
row: 8
|
||||
column: 44
|
||||
fix:
|
||||
content: "obj.startswith((foo, \"foo\"))"
|
||||
location:
|
||||
row: 8
|
||||
column: 0
|
||||
end_location:
|
||||
row: 8
|
||||
column: 44
|
||||
parent: ~
|
||||
- kind:
|
||||
name: SingleStartsEndsWith
|
||||
body: "Call `startswith` once with a `tuple`"
|
||||
suggestion: "Merge into a single `startswith` call"
|
||||
fixable: true
|
||||
location:
|
||||
row: 10
|
||||
column: 0
|
||||
end_location:
|
||||
row: 10
|
||||
column: 65
|
||||
fix:
|
||||
content: "obj.endswith(foo) or obj.startswith((foo, \"foo\"))"
|
||||
location:
|
||||
row: 10
|
||||
column: 0
|
||||
end_location:
|
||||
row: 10
|
||||
column: 65
|
||||
column: 26
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::fmt;
|
||||
|
||||
use rustpython_parser::ast::{Expr, ExprKind};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
@@ -8,7 +9,7 @@ use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum VarKind {
|
||||
TypeVar,
|
||||
ParamSpec,
|
||||
|
||||
@@ -28,14 +28,6 @@ impl Violation for ArgumentSimpleDefaults {
|
||||
}
|
||||
}
|
||||
|
||||
const ALLOWED_MATH_ATTRIBUTES_IN_DEFAULTS: &[&[&str]] = &[
|
||||
&["math", "inf"],
|
||||
&["math", "nan"],
|
||||
&["math", "e"],
|
||||
&["math", "pi"],
|
||||
&["math", "tau"],
|
||||
];
|
||||
|
||||
const ALLOWED_ATTRIBUTES_IN_DEFAULTS: &[&[&str]] = &[
|
||||
&["sys", "stdin"],
|
||||
&["sys", "stdout"],
|
||||
@@ -69,74 +61,42 @@ fn is_valid_default_value_with_annotation(default: &Expr, checker: &Checker) ->
|
||||
value: Constant::Bytes(..),
|
||||
..
|
||||
} => return checker.locator.slice(default).len() <= 50,
|
||||
// Ex) `123`, `True`, `False`, `3.14`
|
||||
ExprKind::Constant {
|
||||
value: Constant::Int(..) | Constant::Bool(..) | Constant::Float(..),
|
||||
value: Constant::Int(..),
|
||||
..
|
||||
} => {
|
||||
return checker.locator.slice(default).len() <= 10;
|
||||
}
|
||||
// Ex) `2j`
|
||||
ExprKind::Constant {
|
||||
value: Constant::Complex { real, .. },
|
||||
..
|
||||
} => {
|
||||
if *real == 0.0 {
|
||||
return checker.locator.slice(default).len() <= 10;
|
||||
}
|
||||
}
|
||||
ExprKind::UnaryOp {
|
||||
op: Unaryop::USub,
|
||||
operand,
|
||||
} => {
|
||||
// Ex) `-1`, `-3.14`
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Int(..) | Constant::Float(..),
|
||||
value: Constant::Int(..),
|
||||
..
|
||||
} = &operand.node
|
||||
{
|
||||
return checker.locator.slice(operand).len() <= 10;
|
||||
}
|
||||
// Ex) `-2j`
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Complex { real, .. },
|
||||
..
|
||||
} = &operand.node
|
||||
{
|
||||
if *real == 0.0 {
|
||||
return checker.locator.slice(operand).len() <= 10;
|
||||
}
|
||||
}
|
||||
// Ex) `-math.inf`, `-math.pi`, etc.
|
||||
if let ExprKind::Attribute { .. } = &operand.node {
|
||||
if checker
|
||||
.ctx
|
||||
.resolve_call_path(default)
|
||||
.map_or(false, |call_path| {
|
||||
ALLOWED_MATH_ATTRIBUTES_IN_DEFAULTS.iter().any(|target| {
|
||||
// reject `-math.nan`
|
||||
call_path.as_slice() == *target && *target != ["math", "nan"]
|
||||
})
|
||||
})
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::BinOp {
|
||||
left,
|
||||
op: Operator::Add | Operator::Sub,
|
||||
right,
|
||||
} => {
|
||||
// Ex) `1 + 2j`, `1 - 2j`, `-1 - 2j`, `-1 + 2j`
|
||||
// 1 + 2j
|
||||
// 1 - 2j
|
||||
// -1 - 2j
|
||||
// -1 + 2j
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Complex { .. },
|
||||
..
|
||||
} = right.node
|
||||
{
|
||||
// Ex) `1 + 2j`, `1 - 2j`
|
||||
// 1 + 2j
|
||||
// 1 - 2j
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Int(..) | Constant::Float(..),
|
||||
value: Constant::Int(..),
|
||||
..
|
||||
} = &left.node
|
||||
{
|
||||
@@ -146,9 +106,10 @@ fn is_valid_default_value_with_annotation(default: &Expr, checker: &Checker) ->
|
||||
operand,
|
||||
} = &left.node
|
||||
{
|
||||
// Ex) `-1 + 2j`, `-1 - 2j`
|
||||
// -1 + 2j
|
||||
// -1 - 2j
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Int(..) | Constant::Float(..),
|
||||
value: Constant::Int(..),
|
||||
..
|
||||
} = &operand.node
|
||||
{
|
||||
@@ -157,15 +118,14 @@ fn is_valid_default_value_with_annotation(default: &Expr, checker: &Checker) ->
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ex) `math.inf`, `sys.stdin`, etc.
|
||||
// `sys.stdin`, etc.
|
||||
ExprKind::Attribute { .. } => {
|
||||
if checker
|
||||
.ctx
|
||||
.resolve_call_path(default)
|
||||
.map_or(false, |call_path| {
|
||||
ALLOWED_MATH_ATTRIBUTES_IN_DEFAULTS
|
||||
ALLOWED_ATTRIBUTES_IN_DEFAULTS
|
||||
.iter()
|
||||
.chain(ALLOWED_ATTRIBUTES_IN_DEFAULTS.iter())
|
||||
.any(|target| call_path.as_slice() == *target)
|
||||
})
|
||||
{
|
||||
|
||||
@@ -145,108 +145,4 @@ expression: diagnostics
|
||||
column: 10
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
name: TypedArgumentSimpleDefaults
|
||||
body: Only simple default values allowed for typed arguments
|
||||
suggestion: ~
|
||||
fixable: false
|
||||
location:
|
||||
row: 80
|
||||
column: 15
|
||||
end_location:
|
||||
row: 80
|
||||
column: 23
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
name: TypedArgumentSimpleDefaults
|
||||
body: Only simple default values allowed for typed arguments
|
||||
suggestion: ~
|
||||
fixable: false
|
||||
location:
|
||||
row: 83
|
||||
column: 15
|
||||
end_location:
|
||||
row: 83
|
||||
column: 23
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
name: TypedArgumentSimpleDefaults
|
||||
body: Only simple default values allowed for typed arguments
|
||||
suggestion: ~
|
||||
fixable: false
|
||||
location:
|
||||
row: 86
|
||||
column: 15
|
||||
end_location:
|
||||
row: 86
|
||||
column: 24
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
name: TypedArgumentSimpleDefaults
|
||||
body: Only simple default values allowed for typed arguments
|
||||
suggestion: ~
|
||||
fixable: false
|
||||
location:
|
||||
row: 89
|
||||
column: 15
|
||||
end_location:
|
||||
row: 89
|
||||
column: 18
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
name: TypedArgumentSimpleDefaults
|
||||
body: Only simple default values allowed for typed arguments
|
||||
suggestion: ~
|
||||
fixable: false
|
||||
location:
|
||||
row: 92
|
||||
column: 15
|
||||
end_location:
|
||||
row: 92
|
||||
column: 21
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
name: TypedArgumentSimpleDefaults
|
||||
body: Only simple default values allowed for typed arguments
|
||||
suggestion: ~
|
||||
fixable: false
|
||||
location:
|
||||
row: 95
|
||||
column: 15
|
||||
end_location:
|
||||
row: 95
|
||||
column: 23
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
name: TypedArgumentSimpleDefaults
|
||||
body: Only simple default values allowed for typed arguments
|
||||
suggestion: ~
|
||||
fixable: false
|
||||
location:
|
||||
row: 98
|
||||
column: 15
|
||||
end_location:
|
||||
row: 98
|
||||
column: 24
|
||||
fix: ~
|
||||
parent: ~
|
||||
- kind:
|
||||
name: TypedArgumentSimpleDefaults
|
||||
body: Only simple default values allowed for typed arguments
|
||||
suggestion: ~
|
||||
fixable: false
|
||||
location:
|
||||
row: 101
|
||||
column: 17
|
||||
end_location:
|
||||
row: 102
|
||||
column: 8
|
||||
fix: ~
|
||||
parent: ~
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ impl Violation for FailWithoutMessage {
|
||||
pub fn fail_call(checker: &mut Checker, func: &Expr, args: &[Expr], keywords: &[Keyword]) {
|
||||
if is_pytest_fail(func, checker) {
|
||||
let call_args = SimpleCallArgs::new(args, keywords);
|
||||
let msg = call_args.argument("msg", 0);
|
||||
let msg = call_args.get_argument("msg", Some(0));
|
||||
|
||||
if let Some(msg) = msg {
|
||||
if is_empty_or_null_string(msg) {
|
||||
|
||||
@@ -68,11 +68,11 @@ fn check_patch_call(
|
||||
new_arg_number: usize,
|
||||
) -> Option<Diagnostic> {
|
||||
let simple_args = SimpleCallArgs::new(args, keywords);
|
||||
if simple_args.keyword_argument("return_value").is_some() {
|
||||
if simple_args.get_argument("return_value", None).is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(new_arg) = simple_args.argument("new", new_arg_number) {
|
||||
if let Some(new_arg) = simple_args.get_argument("new", Some(new_arg_number)) {
|
||||
if let ExprKind::Lambda { args, body } = &new_arg.node {
|
||||
// Walk the lambda body.
|
||||
let mut visitor = LambdaBodyVisitor {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Branch {
|
||||
Elif,
|
||||
Else,
|
||||
|
||||
@@ -9,7 +9,6 @@ use ruff_python_ast::helpers::{
|
||||
contains_call_path, contains_effect, create_expr, create_stmt, first_colon_range, has_comments,
|
||||
has_comments_in, unparse_expr, unparse_stmt,
|
||||
};
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -284,7 +283,7 @@ pub fn nested_if_statements(
|
||||
Ok(fix) => {
|
||||
if fix
|
||||
.content
|
||||
.universal_newlines()
|
||||
.lines()
|
||||
.all(|line| line.len() <= checker.settings.line_length)
|
||||
{
|
||||
diagnostic.amend(fix);
|
||||
|
||||
@@ -5,7 +5,6 @@ use ruff_diagnostics::Diagnostic;
|
||||
use ruff_diagnostics::{AutofixKind, Availability, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::{first_colon_range, has_comments_in};
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -116,7 +115,7 @@ pub fn multiple_with_statements(
|
||||
Ok(fix) => {
|
||||
if fix
|
||||
.content
|
||||
.universal_newlines()
|
||||
.lines()
|
||||
.all(|line| line.len() <= checker.settings.line_length)
|
||||
{
|
||||
diagnostic.amend(fix);
|
||||
|
||||
@@ -2,7 +2,6 @@ use rustpython_parser::ast::{Location, Stmt};
|
||||
use rustpython_parser::{lexer, Mode, Tok};
|
||||
|
||||
use ruff_python_ast::helpers::is_docstring_stmt;
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
|
||||
use super::types::TrailingComma;
|
||||
@@ -63,7 +62,7 @@ pub fn has_comment_break(stmt: &Stmt, locator: &Locator) -> bool {
|
||||
// # Direct comment.
|
||||
// def f(): pass
|
||||
let mut seen_blank = false;
|
||||
for line in locator.take(stmt.location).universal_newlines().rev() {
|
||||
for line in locator.take(stmt.location).lines().rev() {
|
||||
let line = line.trim();
|
||||
if seen_blank {
|
||||
if line.starts_with('#') {
|
||||
|
||||
@@ -4,24 +4,6 @@ use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::rules::pycodestyle::helpers::is_ambiguous_name;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the use of the characters 'l', 'O', or 'I' as class names.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// In some fonts, these characters are indistinguishable from the
|
||||
/// numerals one and zero. When tempted to use 'l', use 'L' instead.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// class I(object):
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// class Integer(object):
|
||||
/// ...
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct AmbiguousClassName(pub String);
|
||||
|
||||
|
||||
@@ -4,24 +4,6 @@ use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::rules::pycodestyle::helpers::is_ambiguous_name;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the use of the characters 'l', 'O', or 'I' as function names.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// In some fonts, these characters are indistinguishable from the
|
||||
/// numerals one and zero. When tempted to use 'l', use 'L' instead.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// def l(x):
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// def long_name(x):
|
||||
/// ...
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct AmbiguousFunctionName(pub String);
|
||||
|
||||
|
||||
@@ -4,27 +4,6 @@ use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::rules::pycodestyle::helpers::is_ambiguous_name;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the use of the characters 'l', 'O', or 'I' as variable names.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// In some fonts, these characters are indistinguishable from the
|
||||
/// numerals one and zero. When tempted to use 'l', use 'L' instead.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// l = 0
|
||||
/// O = 123
|
||||
/// I = 42
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// L = 0
|
||||
/// o = 123
|
||||
/// i = 42
|
||||
/// ```
|
||||
|
||||
#[violation]
|
||||
pub struct AmbiguousVariableName(pub String);
|
||||
|
||||
|
||||
@@ -9,25 +9,6 @@ use ruff_python_ast::types::Range;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::{flags, Settings};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for compound statements (multiple statements on the same line).
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, "compound statements are generally discouraged".
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// if foo == 'blah': do_blah_thing()
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// if foo == 'blah':
|
||||
/// do_blah_thing()
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#other-recommendations)
|
||||
#[violation]
|
||||
pub struct MultipleStatementsOnOneLineColon;
|
||||
|
||||
@@ -38,27 +19,6 @@ impl Violation for MultipleStatementsOnOneLineColon {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for multiline statements on one line.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, including multi-clause statements on the same line is
|
||||
/// discouraged.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// do_one(); do_two(); do_three()
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// do_one()
|
||||
/// do_two()
|
||||
/// do_three()
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#other-recommendations)
|
||||
#[violation]
|
||||
pub struct MultipleStatementsOnOneLineSemicolon;
|
||||
|
||||
@@ -69,21 +29,6 @@ impl Violation for MultipleStatementsOnOneLineSemicolon {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for statements that end with an unnecessary semicolon.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// A trailing semicolon is unnecessary and should be removed.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// do_four(); # useless semicolon
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// do_four()
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct UselessSemicolon;
|
||||
|
||||
@@ -98,7 +43,6 @@ impl AlwaysAutofixableViolation for UselessSemicolon {
|
||||
}
|
||||
}
|
||||
|
||||
/// E701, E702, E703
|
||||
pub fn compound_statements(
|
||||
lxr: &[LexResult],
|
||||
settings: &Settings,
|
||||
|
||||
@@ -7,27 +7,6 @@ use ruff_python_ast::types::Range;
|
||||
use crate::rules::pycodestyle::helpers::is_overlong;
|
||||
use crate::settings::Settings;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for doc lines that exceed the specified maximum character length.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// For flowing long blocks of text (docstrings or comments), overlong lines
|
||||
/// can hurt readability.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// def function(x):
|
||||
/// """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis auctor purus ut ex fermentum, at maximus est hendrerit."""
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// def function(x):
|
||||
/// """
|
||||
/// Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
/// Duis auctor purus ut ex fermentum, at maximus est hendrerit.
|
||||
/// """
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct DocLineTooLong(pub usize, pub usize);
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ pub struct IOError {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
/// E902
|
||||
impl Violation for IOError {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
@@ -31,7 +30,6 @@ impl Violation for SyntaxError {
|
||||
}
|
||||
}
|
||||
|
||||
/// E901
|
||||
pub fn syntax_error(diagnostics: &mut Vec<Diagnostic>, parse_error: &ParseError) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
SyntaxError {
|
||||
|
||||
@@ -7,28 +7,6 @@ use ruff_diagnostics::DiagnosticKind;
|
||||
use ruff_diagnostics::Violation;
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the use of extraneous whitespace after "(".
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// PEP 8 recommends the omission of whitespace in the following cases:
|
||||
/// - "Immediately inside parentheses, brackets or braces."
|
||||
/// - "Immediately before a comma, semicolon, or colon."
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// spam( ham[1], {eggs: 2})
|
||||
/// spam(ham[ 1], {eggs: 2})
|
||||
/// spam(ham[1], { eggs: 2})
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// spam(ham[1], {eggs: 2})
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#pet-peeves)
|
||||
#[violation]
|
||||
pub struct WhitespaceAfterOpenBracket;
|
||||
|
||||
@@ -39,28 +17,6 @@ impl Violation for WhitespaceAfterOpenBracket {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the use of extraneous whitespace before ")".
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// PEP 8 recommends the omission of whitespace in the following cases:
|
||||
/// - "Immediately inside parentheses, brackets or braces."
|
||||
/// - "Immediately before a comma, semicolon, or colon."
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// spam(ham[1], {eggs: 2} )
|
||||
/// spam(ham[1 ], {eggs: 2})
|
||||
/// spam(ham[1], {eggs: 2 })
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// spam(ham[1], {eggs: 2})
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#pet-peeves)
|
||||
#[violation]
|
||||
pub struct WhitespaceBeforeCloseBracket;
|
||||
|
||||
@@ -71,26 +27,6 @@ impl Violation for WhitespaceBeforeCloseBracket {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the use of extraneous whitespace before ",", ";" or ":".
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// PEP 8 recommends the omission of whitespace in the following cases:
|
||||
/// - "Immediately inside parentheses, brackets or braces."
|
||||
/// - "Immediately before a comma, semicolon, or colon."
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// if x == 4: print(x, y); x, y = y , x
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// if x == 4: print(x, y); x, y = y, x
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#pet-peeves)
|
||||
#[violation]
|
||||
pub struct WhitespaceBeforePunctuation;
|
||||
|
||||
|
||||
@@ -6,25 +6,6 @@ use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Check for multiple imports on one line.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, "imports should usually be on separate lines."
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// import sys, os
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// import os
|
||||
/// import sys
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#imports)
|
||||
#[violation]
|
||||
pub struct MultipleImportsOnOneLine;
|
||||
|
||||
@@ -35,33 +16,6 @@ impl Violation for MultipleImportsOnOneLine {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for imports that are not at the top of the file.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, "imports are always put at the top of the file, just after any
|
||||
/// module comments and docstrings, and before module globals and constants."
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// 'One string'
|
||||
/// "Two string"
|
||||
/// a = 1
|
||||
/// import os
|
||||
/// from sys import x
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// import os
|
||||
/// from sys import x
|
||||
/// 'One string'
|
||||
/// "Two string"
|
||||
/// a = 1
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#imports)
|
||||
#[violation]
|
||||
pub struct ModuleImportNotAtTopOfFile;
|
||||
|
||||
@@ -72,7 +26,6 @@ impl Violation for ModuleImportNotAtTopOfFile {
|
||||
}
|
||||
}
|
||||
|
||||
/// E401
|
||||
pub fn multiple_imports_on_one_line(checker: &mut Checker, stmt: &Stmt, names: &[Alias]) {
|
||||
if names.len() > 1 {
|
||||
checker
|
||||
@@ -81,7 +34,6 @@ pub fn multiple_imports_on_one_line(checker: &mut Checker, stmt: &Stmt, names: &
|
||||
}
|
||||
}
|
||||
|
||||
/// E402
|
||||
pub fn module_import_not_at_top_of_file(checker: &mut Checker, stmt: &Stmt) {
|
||||
if checker.ctx.seen_import_boundary && stmt.location.column() == 0 {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
|
||||
@@ -6,26 +6,6 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
use crate::rules::pycodestyle::logical_lines::LogicalLine;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for indentation with a non-multiple of 4 spaces.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, 4 spaces per indentation level should be preferred.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// if True:
|
||||
/// a = 1
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// if True:
|
||||
/// a = 1
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#indentation)
|
||||
#[violation]
|
||||
pub struct IndentationWithInvalidMultiple {
|
||||
pub indent_size: usize,
|
||||
@@ -39,26 +19,6 @@ impl Violation for IndentationWithInvalidMultiple {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for indentation of comments with a non-multiple of 4 spaces.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, 4 spaces per indentation level should be preferred.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// if True:
|
||||
/// # a = 1
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// if True:
|
||||
/// # a = 1
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#indentation)
|
||||
#[violation]
|
||||
pub struct IndentationWithInvalidMultipleComment {
|
||||
pub indent_size: usize,
|
||||
@@ -72,28 +32,6 @@ impl Violation for IndentationWithInvalidMultipleComment {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for indented blocks that are lacking indentation.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// All indented blocks should be indented; otherwise, they are not valid
|
||||
/// Python syntax.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// for item in items:
|
||||
/// pass
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// for item in items:
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#indentation)
|
||||
#[violation]
|
||||
pub struct NoIndentedBlock;
|
||||
|
||||
@@ -104,29 +42,6 @@ impl Violation for NoIndentedBlock {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for comments in a code blocks that are lacking indentation.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Comments within an indented block should themselves be indented, to
|
||||
/// indicate that they are part of the block.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// for item in items:
|
||||
/// # Hi
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// for item in items:
|
||||
/// # Hi
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#indentation)
|
||||
#[violation]
|
||||
pub struct NoIndentedBlockComment;
|
||||
|
||||
@@ -137,26 +52,6 @@ impl Violation for NoIndentedBlockComment {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for unexpected indentation.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Indentation outside of a code block is not valid Python syntax.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// a = 1
|
||||
/// b = 2
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// a = 1
|
||||
/// b = 2
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#indentation)
|
||||
#[violation]
|
||||
pub struct UnexpectedIndentation;
|
||||
|
||||
@@ -167,26 +62,6 @@ impl Violation for UnexpectedIndentation {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for unexpected indentation of comment.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Comments should match the indentation of the containing code block.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// a = 1
|
||||
/// # b = 2
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// a = 1
|
||||
/// # b = 2
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#indentation)
|
||||
#[violation]
|
||||
pub struct UnexpectedIndentationComment;
|
||||
|
||||
@@ -197,28 +72,6 @@ impl Violation for UnexpectedIndentationComment {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for over-indented code.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, 4 spaces per indentation level should be preferred. Increased
|
||||
/// indentation can lead to inconsistent formatting, which can hurt
|
||||
/// readability.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// for item in items:
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// for item in items:
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#indentation)
|
||||
#[violation]
|
||||
pub struct OverIndented;
|
||||
|
||||
@@ -229,7 +82,7 @@ impl Violation for OverIndented {
|
||||
}
|
||||
}
|
||||
|
||||
/// E111, E114, E112, E113, E115, E116, E117
|
||||
/// E111
|
||||
#[cfg(feature = "logical_lines")]
|
||||
pub fn indentation(
|
||||
logical_line: &LogicalLine,
|
||||
|
||||
@@ -4,25 +4,9 @@ use rustpython_parser::ast::Location;
|
||||
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for invalid escape sequences.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Invalid escape sequences are deprecated in Python 3.6.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// regex = '\.png$'
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// regex = r'\.png$'
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct InvalidEscapeSequence(pub char);
|
||||
|
||||
@@ -77,7 +61,7 @@ pub fn invalid_escape_sequence(
|
||||
let body = &text[(quote_pos + quote.len())..(text.len() - quote.len())];
|
||||
|
||||
if !prefix.contains('r') {
|
||||
for (row_offset, line) in body.universal_newlines().enumerate() {
|
||||
for (row_offset, line) in body.lines().enumerate() {
|
||||
let chars: Vec<char> = line.chars().collect();
|
||||
for col_offset in 0..chars.len() {
|
||||
if chars[col_offset] != '\\' {
|
||||
|
||||
@@ -3,7 +3,6 @@ use rustpython_parser::ast::{Arguments, Expr, ExprKind, Location, Stmt, StmtKind
|
||||
use ruff_diagnostics::{AutofixKind, Availability, Diagnostic, Fix, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::{match_leading_content, match_trailing_content, unparse_stmt};
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::source_code::Stylist;
|
||||
use ruff_python_ast::types::{Range, ScopeKind};
|
||||
use ruff_python_ast::whitespace::leading_space;
|
||||
@@ -11,30 +10,6 @@ use ruff_python_ast::whitespace::leading_space;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for lambda expressions which are assigned to a variable.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, you should "Always use a def statement instead of an assignment
|
||||
/// statement that binds a lambda expression directly to an identifier."
|
||||
///
|
||||
/// Using a `def` statement leads to better tracebacks, and the assignment
|
||||
/// itself negates the primary benefit of using a `lambda` expression (i.e.,
|
||||
/// that it can be embedded inside another expression).
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// f = lambda x: 2*x
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// def f(x):
|
||||
/// return 2 * x
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#programming-recommendations)
|
||||
#[violation]
|
||||
pub struct LambdaAssignment {
|
||||
pub name: String,
|
||||
@@ -87,7 +62,7 @@ pub fn lambda_assignment(checker: &mut Checker, target: &Expr, value: &Expr, stm
|
||||
let indentation = &leading_space(first_line);
|
||||
let mut indented = String::new();
|
||||
for (idx, line) in function(id, args, body, checker.stylist)
|
||||
.universal_newlines()
|
||||
.lines()
|
||||
.enumerate()
|
||||
{
|
||||
if idx == 0 {
|
||||
|
||||
@@ -7,24 +7,6 @@ use ruff_python_ast::types::Range;
|
||||
use crate::rules::pycodestyle::helpers::is_overlong;
|
||||
use crate::settings::Settings;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for lines that exceed the specified maximum character length.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Overlong lines can hurt readability.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// my_function(param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// my_function(
|
||||
/// param1, param2, param3, param4, param5,
|
||||
/// param6, param7, param8, param9, param10
|
||||
/// )
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct LineTooLong(pub usize, pub usize);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use itertools::izip;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_parser::ast::{Cmpop, Constant, Expr, ExprKind};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
@@ -11,7 +12,7 @@ use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
use crate::rules::pycodestyle::helpers::compare;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum EqCmpop {
|
||||
Eq,
|
||||
NotEq,
|
||||
@@ -27,26 +28,6 @@ impl From<&Cmpop> for EqCmpop {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for comparisons to `None` which are not using the `is` operator.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, "Comparisons to singletons like None should always be done with
|
||||
/// is or is not, never the equality operators."
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// if arg != None:
|
||||
/// if None == arg:
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// if arg is not None:
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#programming-recommendations)
|
||||
#[violation]
|
||||
pub struct NoneComparison(pub EqCmpop);
|
||||
|
||||
@@ -69,27 +50,6 @@ impl AlwaysAutofixableViolation for NoneComparison {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for comparisons to booleans which are not using the `is` operator.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, "Comparisons to singletons like None should always be done with
|
||||
/// is or is not, never the equality operators."
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// if arg == True:
|
||||
/// if False == arg:
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// if arg is True:
|
||||
/// if arg is False:
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#programming-recommendations)
|
||||
#[violation]
|
||||
pub struct TrueFalseComparison(pub bool, pub EqCmpop);
|
||||
|
||||
|
||||
@@ -5,26 +5,6 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::Range;
|
||||
use ruff_python_ast::whitespace::leading_space;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for mixed tabs and spaces in indentation.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Never mix tabs and spaces.
|
||||
///
|
||||
/// The most popular way of indenting Python is with spaces only. The
|
||||
/// second-most popular way is with tabs only. Code indented with a
|
||||
/// mixture of tabs and spaces should be converted to using spaces
|
||||
/// exclusively.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// if a == 0:\n a = 1\n\tb = 1
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// if a == 0:\n a = 1\n b = 1
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct MixedSpacesAndTabs;
|
||||
|
||||
|
||||
@@ -2,26 +2,9 @@ use rustpython_parser::ast::Location;
|
||||
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::source_code::{Locator, Stylist};
|
||||
use ruff_python_ast::source_code::Stylist;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for files missing a new line at the end of the file.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Trailing blank lines are superfluous.
|
||||
/// However the last line should end with a new line.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// spam(1)
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// spam(1)\n
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct NoNewLineAtEndOfFile;
|
||||
|
||||
@@ -38,16 +21,16 @@ impl AlwaysAutofixableViolation for NoNewLineAtEndOfFile {
|
||||
|
||||
/// W292
|
||||
pub fn no_newline_at_end_of_file(
|
||||
locator: &Locator,
|
||||
stylist: &Stylist,
|
||||
contents: &str,
|
||||
autofix: bool,
|
||||
) -> Option<Diagnostic> {
|
||||
if !locator.contents().ends_with(['\n', '\r']) {
|
||||
if !contents.ends_with('\n') {
|
||||
// Note: if `lines.last()` is `None`, then `contents` is empty (and so we don't
|
||||
// want to raise W292 anyway).
|
||||
if let Some(line) = locator.contents().universal_newlines().last() {
|
||||
if let Some(line) = contents.lines().last() {
|
||||
// Both locations are at the end of the file (and thus the same).
|
||||
let location = Location::new(locator.count_lines(), line.len());
|
||||
let location = Location::new(contents.lines().count(), line.len());
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(NoNewLineAtEndOfFile, Range::new(location, location));
|
||||
if autofix {
|
||||
|
||||
@@ -8,25 +8,6 @@ use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
use crate::rules::pycodestyle::helpers::compare;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for negative comparison using `not {foo} in {bar}`.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Negative comparison should be done using `not in`.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// Z = not X in Y
|
||||
/// if not X.B in Y:\n pass
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// if x not in y:\n pass
|
||||
/// assert (X in Y or X is Z)
|
||||
///
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct NotInTest;
|
||||
|
||||
@@ -41,25 +22,6 @@ impl AlwaysAutofixableViolation for NotInTest {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for negative comparison using `not {foo} is {bar}`.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Negative comparison should be done using `is not`.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// if not X is Y:
|
||||
/// pass
|
||||
/// Z = not X.B is Y
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// if not (X in Y):
|
||||
/// pass
|
||||
/// zz = x is not y
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct NotIsTest;
|
||||
|
||||
|
||||
@@ -7,25 +7,6 @@ use ruff_diagnostics::DiagnosticKind;
|
||||
use ruff_diagnostics::Violation;
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for extraneous tabs before an operator.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, operators should be surrounded by at most a single space on either
|
||||
/// side.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// a = 4\t+ 5
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// a = 12 + 3
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#whitespace-in-expressions-and-statements)
|
||||
#[violation]
|
||||
pub struct TabBeforeOperator;
|
||||
|
||||
@@ -36,25 +17,6 @@ impl Violation for TabBeforeOperator {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for extraneous whitespace before an operator.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, operators should be surrounded by at most a single space on either
|
||||
/// side.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// a = 4 + 5
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// a = 12 + 3
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#whitespace-in-expressions-and-statements)
|
||||
#[violation]
|
||||
pub struct MultipleSpacesBeforeOperator;
|
||||
|
||||
@@ -65,25 +27,6 @@ impl Violation for MultipleSpacesBeforeOperator {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for extraneous tabs after an operator.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, operators should be surrounded by at most a single space on either
|
||||
/// side.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// a = 4 +\t5
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// a = 12 + 3
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#whitespace-in-expressions-and-statements)
|
||||
#[violation]
|
||||
pub struct TabAfterOperator;
|
||||
|
||||
@@ -94,25 +37,6 @@ impl Violation for TabAfterOperator {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for extraneous whitespace after an operator.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, operators should be surrounded by at most a single space on either
|
||||
/// side.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// a = 4 + 5
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// a = 12 + 3
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#whitespace-in-expressions-and-statements)
|
||||
#[violation]
|
||||
pub struct MultipleSpacesAfterOperator;
|
||||
|
||||
|
||||
@@ -7,25 +7,6 @@ use ruff_python_ast::types::Range;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::{flags, Settings};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for superfluous trailing whitespace.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, "avoid trailing whitespace anywhere. Because it’s usually
|
||||
/// invisible, it can be confusing"
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// spam(1) \n#
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// spam(1)\n#
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#other-recommendations)
|
||||
#[violation]
|
||||
pub struct TrailingWhitespace;
|
||||
|
||||
@@ -40,26 +21,6 @@ impl AlwaysAutofixableViolation for TrailingWhitespace {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for superfluous whitespace in blank lines.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP 8, "avoid trailing whitespace anywhere. Because it’s usually
|
||||
/// invisible, it can be confusing"
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// class Foo(object):\n \n bang = 12
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// class Foo(object):\n\n bang = 12
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#other-recommendations)
|
||||
#[violation]
|
||||
pub struct BlankLineContainsWhitespace;
|
||||
|
||||
|
||||
@@ -5,24 +5,6 @@ use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for object type comparisons without using isinstance().
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Do not compare types directly.
|
||||
/// When checking if an object is a instance of a certain type, keep in mind that it might
|
||||
/// be subclassed. E.g. `bool` inherits from `int` or `Exception` inherits from `BaseException`.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// if type(obj) is type(1):
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// if isinstance(obj, int):
|
||||
/// if type(a1) is type(b1):
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct TypeComparison;
|
||||
|
||||
|
||||
@@ -7,21 +7,6 @@ use ruff_diagnostics::DiagnosticKind;
|
||||
use ruff_diagnostics::Violation;
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for extraneous whitespace after keywords.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
///
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// True and False
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// True and False
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct MultipleSpacesAfterKeyword;
|
||||
|
||||
@@ -32,22 +17,6 @@ impl Violation for MultipleSpacesAfterKeyword {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for extraneous whitespace before keywords.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
///
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// True and False
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// True and False
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct MultipleSpacesBeforeKeyword;
|
||||
|
||||
@@ -58,22 +27,6 @@ impl Violation for MultipleSpacesBeforeKeyword {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for extraneous tabs after keywords.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
///
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// True and\tFalse
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// True and False
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct TabAfterKeyword;
|
||||
|
||||
@@ -84,22 +37,6 @@ impl Violation for TabAfterKeyword {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for extraneous tabs before keywords.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
///
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// True\tand False
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// True and False
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct TabBeforeKeyword;
|
||||
|
||||
|
||||
@@ -9,25 +9,6 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks if inline comments are separated by at least two spaces.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// An inline comment is a comment on the same line as a statement.
|
||||
///
|
||||
/// Per PEP8, inline comments should be separated by at least two spaces from
|
||||
/// the preceding statement.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// x = x + 1 # Increment x
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// x = x + 1 # Increment x
|
||||
/// x = x + 1 # Increment x
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct TooFewSpacesBeforeInlineComment;
|
||||
|
||||
@@ -38,29 +19,6 @@ impl Violation for TooFewSpacesBeforeInlineComment {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks if one space is used after inline comments.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// An inline comment is a comment on the same line as a statement.
|
||||
///
|
||||
/// Per PEP8, inline comments should start with a # and a single space.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// x = x + 1 #Increment x
|
||||
/// x = x + 1 # Increment x
|
||||
/// x = x + 1 # \xa0Increment x
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// x = x + 1 # Increment x
|
||||
/// x = x + 1 # Increment x
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#comments)
|
||||
#[violation]
|
||||
pub struct NoSpaceAfterInlineComment;
|
||||
|
||||
@@ -71,29 +29,6 @@ impl Violation for NoSpaceAfterInlineComment {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks if one space is used after block comments.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP8, "Block comments generally consist of one or more paragraphs built
|
||||
/// out of complete sentences, with each sentence ending in a period."
|
||||
///
|
||||
/// Block comments should start with a # and a single space.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// #Block comment
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// # Block comments:
|
||||
/// # - Block comment list
|
||||
/// # \xa0- Block comment list
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#comments)
|
||||
#[violation]
|
||||
pub struct NoSpaceAfterBlockComment;
|
||||
|
||||
@@ -104,30 +39,6 @@ impl Violation for NoSpaceAfterBlockComment {
|
||||
}
|
||||
}
|
||||
|
||||
/// ## What it does
|
||||
/// Checks if block comments start with a single "#".
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Per PEP8, "Block comments generally consist of one or more paragraphs built
|
||||
/// out of complete sentences, with each sentence ending in a period."
|
||||
///
|
||||
/// Each line of a block comment should start with a # and a single space.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// ### Block comment
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// # Block comments:
|
||||
/// # - Block comment list
|
||||
/// # \xa0- Block comment list
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [PEP 8](https://peps.python.org/pep-0008/#comments)
|
||||
#[violation]
|
||||
pub struct MultipleLeadingHashesForBlockComment;
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::collections::BTreeSet;
|
||||
|
||||
use ruff_python_ast::cast;
|
||||
use ruff_python_ast::helpers::{map_callable, to_call_path};
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::docstrings::definition::{Definition, DefinitionKind};
|
||||
@@ -11,7 +10,7 @@ use crate::docstrings::definition::{Definition, DefinitionKind};
|
||||
pub fn logical_line(content: &str) -> Option<usize> {
|
||||
// Find the first logical line.
|
||||
let mut logical_line = None;
|
||||
for (i, line) in content.universal_newlines().enumerate() {
|
||||
for (i, line) in content.lines().enumerate() {
|
||||
if line.trim().is_empty() {
|
||||
// Empty line. If this is the line _after_ the first logical line, stop.
|
||||
if logical_line.is_some() {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use ruff_diagnostics::{AutofixKind, Availability, Diagnostic, Fix, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -46,7 +45,7 @@ pub fn blank_after_summary(checker: &mut Checker, docstring: &Docstring) {
|
||||
|
||||
let mut lines_count = 1;
|
||||
let mut blanks_count = 0;
|
||||
for line in body.trim().universal_newlines().skip(1) {
|
||||
for line in body.trim().lines().skip(1) {
|
||||
lines_count += 1;
|
||||
if line.trim().is_empty() {
|
||||
blanks_count += 1;
|
||||
@@ -65,7 +64,7 @@ pub fn blank_after_summary(checker: &mut Checker, docstring: &Docstring) {
|
||||
if blanks_count > 1 {
|
||||
// Find the "summary" line (defined as the first non-blank line).
|
||||
let mut summary_line = 0;
|
||||
for line in body.universal_newlines() {
|
||||
for line in body.lines() {
|
||||
if line.trim().is_empty() {
|
||||
summary_line += 1;
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -76,7 +75,7 @@ pub fn blank_before_after_class(checker: &mut Checker, docstring: &Docstring) {
|
||||
.slice(Range::new(parent.location, docstring.expr.location));
|
||||
|
||||
let blank_lines_before = before
|
||||
.universal_newlines()
|
||||
.lines()
|
||||
.rev()
|
||||
.skip(1)
|
||||
.take_while(|line| line.trim().is_empty())
|
||||
@@ -139,7 +138,7 @@ pub fn blank_before_after_class(checker: &mut Checker, docstring: &Docstring) {
|
||||
));
|
||||
|
||||
let all_blank_after = after
|
||||
.universal_newlines()
|
||||
.lines()
|
||||
.skip(1)
|
||||
.all(|line| line.trim().is_empty() || line.trim_start().starts_with('#'));
|
||||
if all_blank_after {
|
||||
@@ -147,7 +146,7 @@ pub fn blank_before_after_class(checker: &mut Checker, docstring: &Docstring) {
|
||||
}
|
||||
|
||||
let blank_lines_after = after
|
||||
.universal_newlines()
|
||||
.lines()
|
||||
.skip(1)
|
||||
.take_while(|line| line.trim().is_empty())
|
||||
.count();
|
||||
|
||||
@@ -3,7 +3,6 @@ use regex::Regex;
|
||||
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -68,7 +67,7 @@ pub fn blank_before_after_function(checker: &mut Checker, docstring: &Docstring)
|
||||
.slice(Range::new(parent.location, docstring.expr.location));
|
||||
|
||||
let blank_lines_before = before
|
||||
.universal_newlines()
|
||||
.lines()
|
||||
.rev()
|
||||
.skip(1)
|
||||
.take_while(|line| line.trim().is_empty())
|
||||
@@ -103,7 +102,7 @@ pub fn blank_before_after_function(checker: &mut Checker, docstring: &Docstring)
|
||||
|
||||
// If the docstring is only followed by blank and commented lines, abort.
|
||||
let all_blank_after = after
|
||||
.universal_newlines()
|
||||
.lines()
|
||||
.skip(1)
|
||||
.all(|line| line.trim().is_empty() || line.trim_start().starts_with('#'));
|
||||
if all_blank_after {
|
||||
@@ -112,7 +111,7 @@ pub fn blank_before_after_function(checker: &mut Checker, docstring: &Docstring)
|
||||
|
||||
// Count the number of blank lines after the docstring.
|
||||
let blank_lines_after = after
|
||||
.universal_newlines()
|
||||
.lines()
|
||||
.skip(1)
|
||||
.take_while(|line| line.trim().is_empty())
|
||||
.count();
|
||||
@@ -120,7 +119,7 @@ pub fn blank_before_after_function(checker: &mut Checker, docstring: &Docstring)
|
||||
// Avoid violations for blank lines followed by inner functions or classes.
|
||||
if blank_lines_after == 1
|
||||
&& after
|
||||
.universal_newlines()
|
||||
.lines()
|
||||
.skip(1 + blank_lines_after)
|
||||
.find(|line| !line.trim_start().starts_with('#'))
|
||||
.map_or(false, |line| INNER_FUNCTION_OR_CLASS_REGEX.is_match(line))
|
||||
|
||||
@@ -2,7 +2,6 @@ use strum::IntoEnumIterator;
|
||||
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::str::leading_quote;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
@@ -32,7 +31,7 @@ pub fn ends_with_period(checker: &mut Checker, docstring: &Docstring) {
|
||||
let contents = docstring.contents;
|
||||
let body = docstring.body;
|
||||
|
||||
if let Some(first_line) = body.trim().universal_newlines().next() {
|
||||
if let Some(first_line) = body.trim().lines().next() {
|
||||
let trimmed = first_line.trim();
|
||||
|
||||
// Avoid false-positives: `:param`, etc.
|
||||
@@ -56,7 +55,7 @@ pub fn ends_with_period(checker: &mut Checker, docstring: &Docstring) {
|
||||
}
|
||||
|
||||
if let Some(index) = logical_line(body) {
|
||||
let line = body.universal_newlines().nth(index).unwrap();
|
||||
let line = body.lines().nth(index).unwrap();
|
||||
let trimmed = line.trim_end();
|
||||
|
||||
if !trimmed.ends_with('.') {
|
||||
|
||||
@@ -2,7 +2,6 @@ use strum::IntoEnumIterator;
|
||||
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::str::leading_quote;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
@@ -32,7 +31,7 @@ pub fn ends_with_punctuation(checker: &mut Checker, docstring: &Docstring) {
|
||||
let contents = docstring.contents;
|
||||
let body = docstring.body;
|
||||
|
||||
if let Some(first_line) = body.trim().universal_newlines().next() {
|
||||
if let Some(first_line) = body.trim().lines().next() {
|
||||
let trimmed = first_line.trim();
|
||||
|
||||
// Avoid false-positives: `:param`, etc.
|
||||
@@ -56,7 +55,7 @@ pub fn ends_with_punctuation(checker: &mut Checker, docstring: &Docstring) {
|
||||
}
|
||||
|
||||
if let Some(index) = logical_line(body) {
|
||||
let line = body.universal_newlines().nth(index).unwrap();
|
||||
let line = body.lines().nth(index).unwrap();
|
||||
let trimmed = line.trim_end();
|
||||
if !(trimmed.ends_with('.') || trimmed.ends_with('!') || trimmed.ends_with('?')) {
|
||||
let mut diagnostic = Diagnostic::new(EndsInPunctuation, Range::from(docstring.expr));
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Violation};
|
||||
use ruff_diagnostics::{Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::newlines::NewlineWithTrailingNewline;
|
||||
use ruff_python_ast::types::Range;
|
||||
use ruff_python_ast::whitespace;
|
||||
use ruff_python_ast::whitespace::LinesWithTrailingNewline;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::docstrings::definition::Docstring;
|
||||
@@ -53,7 +53,7 @@ pub fn indent(checker: &mut Checker, docstring: &Docstring) {
|
||||
let body = docstring.body;
|
||||
|
||||
// Split the docstring into lines.
|
||||
let lines: Vec<&str> = NewlineWithTrailingNewline::from(body).collect();
|
||||
let lines: Vec<&str> = LinesWithTrailingNewline::from(body).collect();
|
||||
if lines.len() <= 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::newlines::{NewlineWithTrailingNewline, StrExt};
|
||||
use ruff_python_ast::str::{is_triple_quote, leading_quote};
|
||||
use ruff_python_ast::types::Range;
|
||||
use ruff_python_ast::whitespace::LinesWithTrailingNewline;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::docstrings::definition::{DefinitionKind, Docstring};
|
||||
@@ -42,10 +42,10 @@ pub fn multi_line_summary_start(checker: &mut Checker, docstring: &Docstring) {
|
||||
let contents = docstring.contents;
|
||||
let body = docstring.body;
|
||||
|
||||
if NewlineWithTrailingNewline::from(body).nth(1).is_none() {
|
||||
if LinesWithTrailingNewline::from(body).nth(1).is_none() {
|
||||
return;
|
||||
};
|
||||
let mut content_lines = contents.universal_newlines();
|
||||
let mut content_lines = contents.lines();
|
||||
let Some(first_line) = content_lines
|
||||
.next()
|
||||
else
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::newlines::{NewlineWithTrailingNewline, StrExt};
|
||||
use ruff_python_ast::types::Range;
|
||||
use ruff_python_ast::whitespace;
|
||||
use ruff_python_ast::whitespace::LinesWithTrailingNewline;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::docstrings::definition::Docstring;
|
||||
@@ -29,12 +29,12 @@ pub fn newline_after_last_paragraph(checker: &mut Checker, docstring: &Docstring
|
||||
let body = docstring.body;
|
||||
|
||||
let mut line_count = 0;
|
||||
for line in NewlineWithTrailingNewline::from(body) {
|
||||
for line in LinesWithTrailingNewline::from(body) {
|
||||
if !line.trim().is_empty() {
|
||||
line_count += 1;
|
||||
}
|
||||
if line_count > 1 {
|
||||
if let Some(last_line) = contents.universal_newlines().last().map(str::trim) {
|
||||
if let Some(last_line) = contents.lines().last().map(str::trim) {
|
||||
if last_line != "\"\"\"" && last_line != "'''" {
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(NewLineAfterLastParagraph, Range::from(docstring.expr));
|
||||
|
||||
@@ -2,7 +2,6 @@ use rustpython_parser::ast::StmtKind;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -33,7 +32,7 @@ pub fn no_signature(checker: &mut Checker, docstring: &Docstring) {
|
||||
|
||||
let body = docstring.body;
|
||||
|
||||
let Some(first_line) = body.trim().universal_newlines().next() else {
|
||||
let Some(first_line) = body.trim().lines().next() else {
|
||||
return;
|
||||
};
|
||||
if !first_line.contains(&format!("{name}(")) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::newlines::NewlineWithTrailingNewline;
|
||||
use ruff_python_ast::str::leading_quote;
|
||||
use ruff_python_ast::types::Range;
|
||||
use ruff_python_ast::whitespace::LinesWithTrailingNewline;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::docstrings::definition::Docstring;
|
||||
@@ -28,7 +28,7 @@ pub fn no_surrounding_whitespace(checker: &mut Checker, docstring: &Docstring) {
|
||||
let contents = docstring.contents;
|
||||
let body = docstring.body;
|
||||
|
||||
let mut lines = NewlineWithTrailingNewline::from(body);
|
||||
let mut lines = LinesWithTrailingNewline::from(body);
|
||||
let Some(line) = lines.next() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -7,7 +7,6 @@ use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::cast;
|
||||
use ruff_python_ast::helpers::to_call_path;
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::types::{CallPath, Range};
|
||||
use ruff_python_ast::visibility::{is_property, is_test};
|
||||
|
||||
@@ -49,7 +48,7 @@ pub fn non_imperative_mood(
|
||||
let body = docstring.body;
|
||||
|
||||
// Find first line, disregarding whitespace.
|
||||
let line = match body.trim().universal_newlines().next() {
|
||||
let line = match body.trim().lines().next() {
|
||||
Some(line) => line.trim(),
|
||||
None => return,
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::newlines::NewlineWithTrailingNewline;
|
||||
use ruff_python_ast::str::{leading_quote, trailing_quote};
|
||||
use ruff_python_ast::types::Range;
|
||||
use ruff_python_ast::whitespace::LinesWithTrailingNewline;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::docstrings::definition::Docstring;
|
||||
@@ -26,7 +26,7 @@ impl AlwaysAutofixableViolation for FitsOnOneLine {
|
||||
pub fn one_liner(checker: &mut Checker, docstring: &Docstring) {
|
||||
let mut line_count = 0;
|
||||
let mut non_empty_line_count = 0;
|
||||
for line in NewlineWithTrailingNewline::from(docstring.body) {
|
||||
for line in LinesWithTrailingNewline::from(docstring.body) {
|
||||
line_count += 1;
|
||||
if !line.trim().is_empty() {
|
||||
non_empty_line_count += 1;
|
||||
|
||||
@@ -8,9 +8,9 @@ use ruff_diagnostics::{AlwaysAutofixableViolation, Violation};
|
||||
use ruff_diagnostics::{Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::identifier_range;
|
||||
use ruff_python_ast::newlines::NewlineWithTrailingNewline;
|
||||
use ruff_python_ast::types::Range;
|
||||
use ruff_python_ast::visibility::is_staticmethod;
|
||||
use ruff_python_ast::whitespace::LinesWithTrailingNewline;
|
||||
use ruff_python_ast::{cast, whitespace};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -273,7 +273,7 @@ impl AlwaysAutofixableViolation for NoBlankLinesBetweenHeaderAndContent {
|
||||
pub fn sections(checker: &mut Checker, docstring: &Docstring, convention: Option<&Convention>) {
|
||||
let body = docstring.body;
|
||||
|
||||
let lines: Vec<&str> = NewlineWithTrailingNewline::from(body).collect();
|
||||
let lines: Vec<&str> = LinesWithTrailingNewline::from(body).collect();
|
||||
if lines.len() < 2 {
|
||||
return;
|
||||
}
|
||||
@@ -294,6 +294,18 @@ pub fn sections(checker: &mut Checker, docstring: &Docstring, convention: Option
|
||||
// (e.g., "Returns", "Raises"). Break ties by checking for the presence of some of the
|
||||
// section names that are unique to each convention.
|
||||
|
||||
// If the docstring contains `Args:` or `Arguments:`, use the Google convention.
|
||||
let google_sections = section_contexts(&lines, &SectionStyle::Google);
|
||||
if google_sections
|
||||
.iter()
|
||||
.any(|context| matches!(context.kind, SectionKind::Arguments | SectionKind::Args))
|
||||
{
|
||||
for context in &google_sections {
|
||||
google_section(checker, docstring, context);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If the docstring contains `Parameters:` or `Other Parameters:`, use the NumPy
|
||||
// convention.
|
||||
let numpy_sections = section_contexts(&lines, &SectionStyle::Numpy);
|
||||
@@ -309,18 +321,6 @@ pub fn sections(checker: &mut Checker, docstring: &Docstring, convention: Option
|
||||
return;
|
||||
}
|
||||
|
||||
// If the docstring contains `Args:` or `Arguments:`, use the Google convention.
|
||||
let google_sections = section_contexts(&lines, &SectionStyle::Google);
|
||||
if google_sections
|
||||
.iter()
|
||||
.any(|context| matches!(context.kind, SectionKind::Arguments | SectionKind::Args))
|
||||
{
|
||||
for context in &google_sections {
|
||||
google_section(checker, docstring, context);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, use whichever convention matched more sections.
|
||||
if google_sections.len() > numpy_sections.len() {
|
||||
for context in &google_sections {
|
||||
@@ -923,32 +923,30 @@ fn parameters_section(checker: &mut Checker, docstring: &Docstring, context: &Se
|
||||
|
||||
// Join line continuations, then resplit by line.
|
||||
let adjusted_following_lines = context.following_lines.join("\n").replace("\\\n", "");
|
||||
let mut lines = NewlineWithTrailingNewline::from(&adjusted_following_lines);
|
||||
if let Some(mut current_line) = lines.next() {
|
||||
for next_line in lines {
|
||||
let current_leading_space = whitespace::leading_space(current_line);
|
||||
if current_leading_space == section_level_indent
|
||||
&& (whitespace::leading_space(next_line).len() > current_leading_space.len())
|
||||
&& !next_line.trim().is_empty()
|
||||
{
|
||||
let parameters = if let Some(semi_index) = current_line.find(':') {
|
||||
// If the parameter has a type annotation, exclude it.
|
||||
¤t_line[..semi_index]
|
||||
} else {
|
||||
// Otherwise, it's just a list of parameters on the current line.
|
||||
current_line.trim()
|
||||
};
|
||||
// Notably, NumPy lets you put multiple parameters of the same type on the same
|
||||
// line.
|
||||
for parameter in parameters.split(',') {
|
||||
docstring_args.insert(parameter.trim());
|
||||
}
|
||||
}
|
||||
let lines: Vec<&str> = LinesWithTrailingNewline::from(&adjusted_following_lines).collect();
|
||||
|
||||
current_line = next_line;
|
||||
for i in 1..lines.len() {
|
||||
let current_line = lines[i - 1];
|
||||
let current_leading_space = whitespace::leading_space(current_line);
|
||||
let next_line = lines[i];
|
||||
if current_leading_space == section_level_indent
|
||||
&& (whitespace::leading_space(next_line).len() > current_leading_space.len())
|
||||
&& !next_line.trim().is_empty()
|
||||
{
|
||||
let parameters = if let Some(semi_index) = current_line.find(':') {
|
||||
// If the parameter has a type annotation, exclude it.
|
||||
¤t_line[..semi_index]
|
||||
} else {
|
||||
// Otherwise, it's just a list of parameters on the current line.
|
||||
current_line.trim()
|
||||
};
|
||||
// Notably, NumPy lets you put multiple parameters of the same type on the same
|
||||
// line.
|
||||
for parameter in parameters.split(',') {
|
||||
docstring_args.insert(parameter.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that all arguments were documented.
|
||||
missing_args(checker, docstring, &docstring_args);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -21,7 +20,8 @@ pub fn triple_quotes(checker: &mut Checker, docstring: &Docstring) {
|
||||
let contents = docstring.contents;
|
||||
let body = docstring.body;
|
||||
|
||||
let Some(first_line) = contents.universal_newlines()
|
||||
let Some(first_line) = contents
|
||||
.lines()
|
||||
.next()
|
||||
.map(str::to_lowercase) else
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ use itertools::izip;
|
||||
use log::error;
|
||||
use once_cell::unsync::Lazy;
|
||||
use rustpython_parser::ast::{Cmpop, Expr};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
@@ -12,7 +13,7 @@ use ruff_python_ast::types::Range;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum IsCmpop {
|
||||
Is,
|
||||
IsNot,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::fmt;
|
||||
|
||||
use rustpython_parser::ast::{Expr, ExprKind};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
@@ -8,7 +9,7 @@ use ruff_python_ast::types::{Range, ScopeKind};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DeferralKeyword {
|
||||
Yield,
|
||||
YieldFrom,
|
||||
|
||||
@@ -43,8 +43,6 @@ mod tests {
|
||||
#[test_case(Rule::GlobalStatement, Path::new("global_statement.py"); "PLW0603")]
|
||||
#[test_case(Rule::InvalidAllFormat, Path::new("invalid_all_format.py"); "PLE0605")]
|
||||
#[test_case(Rule::InvalidAllObject, Path::new("invalid_all_object.py"); "PLE0604")]
|
||||
#[test_case(Rule::InvalidEnvvarDefault, Path::new("invalid_envvar_default.py"); "PLW1508")]
|
||||
#[test_case(Rule::InvalidEnvvarValue, Path::new("invalid_envvar_value.py"); "PLE1507")]
|
||||
#[test_case(Rule::TooManyReturnStatements, Path::new("too_many_return_statements.py"); "PLR0911")]
|
||||
#[test_case(Rule::TooManyArguments, Path::new("too_many_arguments.py"); "PLR0913")]
|
||||
#[test_case(Rule::TooManyBranches, Path::new("too_many_branches.py"); "PLR0912")]
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::fmt;
|
||||
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustpython_parser::ast::{Constant, Expr, ExprKind};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
@@ -30,7 +31,7 @@ impl Violation for BadStrStripCall {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum StripKind {
|
||||
Strip,
|
||||
LStrip,
|
||||
@@ -59,7 +60,7 @@ impl fmt::Display for StripKind {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum RemovalKind {
|
||||
RemovePrefix,
|
||||
RemoveSuffix,
|
||||
|
||||
@@ -86,10 +86,17 @@ pub fn compare_to_empty_string(
|
||||
if let ExprKind::Constant { value, .. } = &lhs.node {
|
||||
if let Constant::Str(s) = value {
|
||||
if s.is_empty() {
|
||||
let constant = unparse_constant(value, checker.stylist);
|
||||
let expr = unparse_expr(rhs, checker.stylist);
|
||||
let existing = format!("{constant} {op} {expr}");
|
||||
let replacement = format!("{}{expr}", op.into_unary());
|
||||
let existing = format!(
|
||||
"{} {} {}",
|
||||
unparse_constant(value, checker.stylist),
|
||||
op,
|
||||
unparse_expr(rhs, checker.stylist)
|
||||
);
|
||||
let replacement = format!(
|
||||
"{}{}",
|
||||
op.into_unary(),
|
||||
unparse_expr(rhs, checker.stylist)
|
||||
);
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
CompareToEmptyString {
|
||||
existing,
|
||||
@@ -106,10 +113,14 @@ pub fn compare_to_empty_string(
|
||||
if let ExprKind::Constant { value, .. } = &rhs.node {
|
||||
if let Constant::Str(s) = value {
|
||||
if s.is_empty() {
|
||||
let expr = unparse_expr(lhs, checker.stylist);
|
||||
let constant = unparse_constant(value, checker.stylist);
|
||||
let existing = format!("{expr} {op} {constant}");
|
||||
let replacement = format!("{}{expr}", op.into_unary());
|
||||
let existing = format!(
|
||||
"{} {} {}",
|
||||
unparse_expr(lhs, checker.stylist),
|
||||
op,
|
||||
unparse_constant(value, checker.stylist),
|
||||
);
|
||||
let replacement =
|
||||
format!("{}{}", op.into_unary(), unparse_expr(lhs, checker.stylist));
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
CompareToEmptyString {
|
||||
existing,
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::fmt;
|
||||
|
||||
use itertools::Itertools;
|
||||
use rustpython_parser::ast::{Cmpop, Expr, ExprKind, Located};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
@@ -10,7 +11,7 @@ use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum ViolationsCmpop {
|
||||
Eq,
|
||||
NotEq,
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
use rustpython_parser::ast::{Constant, Expr, ExprKind, Keyword};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `env.getenv` calls with invalid default values.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// If an environment variable is set, `env.getenv` will return its value as
|
||||
/// a string. If the environment variable is _not_ set, `env.getenv` will
|
||||
/// return `None`, or the default value if one is provided.
|
||||
///
|
||||
/// If the default value is not a string or `None`, then it will be
|
||||
/// inconsistent with the return type of `env.getenv`, which can lead to
|
||||
/// confusing behavior.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// int(env.getenv("FOO", 1))
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// int(env.getenv("FOO", "1"))
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct InvalidEnvvarDefault;
|
||||
|
||||
impl Violation for InvalidEnvvarDefault {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Invalid type for environment variable default; expected `str` or `None`")
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid_default(expr: &Expr) -> bool {
|
||||
// We can't infer the types of these defaults, so assume they're valid.
|
||||
if matches!(
|
||||
expr.node,
|
||||
ExprKind::Name { .. }
|
||||
| ExprKind::Attribute { .. }
|
||||
| ExprKind::Subscript { .. }
|
||||
| ExprKind::Call { .. }
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, the default must be a string or `None`.
|
||||
matches!(
|
||||
expr.node,
|
||||
ExprKind::Constant {
|
||||
value: Constant::Str { .. } | Constant::None { .. },
|
||||
..
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// PLW1508
|
||||
pub fn invalid_envvar_default(
|
||||
checker: &mut Checker,
|
||||
func: &Expr,
|
||||
args: &[Expr],
|
||||
keywords: &[Keyword],
|
||||
) {
|
||||
if checker
|
||||
.ctx
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| call_path.as_slice() == ["os", "getenv"])
|
||||
{
|
||||
// Find the `default` argument, if it exists.
|
||||
let Some(expr) = args.get(1).or_else(|| {
|
||||
keywords
|
||||
.iter()
|
||||
.find(|keyword| keyword.node.arg.as_ref().map_or(false, |arg| arg == "default"))
|
||||
.map(|keyword| &keyword.node.value)
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if !is_valid_default(expr) {
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(InvalidEnvvarDefault, Range::from(expr)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
use rustpython_parser::ast::{Constant, Expr, ExprKind, Keyword};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `os.getenv` calls with an invalid `key` argument.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// `os.getenv` only supports strings as the first argument (`key`).
|
||||
///
|
||||
/// If the provided argument is not a string, `os.getenv` will throw a
|
||||
/// `TypeError` at runtime.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// os.getenv(1)
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// os.getenv("1")
|
||||
/// ```
|
||||
#[violation]
|
||||
pub struct InvalidEnvvarValue;
|
||||
|
||||
impl Violation for InvalidEnvvarValue {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Invalid type for initial `os.getenv` argument; expected `str`")
|
||||
}
|
||||
}
|
||||
|
||||
/// PLE1507
|
||||
pub fn invalid_envvar_value(
|
||||
checker: &mut Checker,
|
||||
func: &Expr,
|
||||
args: &[Expr],
|
||||
keywords: &[Keyword],
|
||||
) {
|
||||
if checker
|
||||
.ctx
|
||||
.resolve_call_path(func)
|
||||
.map_or(false, |call_path| call_path.as_slice() == ["os", "getenv"])
|
||||
{
|
||||
// Get the first argument for `getenv`
|
||||
if let Some(expr) = args.get(0).or_else(|| {
|
||||
keywords
|
||||
.iter()
|
||||
.find(|keyword| keyword.node.arg.as_ref().map_or(false, |arg| arg == "key"))
|
||||
.map(|keyword| &keyword.node.value)
|
||||
}) {
|
||||
// Ignoring types that are inferred, only do direct constants
|
||||
if !matches!(
|
||||
expr.node,
|
||||
ExprKind::Constant {
|
||||
value: Constant::Str { .. },
|
||||
..
|
||||
} | ExprKind::Name { .. }
|
||||
| ExprKind::Attribute { .. }
|
||||
| ExprKind::Subscript { .. }
|
||||
| ExprKind::Call { .. }
|
||||
) {
|
||||
checker
|
||||
.diagnostics
|
||||
.push(Diagnostic::new(InvalidEnvvarValue, Range::from(expr)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,7 @@ pub fn logging_call(checker: &mut Checker, func: &Expr, args: &[Expr], keywords:
|
||||
if let ExprKind::Attribute { attr, .. } = &func.node {
|
||||
if LoggingLevel::from_attribute(attr.as_str()).is_some() {
|
||||
let call_args = SimpleCallArgs::new(args, keywords);
|
||||
if let Some(msg) = call_args.argument("msg", 0) {
|
||||
if let Some(msg) = call_args.get_argument("msg", Some(0)) {
|
||||
if let ExprKind::Constant {
|
||||
value: Constant::Str(value),
|
||||
..
|
||||
|
||||
@@ -10,8 +10,6 @@ pub use global_statement::{global_statement, GlobalStatement};
|
||||
pub use global_variable_not_assigned::GlobalVariableNotAssigned;
|
||||
pub use invalid_all_format::{invalid_all_format, InvalidAllFormat};
|
||||
pub use invalid_all_object::{invalid_all_object, InvalidAllObject};
|
||||
pub use invalid_envvar_default::{invalid_envvar_default, InvalidEnvvarDefault};
|
||||
pub use invalid_envvar_value::{invalid_envvar_value, InvalidEnvvarValue};
|
||||
pub use logging::{logging_call, LoggingTooFewArgs, LoggingTooManyArgs};
|
||||
pub use magic_value_comparison::{magic_value_comparison, MagicValueComparison};
|
||||
pub use merge_isinstance::{merge_isinstance, ConsiderMergingIsinstance};
|
||||
@@ -46,8 +44,6 @@ mod global_statement;
|
||||
mod global_variable_not_assigned;
|
||||
mod invalid_all_format;
|
||||
mod invalid_all_object;
|
||||
mod invalid_envvar_default;
|
||||
mod invalid_envvar_value;
|
||||
mod logging;
|
||||
mod magic_value_comparison;
|
||||
mod merge_isinstance;
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::{fmt, iter};
|
||||
|
||||
use regex::Regex;
|
||||
use rustpython_parser::ast::{Expr, ExprContext, ExprKind, Stmt, StmtKind, Withitem};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
@@ -13,7 +14,7 @@ use ruff_python_ast::visitor::Visitor;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
|
||||
pub enum OuterBindingKind {
|
||||
For,
|
||||
With,
|
||||
@@ -28,7 +29,7 @@ impl fmt::Display for OuterBindingKind {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
|
||||
pub enum InnerBindingKind {
|
||||
For,
|
||||
With,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user