Compare commits
2 Commits
0.12.1
...
micha/add-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
daa385c1a9 | ||
|
|
01d9312529 |
29
.github/renovate.json5
vendored
29
.github/renovate.json5
vendored
@@ -16,7 +16,7 @@
|
||||
pep621: {
|
||||
// The default for this package manager is to only search for `pyproject.toml` files
|
||||
// found at the repository root: https://docs.renovatebot.com/modules/manager/pep621/#file-matching
|
||||
fileMatch: ["^(python|scripts)/.*pyproject\\.toml$"],
|
||||
managerFilePatterns: ["/^(python|scripts)/.*pyproject\\.toml$/"],
|
||||
},
|
||||
pip_requirements: {
|
||||
// The default for this package manager is to run on all requirements.txt files:
|
||||
@@ -34,12 +34,32 @@
|
||||
npm: {
|
||||
// The default for this package manager is to only search for `package.json` files
|
||||
// found at the repository root: https://docs.renovatebot.com/modules/manager/npm/#file-matching
|
||||
fileMatch: ["^playground/.*package\\.json$"],
|
||||
managerFilePatterns: ["/^playground/.*package\\.json$/"],
|
||||
},
|
||||
customManagers: [
|
||||
{
|
||||
customType: "regex",
|
||||
managerFilePatterns: ["/^dist-workspace\\.toml$/"],
|
||||
matchStrings: [
|
||||
'"(?<depName>actions/[^"]+)" = "(?<currentDigest>[a-f0-9]{40})"\\s*#\\s*(?<currentValue>v[\\d\\.]+).*'
|
||||
],
|
||||
datasourceTemplate: "github-tags",
|
||||
autoReplaceStringTemplate: '"{{depName}}" = "{{newDigest}}" # {{newValue}}"',
|
||||
extractVersionTemplate: "^(?<version>v[\\d\\.]+)$",
|
||||
versioningTemplate: "semver"
|
||||
}
|
||||
],
|
||||
"pre-commit": {
|
||||
enabled: true,
|
||||
},
|
||||
packageRules: [
|
||||
// Ignore GitHub Actions in generated release.yml (managed by cargo-dist)
|
||||
{
|
||||
matchManagers: ["github-actions"],
|
||||
matchFileNames: [".github/workflows/release.yml"],
|
||||
enabled: false,
|
||||
description: "Ignore GitHub Actions in release.yml as it's generated by cargo-dist",
|
||||
},
|
||||
// Pin GitHub Actions to immutable SHAs.
|
||||
{
|
||||
matchDepTypes: ["action"],
|
||||
@@ -106,6 +126,11 @@
|
||||
matchManagers: ["cargo"],
|
||||
matchPackageNames: ["strum"],
|
||||
description: "Weekly update of strum dependencies",
|
||||
},
|
||||
{
|
||||
groupName: "cargo-dist GitHub Actions",
|
||||
matchManagers: ["custom.regex"],
|
||||
description: "Weekly update of GitHub Actions dependencies managed by cargo-dist",
|
||||
}
|
||||
],
|
||||
vulnerabilityAlerts: {
|
||||
|
||||
2
.github/workflows/mypy_primer.yaml
vendored
2
.github/workflows/mypy_primer.yaml
vendored
@@ -70,7 +70,7 @@ jobs:
|
||||
echo "Project selector: $PRIMER_SELECTOR"
|
||||
# Allow the exit code to be 0 or 1, only fail for actual mypy_primer crashes/bugs
|
||||
uvx \
|
||||
--from="git+https://github.com/hauntsaninja/mypy_primer@e5f55447969d33ae3c7ccdb183e2a37101867270" \
|
||||
--from="git+https://github.com/hauntsaninja/mypy_primer@01a7ca325f674433c58e02416a867178d1571128" \
|
||||
mypy_primer \
|
||||
--repo ruff \
|
||||
--type-checker ty \
|
||||
|
||||
18
.github/workflows/release.yml
vendored
18
.github/workflows/release.yml
vendored
@@ -61,7 +61,7 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -69,9 +69,9 @@ jobs:
|
||||
# we specify bash to get pipefail; it guards against the `curl` command
|
||||
# failing. otherwise `sh` won't catch that `curl` returned non-0
|
||||
shell: bash
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/cargo-dist/releases/download/v0.28.5-prerelease.1/cargo-dist-installer.sh | sh"
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/cargo-dist/releases/download/v0.28.5-prerelease.3/cargo-dist-installer.sh | sh"
|
||||
- name: Cache dist
|
||||
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/dist
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
cat plan-dist-manifest.json
|
||||
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||
- name: "Upload dist-manifest.json"
|
||||
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
with:
|
||||
name: artifacts-plan-dist-manifest
|
||||
path: plan-dist-manifest.json
|
||||
@@ -124,7 +124,7 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
|
||||
steps:
|
||||
- uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -154,7 +154,7 @@ jobs:
|
||||
|
||||
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||
- name: "Upload artifacts"
|
||||
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
with:
|
||||
name: artifacts-build-global
|
||||
path: |
|
||||
@@ -175,7 +175,7 @@ jobs:
|
||||
outputs:
|
||||
val: ${{ steps.host.outputs.manifest }}
|
||||
steps:
|
||||
- uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -201,7 +201,7 @@ jobs:
|
||||
cat dist-manifest.json
|
||||
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||
- name: "Upload dist-manifest.json"
|
||||
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
with:
|
||||
# Overwrite the previous copy
|
||||
name: artifacts-dist-manifest
|
||||
@@ -251,7 +251,7 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
|
||||
80
CHANGELOG.md
80
CHANGELOG.md
@@ -1,81 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Preview features
|
||||
|
||||
- \[`flake8-errmsg`\] Extend `EM101` to support byte strings ([#18867](https://github.com/astral-sh/ruff/pull/18867))
|
||||
- \[`flake8-use-pathlib`\] Add autofix for `PTH202` ([#18763](https://github.com/astral-sh/ruff/pull/18763))
|
||||
- \[`pygrep-hooks`\] Add `AsyncMock` methods to `invalid-mock-access` (`PGH005`) ([#18547](https://github.com/astral-sh/ruff/pull/18547))
|
||||
- \[`pylint`\] Ignore `__init__.py` files in (`PLC0414`) ([#18400](https://github.com/astral-sh/ruff/pull/18400))
|
||||
- \[`ruff`\] Trigger `RUF037` for empty string and byte strings ([#18862](https://github.com/astral-sh/ruff/pull/18862))
|
||||
- [formatter] Fix missing blank lines before decorated classes in `.pyi` files ([#18888](https://github.com/astral-sh/ruff/pull/18888))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Avoid generating diagnostics with per-file ignores ([#18801](https://github.com/astral-sh/ruff/pull/18801))
|
||||
- Handle parenthesized arguments in `remove_argument` ([#18805](https://github.com/astral-sh/ruff/pull/18805))
|
||||
- \[`flake8-logging`\] Avoid false positive for `exc_info=True` outside `logger.exception` (`LOG014`) ([#18737](https://github.com/astral-sh/ruff/pull/18737))
|
||||
- \[`flake8-pytest-style`\] Enforce `pytest` import for decorators ([#18779](https://github.com/astral-sh/ruff/pull/18779))
|
||||
- \[`flake8-pytest-style`\] Mark autofix for `PT001` and `PT023` as unsafe if there's comments in the decorator ([#18792](https://github.com/astral-sh/ruff/pull/18792))
|
||||
- \[`flake8-pytest-style`\] `PT001`/`PT023` fix makes syntax error on parenthesized decorator ([#18782](https://github.com/astral-sh/ruff/pull/18782))
|
||||
- \[`flake8-raise`\] Make fix unsafe if it deletes comments (`RSE102`) ([#18788](https://github.com/astral-sh/ruff/pull/18788))
|
||||
- \[`flake8-simplify`\] Fix `SIM911` autofix creating a syntax error ([#18793](https://github.com/astral-sh/ruff/pull/18793))
|
||||
- \[`flake8-simplify`\] Fix false negatives for shadowed bindings (`SIM910`, `SIM911`) ([#18794](https://github.com/astral-sh/ruff/pull/18794))
|
||||
- \[`flake8-simplify`\] Preserve original behavior for `except ()` and bare `except` (`SIM105`) ([#18213](https://github.com/astral-sh/ruff/pull/18213))
|
||||
- \[`flake8-pyi`\] Fix `PYI041`'s fix causing `TypeError` with `None | None | ...` ([#18637](https://github.com/astral-sh/ruff/pull/18637))
|
||||
- \[`perflint`\] Fix `PERF101` autofix creating a syntax error and mark autofix as unsafe if there are comments in the `list` call expr ([#18803](https://github.com/astral-sh/ruff/pull/18803))
|
||||
- \[`perflint`\] Fix false negative in `PERF401` ([#18866](https://github.com/astral-sh/ruff/pull/18866))
|
||||
- \[`pylint`\] Avoid flattening nested `min`/`max` when outer call has single argument (`PLW3301`) ([#16885](https://github.com/astral-sh/ruff/pull/16885))
|
||||
- \[`pylint`\] Fix `PLC2801` autofix creating a syntax error ([#18857](https://github.com/astral-sh/ruff/pull/18857))
|
||||
- \[`pylint`\] Mark `PLE0241` autofix as unsafe if there's comments in the base classes ([#18832](https://github.com/astral-sh/ruff/pull/18832))
|
||||
- \[`pylint`\] Suppress `PLE2510`/`PLE2512`/`PLE2513`/`PLE2514`/`PLE2515` autofix if the text contains an odd number of backslashes ([#18856](https://github.com/astral-sh/ruff/pull/18856))
|
||||
- \[`refurb`\] Detect more exotic float literals in `FURB164` ([#18925](https://github.com/astral-sh/ruff/pull/18925))
|
||||
- \[`refurb`\] Fix `FURB163` autofix creating a syntax error for `yield` expressions ([#18756](https://github.com/astral-sh/ruff/pull/18756))
|
||||
- \[`refurb`\] Mark `FURB129` autofix as unsafe if there's comments in the `readlines` call ([#18858](https://github.com/astral-sh/ruff/pull/18858))
|
||||
- \[`ruff`\] Fix false positives and negatives in `RUF010` ([#18690](https://github.com/astral-sh/ruff/pull/18690))
|
||||
- Fix casing of `analyze.direction` variant names ([#18892](https://github.com/astral-sh/ruff/pull/18892))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- Fix f-string interpolation escaping in generated fixes ([#18882](https://github.com/astral-sh/ruff/pull/18882))
|
||||
- \[`flake8-return`\] Mark `RET501` fix unsafe if comments are inside ([#18780](https://github.com/astral-sh/ruff/pull/18780))
|
||||
- \[`flake8-async`\] Fix detection for large integer sleep durations in `ASYNC116` rule ([#18767](https://github.com/astral-sh/ruff/pull/18767))
|
||||
- \[`flake8-async`\] Mark autofix for `ASYNC115` as unsafe if the call expression contains comments ([#18753](https://github.com/astral-sh/ruff/pull/18753))
|
||||
- \[`flake8-bugbear`\] Mark autofix for `B004` as unsafe if the `hasattr` call expr contains comments ([#18755](https://github.com/astral-sh/ruff/pull/18755))
|
||||
- \[`flake8-comprehension`\] Mark autofix for `C420` as unsafe if there's comments inside the dict comprehension ([#18768](https://github.com/astral-sh/ruff/pull/18768))
|
||||
- \[`flake8-comprehensions`\] Handle template strings for comprehension fixes ([#18710](https://github.com/astral-sh/ruff/pull/18710))
|
||||
- \[`flake8-future-annotations`\] Add autofix (`FA100`) ([#18903](https://github.com/astral-sh/ruff/pull/18903))
|
||||
- \[`pyflakes`\] Mark `F504`/`F522`/`F523` autofix as unsafe if there's a call with side effect ([#18839](https://github.com/astral-sh/ruff/pull/18839))
|
||||
- \[`pylint`\] Allow fix with comments and document performance implications (`PLW3301`) ([#18936](https://github.com/astral-sh/ruff/pull/18936))
|
||||
- \[`pylint`\] Detect more exotic `NaN` literals in `PLW0177` ([#18630](https://github.com/astral-sh/ruff/pull/18630))
|
||||
- \[`pylint`\] Fix `PLC1802` autofix creating a syntax error and mark autofix as unsafe if there's comments in the `len` call ([#18836](https://github.com/astral-sh/ruff/pull/18836))
|
||||
- \[`pyupgrade`\] Extend version detection to include `sys.version_info.major` (`UP036`) ([#18633](https://github.com/astral-sh/ruff/pull/18633))
|
||||
- \[`ruff`\] Add lint rule `RUF064` for calling `chmod` with non-octal integers ([#18541](https://github.com/astral-sh/ruff/pull/18541))
|
||||
- \[`ruff`\] Added `cls.__dict__.get('__annotations__')` check (`RUF063`) ([#18233](https://github.com/astral-sh/ruff/pull/18233))
|
||||
- \[`ruff`\] Frozen `dataclass` default should be valid (`RUF009`) ([#18735](https://github.com/astral-sh/ruff/pull/18735))
|
||||
|
||||
### Server
|
||||
|
||||
- Consider virtual path for various server actions ([#18910](https://github.com/astral-sh/ruff/pull/18910))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Add fix safety sections ([#18940](https://github.com/astral-sh/ruff/pull/18940),[#18841](https://github.com/astral-sh/ruff/pull/18841),[#18802](https://github.com/astral-sh/ruff/pull/18802),[#18837](https://github.com/astral-sh/ruff/pull/18837),[#18800](https://github.com/astral-sh/ruff/pull/18800),[#18415](https://github.com/astral-sh/ruff/pull/18415),[#18853](https://github.com/astral-sh/ruff/pull/18853),[#18842](https://github.com/astral-sh/ruff/pull/18842))
|
||||
- Use updated pre-commit id ([#18718](https://github.com/astral-sh/ruff/pull/18718))
|
||||
- \[`perflint`\] Small docs improvement to `PERF401` ([#18786](https://github.com/astral-sh/ruff/pull/18786))
|
||||
- \[`pyupgrade`\]: Use `super()`, not `__super__` in error messages (`UP008`) ([#18743](https://github.com/astral-sh/ruff/pull/18743))
|
||||
- \[`flake8-pie`\] Small docs fix to `PIE794` ([#18829](https://github.com/astral-sh/ruff/pull/18829))
|
||||
- \[`flake8-pyi`\] Correct `collections-named-tuple` example to use PascalCase assignment ([#16884](https://github.com/astral-sh/ruff/pull/16884))
|
||||
- \[`flake8-pie`\] Add note on type checking benefits to `unnecessary-dict-kwargs` (`PIE804`) ([#18666](https://github.com/astral-sh/ruff/pull/18666))
|
||||
- \[`pycodestyle`\] Clarify PEP 8 relationship to `whitespace-around-operator` rules ([#18870](https://github.com/astral-sh/ruff/pull/18870))
|
||||
|
||||
### Other changes
|
||||
|
||||
- Disallow newlines in format specifiers of single quoted f- or t-strings ([#18708](https://github.com/astral-sh/ruff/pull/18708))
|
||||
- \[`flake8-logging`\] Add fix safety section to `LOG002` ([#18840](https://github.com/astral-sh/ruff/pull/18840))
|
||||
- \[`pyupgrade`\] Add fix safety section to `UP010` ([#18838](https://github.com/astral-sh/ruff/pull/18838))
|
||||
|
||||
## 0.12.0
|
||||
|
||||
Check out the [blog post](https://astral.sh/blog/ruff-v0.12.0) for a migration
|
||||
@@ -92,7 +16,7 @@ guide and overview of the changes!
|
||||
|
||||
- **New default Python version handling for syntax errors**
|
||||
|
||||
Ruff will default to the *latest* supported Python version (3.13) when
|
||||
Ruff will default to the _latest_ supported Python version (3.13) when
|
||||
checking for the version-related syntax errors mentioned above to prevent
|
||||
false positives in projects without a Python version configured. The default
|
||||
in all other cases, like applying lint rules, is unchanged and remains at the
|
||||
@@ -147,7 +71,7 @@ The following rules have been stabilized and are no longer in preview:
|
||||
- [`class-with-mixed-type-vars`](https://docs.astral.sh/ruff/rules/class-with-mixed-type-vars) (`RUF053`)
|
||||
- [`unnecessary-round`](https://docs.astral.sh/ruff/rules/unnecessary-round) (`RUF057`)
|
||||
- [`starmap-zip`](https://docs.astral.sh/ruff/rules/starmap-zip) (`RUF058`)
|
||||
- [`non-pep604-annotation-optional`] (`UP045`)
|
||||
- [`non-pep604-annotation-optional`](https://docs.astral.sh/ruff/rules/non-pep604-annotation-optional) (`UP045`)
|
||||
- [`non-pep695-generic-class`](https://docs.astral.sh/ruff/rules/non-pep695-generic-class) (`UP046`)
|
||||
- [`non-pep695-generic-function`](https://docs.astral.sh/ruff/rules/non-pep695-generic-function) (`UP047`)
|
||||
- [`private-type-parameter`](https://docs.astral.sh/ruff/rules/private-type-parameter) (`UP049`)
|
||||
|
||||
39
Cargo.lock
generated
39
Cargo.lock
generated
@@ -930,12 +930,35 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_home"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"env_filter",
|
||||
"jiff",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -2559,7 +2582,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.12.1"
|
||||
version = "0.12.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argfile",
|
||||
@@ -2802,7 +2825,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.12.1"
|
||||
version = "0.12.0"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"anyhow",
|
||||
@@ -3023,6 +3046,16 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_resolver"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"insta",
|
||||
"log",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff_python_semantic"
|
||||
version = "0.0.0"
|
||||
@@ -3129,7 +3162,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_wasm"
|
||||
version = "0.12.1"
|
||||
version = "0.12.0"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
|
||||
@@ -75,6 +75,7 @@ dashmap = { version = "6.0.1" }
|
||||
dir-test = { version = "0.4.0" }
|
||||
dunce = { version = "1.0.5" }
|
||||
drop_bomb = { version = "0.1.5" }
|
||||
env_logger = { version = "0.11.0" }
|
||||
etcetera = { version = "0.10.0" }
|
||||
fern = { version = "0.7.0" }
|
||||
filetime = { version = "0.2.23" }
|
||||
|
||||
@@ -148,8 +148,8 @@ curl -LsSf https://astral.sh/ruff/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/install.ps1 | iex"
|
||||
|
||||
# For a specific version.
|
||||
curl -LsSf https://astral.sh/ruff/0.12.1/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.12.1/install.ps1 | iex"
|
||||
curl -LsSf https://astral.sh/ruff/0.12.0/install.sh | sh
|
||||
powershell -c "irm https://astral.sh/ruff/0.12.0/install.ps1 | iex"
|
||||
```
|
||||
|
||||
You can also install Ruff via [Homebrew](https://formulae.brew.sh/formula/ruff), [Conda](https://anaconda.org/conda-forge/ruff),
|
||||
@@ -182,7 +182,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com/) hook via [`ruff
|
||||
```yaml
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.12.1
|
||||
rev: v0.12.0
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff-check
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.12.1"
|
||||
version = "0.12.0"
|
||||
publish = true
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -442,7 +442,7 @@ impl LintCacheData {
|
||||
// Parse the kebab-case rule name into a `Rule`. This will fail for syntax errors, so
|
||||
// this also serves to filter them out, but we shouldn't be caching files with syntax
|
||||
// errors anyway.
|
||||
.filter_map(|msg| Some((msg.name().parse().ok()?, msg)))
|
||||
.filter_map(|msg| Some((msg.noqa_code().and_then(|code| code.rule())?, msg)))
|
||||
.map(|(rule, msg)| {
|
||||
// Make sure that all message use the same source file.
|
||||
assert_eq!(
|
||||
|
||||
@@ -17,7 +17,6 @@ fn command() -> Command {
|
||||
command.arg("analyze");
|
||||
command.arg("graph");
|
||||
command.arg("--preview");
|
||||
command.env_clear();
|
||||
command
|
||||
}
|
||||
|
||||
@@ -566,8 +565,8 @@ fn venv() -> Result<()> {
|
||||
|
||||
----- stderr -----
|
||||
ruff failed
|
||||
Cause: Invalid `--python` argument `none`: does not point to a Python executable or a directory on disk
|
||||
Cause: No such file or directory (os error 2)
|
||||
Cause: Invalid search path settings
|
||||
Cause: Failed to discover the site-packages directory: Invalid `--python` argument `none`: does not point to a Python executable or a directory on disk
|
||||
");
|
||||
});
|
||||
|
||||
|
||||
@@ -352,7 +352,7 @@ impl DisplaySet<'_> {
|
||||
// FIXME: `unicode_width` sometimes disagrees with terminals on how wide a `char`
|
||||
// is. For now, just accept that sometimes the code line will be longer than
|
||||
// desired.
|
||||
let next = char_width(ch).unwrap_or(1);
|
||||
let next = unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1);
|
||||
if taken + next > right - left {
|
||||
was_cut_right = true;
|
||||
break;
|
||||
@@ -377,7 +377,7 @@ impl DisplaySet<'_> {
|
||||
let left: usize = text
|
||||
.chars()
|
||||
.take(left)
|
||||
.map(|ch| char_width(ch).unwrap_or(1))
|
||||
.map(|ch| unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1))
|
||||
.sum();
|
||||
|
||||
let mut annotations = annotations.clone();
|
||||
@@ -1393,7 +1393,6 @@ fn format_body<'m>(
|
||||
let line_length: usize = line.len();
|
||||
let line_range = (current_index, current_index + line_length);
|
||||
let end_line_size = end_line.len();
|
||||
|
||||
body.push(DisplayLine::Source {
|
||||
lineno: Some(current_line),
|
||||
inline_marks: vec![],
|
||||
@@ -1453,12 +1452,12 @@ fn format_body<'m>(
|
||||
let annotation_start_col = line
|
||||
[0..(start - line_start_index).min(line_length)]
|
||||
.chars()
|
||||
.map(|c| char_width(c).unwrap_or(0))
|
||||
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
|
||||
.sum::<usize>();
|
||||
let mut annotation_end_col = line
|
||||
[0..(end - line_start_index).min(line_length)]
|
||||
.chars()
|
||||
.map(|c| char_width(c).unwrap_or(0))
|
||||
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
|
||||
.sum::<usize>();
|
||||
if annotation_start_col == annotation_end_col {
|
||||
// At least highlight something
|
||||
@@ -1500,7 +1499,7 @@ fn format_body<'m>(
|
||||
let annotation_start_col = line
|
||||
[0..(start - line_start_index).min(line_length)]
|
||||
.chars()
|
||||
.map(|c| char_width(c).unwrap_or(0))
|
||||
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
|
||||
.sum::<usize>();
|
||||
let annotation_end_col = annotation_start_col + 1;
|
||||
|
||||
@@ -1559,7 +1558,7 @@ fn format_body<'m>(
|
||||
{
|
||||
let end_mark = line[0..(end - line_start_index).min(line_length)]
|
||||
.chars()
|
||||
.map(|c| char_width(c).unwrap_or(0))
|
||||
.map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
|
||||
.sum::<usize>()
|
||||
.saturating_sub(1);
|
||||
// If the annotation ends on a line-end character, we
|
||||
@@ -1755,11 +1754,3 @@ fn format_inline_marks(
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn char_width(c: char) -> Option<usize> {
|
||||
if c == '\t' {
|
||||
Some(4)
|
||||
} else {
|
||||
unicode_width::UnicodeWidthChar::width(c)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
<svg width="740px" height="146px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { background: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan>: </tspan><tspan class="bold">call-non-callable</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> $DIR/main.py:5:9</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">4 |</tspan><tspan> def f():</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan class="fg-bright-blue bold">5 |</tspan><tspan> return (1 == '2')() # Tab indented</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^^^^^^^^^^^</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,45 +0,0 @@
|
||||
|
||||
# [crates/ruff_db/src/diagnostic/render.rs:123:47] diag.to_annotate() = Message {
|
||||
# level: Error,
|
||||
# id: Some(
|
||||
# "call-non-callable",
|
||||
# ),
|
||||
# title: "Object of type `bool` is not callable",
|
||||
# snippets: [
|
||||
# Snippet {
|
||||
# origin: Some(
|
||||
# "main.py",
|
||||
# ),
|
||||
# line_start: 1,
|
||||
# source: "def f():\n\treturn (1 == '2')() # Tab indented\n",
|
||||
# annotations: [
|
||||
# Annotation {
|
||||
# range: 17..29,
|
||||
# label: None,
|
||||
# level: Error,
|
||||
# },
|
||||
# ],
|
||||
# fold: false,
|
||||
# },
|
||||
# ],
|
||||
# footer: [],
|
||||
# }
|
||||
|
||||
[message]
|
||||
level = "Error"
|
||||
id = "E0308"
|
||||
title = "call-non-callable"
|
||||
|
||||
[[message.snippets]]
|
||||
source = "def f():\n\treturn (1 == '2')() # Tab indented\n"
|
||||
line_start = 4
|
||||
origin = "$DIR/main.py"
|
||||
|
||||
[[message.snippets.annotations]]
|
||||
label = ""
|
||||
level = "Error"
|
||||
range = [17, 29]
|
||||
|
||||
[renderer]
|
||||
# anonymized_line_numbers = true
|
||||
color = true
|
||||
@@ -1,36 +0,0 @@
|
||||
<svg width="1196px" height="128px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { background: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan>: </tspan><tspan class="bold">mismatched types</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> $DIR/non-whitespace-trimming.rs:4:6</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">4 |</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">...</tspan><tspan> s_data['d_dails'] = bb['contacted'][hostip]['ansible_facts']['ansible_devices']['vda']['vendor'] + " " + bb['contacted'][hostip</tspan><tspan class="fg-bright-blue bold">...</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^^^^^</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,20 +0,0 @@
|
||||
[message]
|
||||
level = "Error"
|
||||
id = "E0308"
|
||||
title = "mismatched types"
|
||||
|
||||
[[message.snippets]]
|
||||
source = """
|
||||
s_data['d_dails'] = bb['contacted'][hostip]['ansible_facts']['ansible_devices']['vda']['vendor'] + " " + bb['contacted'][hostip]['an
|
||||
"""
|
||||
line_start = 4
|
||||
origin = "$DIR/non-whitespace-trimming.rs"
|
||||
|
||||
[[message.snippets.annotations]]
|
||||
label = ""
|
||||
level = "Error"
|
||||
range = [5, 11]
|
||||
|
||||
[renderer]
|
||||
# anonymized_line_numbers = true
|
||||
color = true
|
||||
@@ -21,7 +21,7 @@
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan>: </tspan><tspan class="bold">mismatched types</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> $DIR/non-whitespace-trimming.rs:4:238</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> $DIR/non-whitespace-trimming.rs:4:242</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
@@ -13,12 +13,12 @@ origin = "$DIR/non-whitespace-trimming.rs"
|
||||
[[message.snippets.annotations]]
|
||||
label = "expected `()`, found integer"
|
||||
level = "Error"
|
||||
range = [237, 239]
|
||||
range = [241, 243]
|
||||
|
||||
[[message.snippets.annotations]]
|
||||
label = "expected due to this"
|
||||
level = "Error"
|
||||
range = [232, 234]
|
||||
range = [236, 238]
|
||||
|
||||
|
||||
[renderer]
|
||||
|
||||
@@ -348,58 +348,6 @@ fn benchmark_many_tuple_assignments(criterion: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn benchmark_many_attribute_assignments(criterion: &mut Criterion) {
|
||||
setup_rayon();
|
||||
|
||||
criterion.bench_function("ty_micro[many_attribute_assignments]", |b| {
|
||||
b.iter_batched_ref(
|
||||
|| {
|
||||
// This is a regression benchmark for https://github.com/astral-sh/ty/issues/627.
|
||||
// Before this was fixed, the following sample would take >1s to type check.
|
||||
setup_micro_case(
|
||||
r#"
|
||||
class C:
|
||||
def f(self: "C"):
|
||||
if isinstance(self.a, str):
|
||||
return
|
||||
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
if isinstance(self.b, str):
|
||||
return
|
||||
"#,
|
||||
)
|
||||
},
|
||||
|case| {
|
||||
let Case { db, .. } = case;
|
||||
let result = db.check();
|
||||
assert!(!result.is_empty());
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
struct ProjectBenchmark<'a> {
|
||||
project: InstalledProject<'a>,
|
||||
fs: MemoryFileSystem,
|
||||
@@ -429,7 +377,8 @@ impl<'a> ProjectBenchmark<'a> {
|
||||
metadata.apply_options(Options {
|
||||
environment: Some(EnvironmentOptions {
|
||||
python_version: Some(RangedValue::cli(self.project.config.python_version)),
|
||||
python: Some(RelativePathBuf::cli(SystemPath::new(".venv"))),
|
||||
python: (!self.project.config().dependencies.is_empty())
|
||||
.then_some(RelativePathBuf::cli(SystemPath::new(".venv"))),
|
||||
..EnvironmentOptions::default()
|
||||
}),
|
||||
..Options::default()
|
||||
@@ -534,7 +483,6 @@ criterion_group!(
|
||||
micro,
|
||||
benchmark_many_string_assignments,
|
||||
benchmark_many_tuple_assignments,
|
||||
benchmark_many_attribute_assignments,
|
||||
);
|
||||
criterion_group!(project, anyio, attrs, hydra);
|
||||
criterion_main!(check_file, micro, project);
|
||||
|
||||
@@ -36,7 +36,8 @@ impl<'a> Benchmark<'a> {
|
||||
metadata.apply_options(Options {
|
||||
environment: Some(EnvironmentOptions {
|
||||
python_version: Some(RangedValue::cli(self.project.config.python_version)),
|
||||
python: Some(RelativePathBuf::cli(SystemPath::new(".venv"))),
|
||||
python: (!self.project.config().dependencies.is_empty())
|
||||
.then_some(RelativePathBuf::cli(SystemPath::new(".venv"))),
|
||||
..EnvironmentOptions::default()
|
||||
}),
|
||||
..Options::default()
|
||||
|
||||
@@ -74,17 +74,19 @@ impl<'a> RealWorldProject<'a> {
|
||||
};
|
||||
|
||||
// Install dependencies if specified
|
||||
tracing::debug!(
|
||||
"Installing {} dependencies for project '{}'...",
|
||||
checkout.project().dependencies.len(),
|
||||
checkout.project().name
|
||||
);
|
||||
let start_install = std::time::Instant::now();
|
||||
install_dependencies(&checkout)?;
|
||||
tracing::debug!(
|
||||
"Dependency installation completed in {:.2}s",
|
||||
start_install.elapsed().as_secs_f64()
|
||||
);
|
||||
if !checkout.project().dependencies.is_empty() {
|
||||
tracing::debug!(
|
||||
"Installing {} dependencies for project '{}'...",
|
||||
checkout.project().dependencies.len(),
|
||||
checkout.project().name
|
||||
);
|
||||
let start = std::time::Instant::now();
|
||||
install_dependencies(&checkout)?;
|
||||
tracing::debug!(
|
||||
"Dependency installation completed in {:.2}s",
|
||||
start.elapsed().as_secs_f64()
|
||||
);
|
||||
}
|
||||
|
||||
tracing::debug!("Project setup took: {:.2}s", start.elapsed().as_secs_f64());
|
||||
|
||||
@@ -279,14 +281,6 @@ fn install_dependencies(checkout: &Checkout) -> Result<()> {
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
|
||||
if checkout.project().dependencies.is_empty() {
|
||||
tracing::debug!(
|
||||
"No dependencies to install for project '{}'",
|
||||
checkout.project().name
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Install dependencies with date constraint in the isolated environment
|
||||
let mut cmd = Command::new("uv");
|
||||
cmd.args([
|
||||
|
||||
@@ -30,14 +30,14 @@ filetime = { workspace = true }
|
||||
glob = { workspace = true }
|
||||
ignore = { workspace = true, optional = true }
|
||||
matchit = { workspace = true }
|
||||
path-slash = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
salsa = { workspace = true }
|
||||
schemars = { workspace = true, optional = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
path-slash = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true, optional = true }
|
||||
rustc-hash = { workspace = true }
|
||||
zip = { workspace = true }
|
||||
|
||||
[target.'cfg(target_arch="wasm32")'.dependencies]
|
||||
|
||||
@@ -735,9 +735,6 @@ pub enum DiagnosticId {
|
||||
/// # no `[overrides.rules]`
|
||||
/// ```
|
||||
UselessOverridesSection,
|
||||
|
||||
/// Use of a deprecated setting.
|
||||
DeprecatedSetting,
|
||||
}
|
||||
|
||||
impl DiagnosticId {
|
||||
@@ -776,7 +773,6 @@ impl DiagnosticId {
|
||||
DiagnosticId::EmptyInclude => "empty-include",
|
||||
DiagnosticId::UnnecessaryOverridesSection => "unnecessary-overrides-section",
|
||||
DiagnosticId::UselessOverridesSection => "useless-overrides-section",
|
||||
DiagnosticId::DeprecatedSetting => "deprecated-setting",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::Result;
|
||||
use std::sync::Arc;
|
||||
use zip::CompressionMethod;
|
||||
|
||||
@@ -9,7 +9,7 @@ use ruff_db::{Db as SourceDb, Upcast};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
use ty_python_semantic::lint::{LintRegistry, RuleSelection};
|
||||
use ty_python_semantic::{
|
||||
Db, Program, ProgramSettings, PythonEnvironment, PythonPlatform, PythonVersionSource,
|
||||
Db, Program, ProgramSettings, PythonPath, PythonPlatform, PythonVersionSource,
|
||||
PythonVersionWithSource, SearchPathSettings, SysPrefixPathOrigin, default_lint_registry,
|
||||
};
|
||||
|
||||
@@ -35,32 +35,24 @@ impl ModuleDb {
|
||||
python_version: PythonVersion,
|
||||
venv_path: Option<SystemPathBuf>,
|
||||
) -> Result<Self> {
|
||||
let db = Self::default();
|
||||
let mut search_paths = SearchPathSettings::new(src_roots);
|
||||
// TODO: Consider calling `PythonEnvironment::discover` if the `venv_path` is not provided.
|
||||
if let Some(venv_path) = venv_path {
|
||||
let environment =
|
||||
PythonEnvironment::new(venv_path, SysPrefixPathOrigin::PythonCliFlag, db.system())?;
|
||||
search_paths.site_packages_paths = environment
|
||||
.site_packages_paths(db.system())
|
||||
.context("Failed to discover the site-packages directory")?
|
||||
.into_vec();
|
||||
search_paths.python_path =
|
||||
PythonPath::sys_prefix(venv_path, SysPrefixPathOrigin::PythonCliFlag);
|
||||
}
|
||||
let search_paths = search_paths
|
||||
.to_search_paths(db.system(), db.vendored())
|
||||
.context("Invalid search path settings")?;
|
||||
|
||||
let db = Self::default();
|
||||
Program::from_settings(
|
||||
&db,
|
||||
ProgramSettings {
|
||||
python_version: PythonVersionWithSource {
|
||||
python_version: Some(PythonVersionWithSource {
|
||||
version: python_version,
|
||||
source: PythonVersionSource::default(),
|
||||
},
|
||||
}),
|
||||
python_platform: PythonPlatform::default(),
|
||||
search_paths,
|
||||
},
|
||||
);
|
||||
)?;
|
||||
|
||||
Ok(db)
|
||||
}
|
||||
|
||||
@@ -36,20 +36,14 @@ impl fmt::Display for AnalyzeSettings {
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, CacheKey)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Serialize, serde::Deserialize),
|
||||
serde(rename_all = "kebab-case")
|
||||
)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
|
||||
pub enum Direction {
|
||||
/// Construct a map from module to its dependencies (i.e., the modules that it imports).
|
||||
#[default]
|
||||
#[cfg_attr(feature = "serde", serde(alias = "Dependencies"))]
|
||||
Dependencies,
|
||||
/// Construct a map from module to its dependents (i.e., the modules that import it).
|
||||
#[cfg_attr(feature = "serde", serde(alias = "Dependents"))]
|
||||
Dependents,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.12.1"
|
||||
version = "0.12.0"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
def f_byte():
|
||||
raise RuntimeError(b"This is an example exception")
|
||||
|
||||
|
||||
def f_byte_empty():
|
||||
raise RuntimeError(b"")
|
||||
@@ -83,13 +83,3 @@ def aliased_parentheses_no_params_multiline():
|
||||
# scope="module"
|
||||
)
|
||||
def my_fixture(): ...
|
||||
|
||||
|
||||
@(pytest.fixture())
|
||||
def outer_paren_fixture_no_params():
|
||||
return 42
|
||||
|
||||
|
||||
@(fixture())
|
||||
def outer_paren_imported_fixture_no_params():
|
||||
return 42
|
||||
|
||||
@@ -88,14 +88,3 @@ class TestClass:
|
||||
# ),
|
||||
)
|
||||
def test_bar(param1, param2): ...
|
||||
|
||||
|
||||
@(pytest.mark.foo())
|
||||
def test_outer_paren_mark_function():
|
||||
pass
|
||||
|
||||
|
||||
class TestClass:
|
||||
@(pytest.mark.foo())
|
||||
def test_method_outer_paren():
|
||||
pass
|
||||
|
||||
@@ -128,16 +128,3 @@ def write_models(directory, Models):
|
||||
pass; \
|
||||
\
|
||||
#
|
||||
|
||||
# Regression tests for: https://github.com/astral-sh/ruff/issues/18209
|
||||
try:
|
||||
1 / 0
|
||||
except ():
|
||||
pass
|
||||
|
||||
|
||||
BaseException = ValueError
|
||||
try:
|
||||
int("a")
|
||||
except BaseException:
|
||||
pass
|
||||
@@ -29,8 +29,3 @@ def foo():
|
||||
dict = {}
|
||||
for country, stars in zip(dict.keys(), dict.values()):
|
||||
...
|
||||
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18776
|
||||
flag_stars = {}
|
||||
for country, stars in(zip)(flag_stars.keys(), flag_stars.values()):...
|
||||
|
||||
@@ -2,81 +2,13 @@ import os.path
|
||||
from pathlib import Path
|
||||
from os.path import getsize
|
||||
|
||||
filename = "filename"
|
||||
filename1 = __file__
|
||||
filename2 = Path("filename")
|
||||
|
||||
|
||||
os.path.getsize("filename")
|
||||
os.path.getsize(b"filename")
|
||||
os.path.getsize(Path("filename"))
|
||||
os.path.getsize(__file__)
|
||||
|
||||
os.path.getsize(filename)
|
||||
os.path.getsize(filename1)
|
||||
os.path.getsize(filename2)
|
||||
|
||||
os.path.getsize(filename="filename")
|
||||
os.path.getsize(filename=b"filename")
|
||||
os.path.getsize(filename=Path("filename"))
|
||||
os.path.getsize(filename=__file__)
|
||||
|
||||
getsize("filename")
|
||||
getsize(b"filename")
|
||||
getsize(Path("filename"))
|
||||
getsize(__file__)
|
||||
|
||||
getsize(filename="filename")
|
||||
getsize(filename=b"filename")
|
||||
getsize(filename=Path("filename"))
|
||||
getsize(filename=__file__)
|
||||
|
||||
getsize(filename)
|
||||
getsize(filename1)
|
||||
getsize(filename2)
|
||||
|
||||
|
||||
os.path.getsize(
|
||||
"filename", # comment
|
||||
)
|
||||
|
||||
os.path.getsize(
|
||||
# comment
|
||||
"filename"
|
||||
,
|
||||
# comment
|
||||
)
|
||||
|
||||
os.path.getsize(
|
||||
# comment
|
||||
b"filename"
|
||||
# comment
|
||||
)
|
||||
|
||||
os.path.getsize( # comment
|
||||
Path(__file__)
|
||||
# comment
|
||||
) # comment
|
||||
|
||||
getsize( # comment
|
||||
"filename")
|
||||
|
||||
getsize( # comment
|
||||
b"filename",
|
||||
#comment
|
||||
)
|
||||
|
||||
os.path.getsize("file" + "name")
|
||||
|
||||
getsize \
|
||||
\
|
||||
\
|
||||
( # comment
|
||||
"filename",
|
||||
)
|
||||
|
||||
getsize(Path("filename").resolve())
|
||||
|
||||
import pathlib
|
||||
|
||||
os.path.getsize(pathlib.Path("filename"))
|
||||
@@ -1,5 +0,0 @@
|
||||
import os
|
||||
|
||||
os.path.getsize(filename="filename")
|
||||
os.path.getsize(filename=b"filename")
|
||||
os.path.getsize(filename=__file__)
|
||||
@@ -85,19 +85,3 @@ import builtins
|
||||
|
||||
for i in builtins.list(nested_tuple): # PERF101
|
||||
pass
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18783
|
||||
items = (1, 2, 3)
|
||||
for i in(list)(items):
|
||||
print(i)
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18784
|
||||
items = (1, 2, 3)
|
||||
for i in ( # 1
|
||||
list # 2
|
||||
# 3
|
||||
)( # 4
|
||||
items # 5
|
||||
# 6
|
||||
):
|
||||
print(i)
|
||||
|
||||
@@ -163,10 +163,4 @@ def foo():
|
||||
|
||||
for k, v in src:
|
||||
if lambda: 0:
|
||||
dst[k] = v
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18859
|
||||
def foo():
|
||||
v = {}
|
||||
for o,(x,)in():
|
||||
v[x,]=o
|
||||
dst[k] = v
|
||||
@@ -14,6 +14,3 @@ hidden = {"a": "!"}
|
||||
"" % {
|
||||
'test1': '',
|
||||
'test2': '',
|
||||
}
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18806
|
||||
|
||||
@@ -4,11 +4,3 @@
|
||||
"{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522
|
||||
(''
|
||||
.format(x=2)) # F522
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18806
|
||||
# The fix here is unsafe because the unused argument has side effect
|
||||
"Hello, {name}".format(greeting=print(1), name="World")
|
||||
|
||||
# The fix here is safe because the unused argument has no side effect,
|
||||
# even though the used argument has a side effect
|
||||
"Hello, {name}".format(greeting="Pikachu", name=print(1))
|
||||
|
||||
@@ -35,11 +35,3 @@
|
||||
# Removing the final argument.
|
||||
"Hello".format("world")
|
||||
"Hello".format("world", key="value")
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18806
|
||||
# The fix here is unsafe because the unused argument has side effect
|
||||
"Hello, {0}".format("world", print(1))
|
||||
|
||||
# The fix here is safe because the unused argument has no side effect,
|
||||
# even though the used argument has a side effect
|
||||
"Hello, {0}".format(print(1), "Pikachu")
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# Mock objects
|
||||
# ============
|
||||
# Errors
|
||||
assert my_mock.not_called()
|
||||
assert my_mock.called_once_with()
|
||||
@@ -19,25 +17,3 @@ my_mock.assert_called()
|
||||
my_mock.assert_called_once_with()
|
||||
"""like :meth:`Mock.assert_called_once_with`"""
|
||||
"""like :meth:`MagicMock.assert_called_once_with`"""
|
||||
|
||||
# AsyncMock objects
|
||||
# =================
|
||||
# Errors
|
||||
assert my_mock.not_awaited()
|
||||
assert my_mock.awaited_once_with()
|
||||
assert my_mock.not_awaited
|
||||
assert my_mock.awaited_once_with
|
||||
my_mock.assert_not_awaited
|
||||
my_mock.assert_awaited
|
||||
my_mock.assert_awaited_once_with
|
||||
my_mock.assert_awaited_once_with
|
||||
MyMock.assert_awaited_once_with
|
||||
assert my_mock.awaited
|
||||
|
||||
# OK
|
||||
assert my_mock.await_count == 1
|
||||
my_mock.assert_not_awaited()
|
||||
my_mock.assert_awaited()
|
||||
my_mock.assert_awaited_once_with()
|
||||
"""like :meth:`Mock.assert_awaited_once_with`"""
|
||||
"""like :meth:`MagicMock.assert_awaited_once_with`"""
|
||||
|
||||
Binary file not shown.
@@ -14,7 +14,7 @@ min(1, min(2, 3, key=test))
|
||||
# This will still trigger, to merge the calls without keyword args.
|
||||
min(1, min(2, 3, key=test), min(4, 5))
|
||||
|
||||
# The fix is already unsafe, so deleting comments is okay.
|
||||
# Don't provide a fix if there are comments within the call.
|
||||
min(
|
||||
1, # This is a comment.
|
||||
min(2, 3),
|
||||
|
||||
@@ -129,6 +129,3 @@ blah = dict[{"a": 1}.__delitem__("a")] # OK
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/14597
|
||||
assert "abc".__str__() == "abc"
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18813
|
||||
three = 1 if 1 else(3.0).__str__()
|
||||
|
||||
@@ -71,47 +71,3 @@ if sys.version_info <= (3, 14, 0):
|
||||
|
||||
if sys.version_info <= (3, 14, 15):
|
||||
print()
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18165
|
||||
|
||||
if sys.version_info.major >= 3:
|
||||
print("3")
|
||||
else:
|
||||
print("2")
|
||||
|
||||
if sys.version_info.major > 3:
|
||||
print("3")
|
||||
else:
|
||||
print("2")
|
||||
|
||||
if sys.version_info.major <= 3:
|
||||
print("3")
|
||||
else:
|
||||
print("2")
|
||||
|
||||
if sys.version_info.major < 3:
|
||||
print("3")
|
||||
else:
|
||||
print("2")
|
||||
|
||||
if sys.version_info.major == 3:
|
||||
print("3")
|
||||
else:
|
||||
print("2")
|
||||
|
||||
# Semantically incorrect, skip fixing
|
||||
|
||||
if sys.version_info.major[1] > 3:
|
||||
print(3)
|
||||
else:
|
||||
print(2)
|
||||
|
||||
if sys.version_info.major > (3, 13):
|
||||
print(3)
|
||||
else:
|
||||
print(2)
|
||||
|
||||
if sys.version_info.major[:2] > (3, 13):
|
||||
print(3)
|
||||
else:
|
||||
print(2)
|
||||
|
||||
@@ -44,13 +44,6 @@ log(1, math.e)
|
||||
math.log(1, 2.0001)
|
||||
math.log(1, 10.0001)
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18747
|
||||
def log():
|
||||
yield math.log((yield), math.e)
|
||||
|
||||
|
||||
def log():
|
||||
yield math.log((yield from x), math.e)
|
||||
|
||||
# see: https://github.com/astral-sh/ruff/issues/18639
|
||||
math.log(1, 10 # comment
|
||||
|
||||
@@ -20,12 +20,6 @@ _ = Decimal.from_float(float("-inf"))
|
||||
_ = Decimal.from_float(float("Infinity"))
|
||||
_ = Decimal.from_float(float("-Infinity"))
|
||||
_ = Decimal.from_float(float("nan"))
|
||||
_ = Decimal.from_float(float("-NaN "))
|
||||
_ = Decimal.from_float(float(" \n+nan \t"))
|
||||
_ = Decimal.from_float(float(" iNf \n\t "))
|
||||
_ = Decimal.from_float(float(" -inF\n \t"))
|
||||
_ = Decimal.from_float(float(" InfinIty \n\t "))
|
||||
_ = Decimal.from_float(float(" -InfinIty\n \t"))
|
||||
|
||||
# OK
|
||||
_ = Fraction(0.1)
|
||||
|
||||
@@ -110,19 +110,3 @@ class ShouldMatchB008RuleOfImmutableTypeAnnotationIgnored:
|
||||
# ignored
|
||||
this_is_fine: int = f()
|
||||
|
||||
|
||||
# Test for:
|
||||
# https://github.com/astral-sh/ruff/issues/17424
|
||||
@dataclass(frozen=True)
|
||||
class C:
|
||||
foo: int = 1
|
||||
|
||||
|
||||
@dataclass
|
||||
class D:
|
||||
c: C = C()
|
||||
|
||||
|
||||
@dataclass
|
||||
class E:
|
||||
c: C = C()
|
||||
@@ -73,55 +73,3 @@ class IntConversionDescriptor:
|
||||
@frozen
|
||||
class InventoryItem:
|
||||
quantity_on_hand: IntConversionDescriptor = IntConversionDescriptor(default=100)
|
||||
|
||||
|
||||
# Test for:
|
||||
# https://github.com/astral-sh/ruff/issues/17424
|
||||
@frozen
|
||||
class C:
|
||||
foo: int = 1
|
||||
|
||||
|
||||
@attr.frozen
|
||||
class D:
|
||||
foo: int = 1
|
||||
|
||||
|
||||
@define
|
||||
class E:
|
||||
c: C = C()
|
||||
d: D = D()
|
||||
|
||||
|
||||
@attr.s
|
||||
class F:
|
||||
foo: int = 1
|
||||
|
||||
|
||||
@attr.mutable
|
||||
class G:
|
||||
foo: int = 1
|
||||
|
||||
|
||||
@attr.attrs
|
||||
class H:
|
||||
f: F = F()
|
||||
g: G = G()
|
||||
|
||||
|
||||
@attr.define
|
||||
class I:
|
||||
f: F = F()
|
||||
g: G = G()
|
||||
|
||||
|
||||
@attr.frozen
|
||||
class J:
|
||||
f: F = F()
|
||||
g: G = G()
|
||||
|
||||
|
||||
@attr.mutable
|
||||
class K:
|
||||
f: F = F()
|
||||
g: G = G()
|
||||
|
||||
@@ -36,19 +36,5 @@ f"{ascii(bla)}" # OK
|
||||
)
|
||||
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/16325
|
||||
# OK
|
||||
f"{str({})}"
|
||||
|
||||
f"{str({} | {})}"
|
||||
|
||||
import builtins
|
||||
|
||||
f"{builtins.repr(1)}"
|
||||
|
||||
f"{repr(1)=}"
|
||||
|
||||
f"{repr(lambda: 1)}"
|
||||
|
||||
f"{repr(x := 2)}"
|
||||
|
||||
f"{str(object=3)}"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import collections
|
||||
from collections import deque
|
||||
import collections
|
||||
|
||||
|
||||
def f():
|
||||
@@ -91,14 +91,3 @@ def f():
|
||||
|
||||
def f():
|
||||
deque([], iterable=[])
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/18854
|
||||
deque("")
|
||||
deque(b"")
|
||||
deque(f"")
|
||||
deque(f"" "")
|
||||
deque(f"" f"")
|
||||
deque("abc") # OK
|
||||
deque(b"abc") # OK
|
||||
deque(f"" "a") # OK
|
||||
deque(f"{x}" "") # OK
|
||||
|
||||
@@ -34,7 +34,7 @@ pub(crate) fn bindings(checker: &Checker) {
|
||||
if binding.kind.is_bound_exception()
|
||||
&& binding.is_unused()
|
||||
&& !checker
|
||||
.settings()
|
||||
.settings
|
||||
.dummy_variable_rgx
|
||||
.is_match(binding.name(checker.source()))
|
||||
{
|
||||
@@ -67,7 +67,7 @@ pub(crate) fn bindings(checker: &Checker) {
|
||||
flake8_import_conventions::rules::unconventional_import_alias(
|
||||
checker,
|
||||
binding,
|
||||
&checker.settings().flake8_import_conventions.aliases,
|
||||
&checker.settings.flake8_import_conventions.aliases,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::UnaliasedCollectionsAbcSetImport) {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
use ruff_python_semantic::{Binding, ScopeKind};
|
||||
use ruff_python_semantic::analyze::visibility;
|
||||
use ruff_python_semantic::{Binding, BindingKind, Imported, ResolvedReference, ScopeKind};
|
||||
use ruff_text_size::Ranged;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::Fix;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::codes::Rule;
|
||||
use crate::fix;
|
||||
use crate::rules::{
|
||||
flake8_builtins, flake8_pyi, flake8_type_checking, flake8_unused_arguments, pep8_naming,
|
||||
pyflakes, pylint, ruff,
|
||||
@@ -71,7 +76,7 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
|
||||
flake8_type_checking::helpers::is_valid_runtime_import(
|
||||
binding,
|
||||
&checker.semantic,
|
||||
&checker.settings().flake8_type_checking,
|
||||
&checker.settings.flake8_type_checking,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
@@ -89,19 +94,260 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
|
||||
}
|
||||
|
||||
if checker.is_rule_enabled(Rule::GlobalVariableNotAssigned) {
|
||||
pylint::rules::global_variable_not_assigned(checker, scope);
|
||||
for (name, binding_id) in scope.bindings() {
|
||||
let binding = checker.semantic.binding(binding_id);
|
||||
// If the binding is a `global`, then it's a top-level `global` that was never
|
||||
// assigned in the current scope. If it were assigned, the `global` would be
|
||||
// shadowed by the assignment.
|
||||
if binding.kind.is_global() {
|
||||
// If the binding was conditionally deleted, it will include a reference within
|
||||
// a `Del` context, but won't be shadowed by a `BindingKind::Deletion`, as in:
|
||||
// ```python
|
||||
// if condition:
|
||||
// del var
|
||||
// ```
|
||||
if binding
|
||||
.references
|
||||
.iter()
|
||||
.map(|id| checker.semantic.reference(*id))
|
||||
.all(ResolvedReference::is_load)
|
||||
{
|
||||
checker.report_diagnostic(
|
||||
pylint::rules::GlobalVariableNotAssigned {
|
||||
name: (*name).to_string(),
|
||||
},
|
||||
binding.range(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if checker.is_rule_enabled(Rule::RedefinedArgumentFromLocal) {
|
||||
pylint::rules::redefined_argument_from_local(checker, scope_id, scope);
|
||||
for (name, binding_id) in scope.bindings() {
|
||||
for shadow in checker.semantic.shadowed_bindings(scope_id, binding_id) {
|
||||
let binding = &checker.semantic.bindings[shadow.binding_id()];
|
||||
if !matches!(
|
||||
binding.kind,
|
||||
BindingKind::LoopVar
|
||||
| BindingKind::BoundException
|
||||
| BindingKind::WithItemVar
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
let shadowed = &checker.semantic.bindings[shadow.shadowed_id()];
|
||||
if !shadowed.kind.is_argument() {
|
||||
continue;
|
||||
}
|
||||
if checker.settings.dummy_variable_rgx.is_match(name) {
|
||||
continue;
|
||||
}
|
||||
let scope = &checker.semantic.scopes[binding.scope];
|
||||
if scope.kind.is_generator() {
|
||||
continue;
|
||||
}
|
||||
checker.report_diagnostic(
|
||||
pylint::rules::RedefinedArgumentFromLocal {
|
||||
name: name.to_string(),
|
||||
},
|
||||
binding.range(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if checker.is_rule_enabled(Rule::ImportShadowedByLoopVar) {
|
||||
pyflakes::rules::import_shadowed_by_loop_var(checker, scope_id, scope);
|
||||
for (name, binding_id) in scope.bindings() {
|
||||
for shadow in checker.semantic.shadowed_bindings(scope_id, binding_id) {
|
||||
// If the shadowing binding isn't a loop variable, abort.
|
||||
let binding = &checker.semantic.bindings[shadow.binding_id()];
|
||||
if !binding.kind.is_loop_var() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the shadowed binding isn't an import, abort.
|
||||
let shadowed = &checker.semantic.bindings[shadow.shadowed_id()];
|
||||
if !matches!(
|
||||
shadowed.kind,
|
||||
BindingKind::Import(..)
|
||||
| BindingKind::FromImport(..)
|
||||
| BindingKind::SubmoduleImport(..)
|
||||
| BindingKind::FutureImport
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the bindings are in different forks, abort.
|
||||
if shadowed.source.is_none_or(|left| {
|
||||
binding
|
||||
.source
|
||||
.is_none_or(|right| !checker.semantic.same_branch(left, right))
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
checker.report_diagnostic(
|
||||
pyflakes::rules::ImportShadowedByLoopVar {
|
||||
name: name.to_string(),
|
||||
row: checker.compute_source_row(shadowed.start()),
|
||||
},
|
||||
binding.range(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if checker.is_rule_enabled(Rule::RedefinedWhileUnused) {
|
||||
pyflakes::rules::redefined_while_unused(checker, scope_id, scope);
|
||||
// Index the redefined bindings by statement.
|
||||
let mut redefinitions = FxHashMap::default();
|
||||
|
||||
for (name, binding_id) in scope.bindings() {
|
||||
for shadow in checker.semantic.shadowed_bindings(scope_id, binding_id) {
|
||||
// If the shadowing binding is a loop variable, abort, to avoid overlap
|
||||
// with F402.
|
||||
let binding = &checker.semantic.bindings[shadow.binding_id()];
|
||||
if binding.kind.is_loop_var() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the shadowed binding is used, abort.
|
||||
let shadowed = &checker.semantic.bindings[shadow.shadowed_id()];
|
||||
if shadowed.is_used() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the shadowing binding isn't considered a "redefinition" of the
|
||||
// shadowed binding, abort.
|
||||
if !binding.redefines(shadowed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if shadow.same_scope() {
|
||||
// If the symbol is a dummy variable, abort, unless the shadowed
|
||||
// binding is an import.
|
||||
if !matches!(
|
||||
shadowed.kind,
|
||||
BindingKind::Import(..)
|
||||
| BindingKind::FromImport(..)
|
||||
| BindingKind::SubmoduleImport(..)
|
||||
| BindingKind::FutureImport
|
||||
) && checker.settings.dummy_variable_rgx.is_match(name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(node_id) = shadowed.source else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// If this is an overloaded function, abort.
|
||||
if shadowed.kind.is_function_definition() {
|
||||
if checker
|
||||
.semantic
|
||||
.statement(node_id)
|
||||
.as_function_def_stmt()
|
||||
.is_some_and(|function| {
|
||||
visibility::is_overload(
|
||||
&function.decorator_list,
|
||||
&checker.semantic,
|
||||
)
|
||||
})
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Only enforce cross-scope shadowing for imports.
|
||||
if !matches!(
|
||||
shadowed.kind,
|
||||
BindingKind::Import(..)
|
||||
| BindingKind::FromImport(..)
|
||||
| BindingKind::SubmoduleImport(..)
|
||||
| BindingKind::FutureImport
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If the bindings are in different forks, abort.
|
||||
if shadowed.source.is_none_or(|left| {
|
||||
binding
|
||||
.source
|
||||
.is_none_or(|right| !checker.semantic.same_branch(left, right))
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
redefinitions
|
||||
.entry(binding.source)
|
||||
.or_insert_with(Vec::new)
|
||||
.push((shadowed, binding));
|
||||
}
|
||||
}
|
||||
|
||||
// Create a fix for each source statement.
|
||||
let mut fixes = FxHashMap::default();
|
||||
for (source, entries) in &redefinitions {
|
||||
let Some(source) = source else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let member_names = entries
|
||||
.iter()
|
||||
.filter_map(|(shadowed, binding)| {
|
||||
if let Some(shadowed_import) = shadowed.as_any_import() {
|
||||
if let Some(import) = binding.as_any_import() {
|
||||
if shadowed_import.qualified_name() == import.qualified_name() {
|
||||
return Some(import.member_name());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !member_names.is_empty() {
|
||||
let statement = checker.semantic.statement(*source);
|
||||
let parent = checker.semantic.parent_statement(*source);
|
||||
let Ok(edit) = fix::edits::remove_unused_imports(
|
||||
member_names.iter().map(std::convert::AsRef::as_ref),
|
||||
statement,
|
||||
parent,
|
||||
checker.locator(),
|
||||
checker.stylist(),
|
||||
checker.indexer(),
|
||||
) else {
|
||||
continue;
|
||||
};
|
||||
fixes.insert(
|
||||
*source,
|
||||
Fix::safe_edit(edit).isolate(Checker::isolation(
|
||||
checker.semantic().parent_statement_id(*source),
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Create diagnostics for each statement.
|
||||
for (source, entries) in &redefinitions {
|
||||
for (shadowed, binding) in entries {
|
||||
let mut diagnostic = checker.report_diagnostic(
|
||||
pyflakes::rules::RedefinedWhileUnused {
|
||||
name: binding.name(checker.source()).to_string(),
|
||||
row: checker.compute_source_row(shadowed.start()),
|
||||
},
|
||||
binding.range(),
|
||||
);
|
||||
|
||||
if let Some(range) = binding.parent_range(&checker.semantic) {
|
||||
diagnostic.set_parent(range.start());
|
||||
}
|
||||
|
||||
if let Some(fix) = source.as_ref().and_then(|source| fixes.get(source)) {
|
||||
diagnostic.set_fix(fix.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if checker.source_type.is_stub()
|
||||
@@ -156,7 +402,7 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
|
||||
&& binding.is_unused()
|
||||
&& !binding.is_nonlocal()
|
||||
&& !binding.is_global()
|
||||
&& !checker.settings().dummy_variable_rgx.is_match(name)
|
||||
&& !checker.settings.dummy_variable_rgx.is_match(name)
|
||||
&& !matches!(
|
||||
name,
|
||||
"__tracebackhide__"
|
||||
|
||||
@@ -167,7 +167,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
if enforce_docstrings || enforce_pydoclint {
|
||||
if pydocstyle::helpers::should_ignore_definition(
|
||||
definition,
|
||||
&checker.settings().pydocstyle,
|
||||
&checker.settings.pydocstyle,
|
||||
&checker.semantic,
|
||||
) {
|
||||
continue;
|
||||
@@ -253,7 +253,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
pydocstyle::rules::non_imperative_mood(
|
||||
checker,
|
||||
&docstring,
|
||||
&checker.settings().pydocstyle,
|
||||
&checker.settings.pydocstyle,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::SignatureInDocstring) {
|
||||
@@ -292,7 +292,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
if enforce_sections || enforce_pydoclint {
|
||||
let section_contexts = pydocstyle::helpers::get_section_contexts(
|
||||
&docstring,
|
||||
checker.settings().pydocstyle.convention(),
|
||||
checker.settings.pydocstyle.convention(),
|
||||
);
|
||||
|
||||
if enforce_sections {
|
||||
@@ -300,7 +300,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
checker,
|
||||
&docstring,
|
||||
§ion_contexts,
|
||||
checker.settings().pydocstyle.convention(),
|
||||
checker.settings.pydocstyle.convention(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||
definition,
|
||||
&docstring,
|
||||
§ion_contexts,
|
||||
checker.settings().pydocstyle.convention(),
|
||||
checker.settings.pydocstyle.convention(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &Checker)
|
||||
except_handler,
|
||||
type_.as_deref(),
|
||||
body,
|
||||
checker.settings().flake8_bandit.check_typed_exception,
|
||||
checker.settings.flake8_bandit.check_typed_exception,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::TryExceptContinue) {
|
||||
@@ -50,7 +50,7 @@ pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &Checker)
|
||||
except_handler,
|
||||
type_.as_deref(),
|
||||
body,
|
||||
checker.settings().flake8_bandit.check_typed_exception,
|
||||
checker.settings.flake8_bandit.check_typed_exception,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::ExceptWithEmptyTuple) {
|
||||
|
||||
@@ -35,7 +35,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||
&& checker.target_version() < PythonVersion::PY310
|
||||
&& checker.target_version() >= PythonVersion::PY37
|
||||
&& checker.semantic.in_annotation()
|
||||
&& !checker.settings().pyupgrade.keep_runtime_typing
|
||||
&& !checker.settings.pyupgrade.keep_runtime_typing
|
||||
{
|
||||
flake8_future_annotations::rules::future_rewritable_type_annotation(
|
||||
checker, value,
|
||||
@@ -51,7 +51,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||
|| (checker.target_version() >= PythonVersion::PY37
|
||||
&& checker.semantic.future_annotations_or_stub()
|
||||
&& checker.semantic.in_annotation()
|
||||
&& !checker.settings().pyupgrade.keep_runtime_typing)
|
||||
&& !checker.settings.pyupgrade.keep_runtime_typing)
|
||||
{
|
||||
pyupgrade::rules::non_pep604_annotation(checker, expr, slice, operator);
|
||||
}
|
||||
@@ -288,7 +288,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||
&& checker.target_version() < PythonVersion::PY39
|
||||
&& checker.target_version() >= PythonVersion::PY37
|
||||
&& checker.semantic.in_annotation()
|
||||
&& !checker.settings().pyupgrade.keep_runtime_typing
|
||||
&& !checker.settings.pyupgrade.keep_runtime_typing
|
||||
{
|
||||
flake8_future_annotations::rules::future_rewritable_type_annotation(checker, expr);
|
||||
}
|
||||
@@ -299,7 +299,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||
|| (checker.target_version() >= PythonVersion::PY37
|
||||
&& checker.semantic.future_annotations_or_stub()
|
||||
&& checker.semantic.in_annotation()
|
||||
&& !checker.settings().pyupgrade.keep_runtime_typing)
|
||||
&& !checker.settings.pyupgrade.keep_runtime_typing)
|
||||
{
|
||||
pyupgrade::rules::use_pep585_annotation(
|
||||
checker,
|
||||
@@ -395,7 +395,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||
&& checker.target_version() < PythonVersion::PY39
|
||||
&& checker.target_version() >= PythonVersion::PY37
|
||||
&& checker.semantic.in_annotation()
|
||||
&& !checker.settings().pyupgrade.keep_runtime_typing
|
||||
&& !checker.settings.pyupgrade.keep_runtime_typing
|
||||
{
|
||||
flake8_future_annotations::rules::future_rewritable_type_annotation(
|
||||
checker, expr,
|
||||
@@ -408,7 +408,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||
|| (checker.target_version() >= PythonVersion::PY37
|
||||
&& checker.semantic.future_annotations_or_stub()
|
||||
&& checker.semantic.in_annotation()
|
||||
&& !checker.settings().pyupgrade.keep_runtime_typing)
|
||||
&& !checker.settings.pyupgrade.keep_runtime_typing)
|
||||
{
|
||||
pyupgrade::rules::use_pep585_annotation(checker, expr, &replacement);
|
||||
}
|
||||
@@ -539,13 +539,14 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||
let location = expr.range();
|
||||
match pyflakes::format::FormatSummary::try_from(string_value.to_str()) {
|
||||
Err(e) => {
|
||||
// F521
|
||||
checker.report_diagnostic_if_enabled(
|
||||
pyflakes::rules::StringDotFormatInvalidFormat {
|
||||
message: pyflakes::format::error_to_string(&e),
|
||||
},
|
||||
location,
|
||||
);
|
||||
if checker.is_rule_enabled(Rule::StringDotFormatInvalidFormat) {
|
||||
checker.report_diagnostic(
|
||||
pyflakes::rules::StringDotFormatInvalidFormat {
|
||||
message: pyflakes::format::error_to_string(&e),
|
||||
},
|
||||
location,
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(summary) => {
|
||||
if checker
|
||||
@@ -840,7 +841,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||
flake8_comprehensions::rules::unnecessary_collection_call(
|
||||
checker,
|
||||
call,
|
||||
&checker.settings().flake8_comprehensions,
|
||||
&checker.settings.flake8_comprehensions,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::UnnecessaryLiteralWithinTupleCall) {
|
||||
@@ -1005,7 +1006,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||
Rule::PrintfInGetTextFuncCall,
|
||||
]) && flake8_gettext::is_gettext_func_call(
|
||||
func,
|
||||
&checker.settings().flake8_gettext.functions_names,
|
||||
&checker.settings.flake8_gettext.functions_names,
|
||||
) {
|
||||
if checker.is_rule_enabled(Rule::FStringInGetTextFuncCall) {
|
||||
flake8_gettext::rules::f_string_in_gettext_func_call(checker, args);
|
||||
@@ -1055,6 +1056,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||
Rule::OsPathSplitext,
|
||||
Rule::BuiltinOpen,
|
||||
Rule::PyPath,
|
||||
Rule::OsPathGetsize,
|
||||
Rule::OsPathGetatime,
|
||||
Rule::OsPathGetmtime,
|
||||
Rule::OsPathGetctime,
|
||||
@@ -1064,9 +1066,6 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||
]) {
|
||||
flake8_use_pathlib::rules::replaceable_by_pathlib(checker, call);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::OsPathGetsize) {
|
||||
flake8_use_pathlib::rules::os_path_getsize(checker, call);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::PathConstructorCurrentDirectory) {
|
||||
flake8_use_pathlib::rules::path_constructor_current_directory(checker, call);
|
||||
}
|
||||
@@ -1314,22 +1313,26 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
||||
typ: CFormatErrorType::UnsupportedFormatChar(c),
|
||||
..
|
||||
}) => {
|
||||
// F509
|
||||
checker.report_diagnostic_if_enabled(
|
||||
pyflakes::rules::PercentFormatUnsupportedFormatCharacter {
|
||||
char: c,
|
||||
},
|
||||
location,
|
||||
);
|
||||
if checker
|
||||
.is_rule_enabled(Rule::PercentFormatUnsupportedFormatCharacter)
|
||||
{
|
||||
checker.report_diagnostic(
|
||||
pyflakes::rules::PercentFormatUnsupportedFormatCharacter {
|
||||
char: c,
|
||||
},
|
||||
location,
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
// F501
|
||||
checker.report_diagnostic_if_enabled(
|
||||
pyflakes::rules::PercentFormatInvalidFormat {
|
||||
message: e.to_string(),
|
||||
},
|
||||
location,
|
||||
);
|
||||
if checker.is_rule_enabled(Rule::PercentFormatInvalidFormat) {
|
||||
checker.report_diagnostic(
|
||||
pyflakes::rules::PercentFormatInvalidFormat {
|
||||
message: e.to_string(),
|
||||
},
|
||||
location,
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(summary) => {
|
||||
if checker.is_rule_enabled(Rule::PercentFormatExpectedMapping) {
|
||||
|
||||
@@ -45,7 +45,18 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
}
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::NonlocalWithoutBinding) {
|
||||
pylint::rules::nonlocal_without_binding(checker, nonlocal);
|
||||
if !checker.semantic.scope_id.is_global() {
|
||||
for name in names {
|
||||
if checker.semantic.nonlocal(name).is_none() {
|
||||
checker.report_diagnostic(
|
||||
pylint::rules::NonlocalWithoutBinding {
|
||||
name: name.to_string(),
|
||||
},
|
||||
name.range(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::NonlocalAndGlobal) {
|
||||
pylint::rules::nonlocal_and_global(checker, nonlocal);
|
||||
@@ -121,7 +132,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
stmt,
|
||||
name,
|
||||
decorator_list,
|
||||
&checker.settings().pep8_naming.ignore_names,
|
||||
&checker.settings.pep8_naming.ignore_names,
|
||||
&checker.semantic,
|
||||
);
|
||||
}
|
||||
@@ -178,7 +189,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
checker.semantic.current_scope(),
|
||||
stmt,
|
||||
name,
|
||||
&checker.settings().pep8_naming.ignore_names,
|
||||
&checker.settings.pep8_naming.ignore_names,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::GlobalStatement) {
|
||||
@@ -229,7 +240,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
stmt,
|
||||
name,
|
||||
body,
|
||||
checker.settings().mccabe.max_complexity,
|
||||
checker.settings.mccabe.max_complexity,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::HardcodedPasswordDefault) {
|
||||
@@ -254,7 +265,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
checker,
|
||||
stmt,
|
||||
body,
|
||||
checker.settings().pylint.max_returns,
|
||||
checker.settings.pylint.max_returns,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::TooManyBranches) {
|
||||
@@ -262,7 +273,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
checker,
|
||||
stmt,
|
||||
body,
|
||||
checker.settings().pylint.max_branches,
|
||||
checker.settings.pylint.max_branches,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::TooManyStatements) {
|
||||
@@ -270,7 +281,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
checker,
|
||||
stmt,
|
||||
body,
|
||||
checker.settings().pylint.max_statements,
|
||||
checker.settings.pylint.max_statements,
|
||||
);
|
||||
}
|
||||
if checker.any_rule_enabled(&[
|
||||
@@ -420,7 +431,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
pylint::rules::too_many_public_methods(
|
||||
checker,
|
||||
class_def,
|
||||
checker.settings().pylint.max_public_methods,
|
||||
checker.settings.pylint.max_public_methods,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::GlobalStatement) {
|
||||
@@ -448,7 +459,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
checker,
|
||||
stmt,
|
||||
name,
|
||||
&checker.settings().pep8_naming.ignore_names,
|
||||
&checker.settings.pep8_naming.ignore_names,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::ErrorSuffixOnExceptionName) {
|
||||
@@ -457,7 +468,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
stmt,
|
||||
arguments.as_deref(),
|
||||
name,
|
||||
&checker.settings().pep8_naming.ignore_names,
|
||||
&checker.settings.pep8_naming.ignore_names,
|
||||
);
|
||||
}
|
||||
if !checker.source_type.is_stub() {
|
||||
@@ -635,7 +646,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
asname,
|
||||
alias,
|
||||
stmt,
|
||||
&checker.settings().pep8_naming.ignore_names,
|
||||
&checker.settings.pep8_naming.ignore_names,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::LowercaseImportedAsNonLowercase) {
|
||||
@@ -645,7 +656,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
asname,
|
||||
alias,
|
||||
stmt,
|
||||
&checker.settings().pep8_naming.ignore_names,
|
||||
&checker.settings.pep8_naming.ignore_names,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::CamelcaseImportedAsLowercase) {
|
||||
@@ -655,7 +666,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
asname,
|
||||
alias,
|
||||
stmt,
|
||||
&checker.settings().pep8_naming.ignore_names,
|
||||
&checker.settings.pep8_naming.ignore_names,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::CamelcaseImportedAsConstant) {
|
||||
@@ -665,7 +676,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
asname,
|
||||
alias,
|
||||
stmt,
|
||||
&checker.settings().pep8_naming.ignore_names,
|
||||
&checker.settings.pep8_naming.ignore_names,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::CamelcaseImportedAsAcronym) {
|
||||
@@ -681,7 +692,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
stmt,
|
||||
&alias.name,
|
||||
asname,
|
||||
&checker.settings().flake8_import_conventions.banned_aliases,
|
||||
&checker.settings.flake8_import_conventions.banned_aliases,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -817,7 +828,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
pyflakes::rules::future_feature_not_defined(checker, alias);
|
||||
}
|
||||
} else if &alias.name == "*" {
|
||||
// F406
|
||||
if checker.is_rule_enabled(Rule::UndefinedLocalWithNestedImportStarUsage) {
|
||||
if !matches!(checker.semantic.current_scope().kind, ScopeKind::Module) {
|
||||
checker.report_diagnostic(
|
||||
@@ -828,13 +838,14 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
);
|
||||
}
|
||||
}
|
||||
// F403
|
||||
checker.report_diagnostic_if_enabled(
|
||||
pyflakes::rules::UndefinedLocalWithImportStar {
|
||||
name: helpers::format_import_from(level, module).to_string(),
|
||||
},
|
||||
stmt.range(),
|
||||
);
|
||||
if checker.is_rule_enabled(Rule::UndefinedLocalWithImportStar) {
|
||||
checker.report_diagnostic(
|
||||
pyflakes::rules::UndefinedLocalWithImportStar {
|
||||
name: helpers::format_import_from(level, module).to_string(),
|
||||
},
|
||||
stmt.range(),
|
||||
);
|
||||
}
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::RelativeImports) {
|
||||
flake8_tidy_imports::rules::banned_relative_import(
|
||||
@@ -843,7 +854,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
level,
|
||||
module,
|
||||
checker.module.qualified_name(),
|
||||
checker.settings().flake8_tidy_imports.ban_relative_imports,
|
||||
checker.settings.flake8_tidy_imports.ban_relative_imports,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::Debugger) {
|
||||
@@ -858,7 +869,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
stmt,
|
||||
&qualified_name,
|
||||
asname,
|
||||
&checker.settings().flake8_import_conventions.banned_aliases,
|
||||
&checker.settings.flake8_import_conventions.banned_aliases,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -870,7 +881,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
asname,
|
||||
alias,
|
||||
stmt,
|
||||
&checker.settings().pep8_naming.ignore_names,
|
||||
&checker.settings.pep8_naming.ignore_names,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::LowercaseImportedAsNonLowercase) {
|
||||
@@ -880,7 +891,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
asname,
|
||||
alias,
|
||||
stmt,
|
||||
&checker.settings().pep8_naming.ignore_names,
|
||||
&checker.settings.pep8_naming.ignore_names,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::CamelcaseImportedAsLowercase) {
|
||||
@@ -890,7 +901,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
asname,
|
||||
alias,
|
||||
stmt,
|
||||
&checker.settings().pep8_naming.ignore_names,
|
||||
&checker.settings.pep8_naming.ignore_names,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::CamelcaseImportedAsConstant) {
|
||||
@@ -900,7 +911,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
asname,
|
||||
alias,
|
||||
stmt,
|
||||
&checker.settings().pep8_naming.ignore_names,
|
||||
&checker.settings.pep8_naming.ignore_names,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::CamelcaseImportedAsAcronym) {
|
||||
@@ -936,7 +947,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||
checker,
|
||||
stmt,
|
||||
&helpers::format_import_from(level, module),
|
||||
&checker.settings().flake8_import_conventions.banned_from,
|
||||
&checker.settings.flake8_import_conventions.banned_from,
|
||||
);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::ByteStringUsage) {
|
||||
|
||||
@@ -34,7 +34,7 @@ pub(crate) fn string_like(string_like: StringLike, checker: &Checker) {
|
||||
flake8_quotes::rules::unnecessary_escaped_quote(checker, string_like);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::AvoidableEscapedQuote)
|
||||
&& checker.settings().flake8_quotes.avoid_escape
|
||||
&& checker.settings.flake8_quotes.avoid_escape
|
||||
{
|
||||
flake8_quotes::rules::avoidable_escaped_quote(checker, string_like);
|
||||
}
|
||||
|
||||
@@ -13,15 +13,15 @@ pub(crate) fn unresolved_references(checker: &Checker) {
|
||||
|
||||
for reference in checker.semantic.unresolved_references() {
|
||||
if reference.is_wildcard_import() {
|
||||
// F406
|
||||
checker.report_diagnostic_if_enabled(
|
||||
pyflakes::rules::UndefinedLocalWithImportStarUsage {
|
||||
name: reference.name(checker.source()).to_string(),
|
||||
},
|
||||
reference.range(),
|
||||
);
|
||||
if checker.is_rule_enabled(Rule::UndefinedLocalWithImportStarUsage) {
|
||||
checker.report_diagnostic(
|
||||
pyflakes::rules::UndefinedLocalWithImportStarUsage {
|
||||
name: reference.name(checker.source()).to_string(),
|
||||
},
|
||||
reference.range(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// F821
|
||||
if checker.is_rule_enabled(Rule::UndefinedName) {
|
||||
if checker.semantic.in_no_type_check() {
|
||||
continue;
|
||||
|
||||
@@ -28,7 +28,7 @@ use itertools::Itertools;
|
||||
use log::debug;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use ruff_diagnostics::{Applicability, Fix, IsolationLevel};
|
||||
use ruff_diagnostics::IsolationLevel;
|
||||
use ruff_notebook::{CellOffsets, NotebookIndex};
|
||||
use ruff_python_ast::helpers::{collect_import_from_member, is_docstring_stmt, to_module_path};
|
||||
use ruff_python_ast::identifier::Identifier;
|
||||
@@ -207,6 +207,8 @@ pub(crate) struct Checker<'a> {
|
||||
/// The [`NoqaMapping`] for the current analysis (i.e., the mapping from line number to
|
||||
/// suppression commented line number).
|
||||
noqa_line_for: &'a NoqaMapping,
|
||||
/// The [`LinterSettings`] for the current analysis, including the enabled rules.
|
||||
pub(crate) settings: &'a LinterSettings,
|
||||
/// The [`Locator`] for the current file, which enables extraction of source code from byte
|
||||
/// offsets.
|
||||
locator: &'a Locator<'a>,
|
||||
@@ -258,12 +260,13 @@ impl<'a> Checker<'a> {
|
||||
notebook_index: Option<&'a NotebookIndex>,
|
||||
target_version: TargetVersion,
|
||||
context: &'a LintContext<'a>,
|
||||
) -> Self {
|
||||
) -> Checker<'a> {
|
||||
let semantic = SemanticModel::new(&settings.typing_modules, path, module);
|
||||
Self {
|
||||
parsed,
|
||||
parsed_type_annotation: None,
|
||||
parsed_annotations_cache: ParsedAnnotationsCache::new(parsed_annotations_arena),
|
||||
settings,
|
||||
noqa_line_for,
|
||||
noqa,
|
||||
path,
|
||||
@@ -388,7 +391,7 @@ impl<'a> Checker<'a> {
|
||||
|
||||
/// Return a [`DiagnosticGuard`] for reporting a diagnostic.
|
||||
///
|
||||
/// The guard derefs to an [`OldDiagnostic`], so it can be used to further modify the diagnostic
|
||||
/// The guard derefs to a [`Diagnostic`], so it can be used to further modify the diagnostic
|
||||
/// before it is added to the collection in the checker on `Drop`.
|
||||
pub(crate) fn report_diagnostic<'chk, T: Violation>(
|
||||
&'chk self,
|
||||
@@ -401,8 +404,8 @@ impl<'a> Checker<'a> {
|
||||
/// Return a [`DiagnosticGuard`] for reporting a diagnostic if the corresponding rule is
|
||||
/// enabled.
|
||||
///
|
||||
/// The guard derefs to an [`OldDiagnostic`], so it can be used to further modify the diagnostic
|
||||
/// before it is added to the collection in the checker on `Drop`.
|
||||
/// Prefer [`Checker::report_diagnostic`] in general because the conversion from a `Diagnostic`
|
||||
/// to a `Rule` is somewhat expensive.
|
||||
pub(crate) fn report_diagnostic_if_enabled<'chk, T: Violation>(
|
||||
&'chk self,
|
||||
kind: T,
|
||||
@@ -461,11 +464,6 @@ impl<'a> Checker<'a> {
|
||||
&self.semantic
|
||||
}
|
||||
|
||||
/// The [`LinterSettings`] for the current analysis, including the enabled rules.
|
||||
pub(crate) const fn settings(&self) -> &'a LinterSettings {
|
||||
self.context.settings
|
||||
}
|
||||
|
||||
/// The [`Path`] to the file under analysis.
|
||||
pub(crate) const fn path(&self) -> &'a Path {
|
||||
self.path
|
||||
@@ -578,7 +576,7 @@ impl<'a> Checker<'a> {
|
||||
) -> Option<TypingImporter<'b, 'a>> {
|
||||
let source_module = if self.target_version() >= version_added_to_typing {
|
||||
"typing"
|
||||
} else if !self.settings().typing_extensions {
|
||||
} else if !self.settings.typing_extensions {
|
||||
return None;
|
||||
} else {
|
||||
"typing_extensions"
|
||||
@@ -625,7 +623,6 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||
fn report_semantic_error(&self, error: SemanticSyntaxError) {
|
||||
match error.kind {
|
||||
SemanticSyntaxErrorKind::LateFutureImport => {
|
||||
// F404
|
||||
if self.is_rule_enabled(Rule::LateFutureImport) {
|
||||
self.report_diagnostic(LateFutureImport, error.range);
|
||||
}
|
||||
@@ -647,7 +644,6 @@ impl SemanticSyntaxContext for Checker<'_> {
|
||||
}
|
||||
}
|
||||
SemanticSyntaxErrorKind::ReturnOutsideFunction => {
|
||||
// F706
|
||||
if self.is_rule_enabled(Rule::ReturnOutsideFunction) {
|
||||
self.report_diagnostic(ReturnOutsideFunction, error.range);
|
||||
}
|
||||
@@ -1058,7 +1054,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
|
||||
let annotation = AnnotationContext::from_function(
|
||||
function_def,
|
||||
&self.semantic,
|
||||
self.settings(),
|
||||
self.settings,
|
||||
self.target_version(),
|
||||
);
|
||||
|
||||
@@ -1260,7 +1256,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
|
||||
}) => {
|
||||
match AnnotationContext::from_model(
|
||||
&self.semantic,
|
||||
self.settings(),
|
||||
self.settings,
|
||||
self.target_version(),
|
||||
) {
|
||||
AnnotationContext::RuntimeRequired => {
|
||||
@@ -1872,8 +1868,8 @@ impl<'a> Visitor<'a> for Checker<'a> {
|
||||
match typing::match_annotated_subscript(
|
||||
value,
|
||||
&self.semantic,
|
||||
self.settings().typing_modules.iter().map(String::as_str),
|
||||
&self.settings().pyflakes.extend_generics,
|
||||
self.settings.typing_modules.iter().map(String::as_str),
|
||||
&self.settings.pyflakes.extend_generics,
|
||||
) {
|
||||
// Ex) Literal["Class"]
|
||||
Some(typing::SubscriptKind::Literal) => {
|
||||
@@ -2480,7 +2476,6 @@ impl<'a> Checker<'a> {
|
||||
|
||||
fn bind_builtins(&mut self) {
|
||||
let target_version = self.target_version();
|
||||
let settings = self.settings();
|
||||
let mut bind_builtin = |builtin| {
|
||||
// Add the builtin to the scope.
|
||||
let binding_id = self.semantic.push_builtin();
|
||||
@@ -2494,7 +2489,7 @@ impl<'a> Checker<'a> {
|
||||
for builtin in MAGIC_GLOBALS {
|
||||
bind_builtin(builtin);
|
||||
}
|
||||
for builtin in &settings.builtins {
|
||||
for builtin in &self.settings.builtins {
|
||||
bind_builtin(builtin);
|
||||
}
|
||||
}
|
||||
@@ -2810,7 +2805,6 @@ impl<'a> Checker<'a> {
|
||||
Err(parse_error) => {
|
||||
self.semantic.restore(snapshot);
|
||||
|
||||
// F722
|
||||
if self.is_rule_enabled(Rule::ForwardAnnotationSyntaxError) {
|
||||
self.report_type_diagnostic(
|
||||
pyflakes::rules::ForwardAnnotationSyntaxError {
|
||||
@@ -2958,7 +2952,6 @@ impl<'a> Checker<'a> {
|
||||
self.semantic.flags -= SemanticModelFlags::DUNDER_ALL_DEFINITION;
|
||||
} else {
|
||||
if self.semantic.global_scope().uses_star_imports() {
|
||||
// F405
|
||||
if self.is_rule_enabled(Rule::UndefinedLocalWithImportStarUsage) {
|
||||
self.report_diagnostic(
|
||||
pyflakes::rules::UndefinedLocalWithImportStarUsage {
|
||||
@@ -2969,9 +2962,8 @@ impl<'a> Checker<'a> {
|
||||
.set_parent(definition.start());
|
||||
}
|
||||
} else {
|
||||
// F822
|
||||
if self.is_rule_enabled(Rule::UndefinedExport) {
|
||||
if is_undefined_export_in_dunder_init_enabled(self.settings())
|
||||
if is_undefined_export_in_dunder_init_enabled(self.settings)
|
||||
|| !self.path.ends_with("__init__.py")
|
||||
{
|
||||
self.report_diagnostic(
|
||||
@@ -3123,6 +3115,7 @@ pub(crate) struct LintContext<'a> {
|
||||
diagnostics: RefCell<Vec<OldDiagnostic>>,
|
||||
source_file: SourceFile,
|
||||
rules: RuleTable,
|
||||
#[expect(unused, reason = "TODO(brent) use this instead of Checker::settings")]
|
||||
settings: &'a LinterSettings,
|
||||
}
|
||||
|
||||
@@ -3150,7 +3143,7 @@ impl<'a> LintContext<'a> {
|
||||
/// Return a [`DiagnosticGuard`] for reporting a diagnostic.
|
||||
///
|
||||
/// The guard derefs to an [`OldDiagnostic`], so it can be used to further modify the diagnostic
|
||||
/// before it is added to the collection in the context on `Drop`.
|
||||
/// before it is added to the collection in the collector on `Drop`.
|
||||
pub(crate) fn report_diagnostic<'chk, T: Violation>(
|
||||
&'chk self,
|
||||
kind: T,
|
||||
@@ -3159,26 +3152,23 @@ impl<'a> LintContext<'a> {
|
||||
DiagnosticGuard {
|
||||
context: self,
|
||||
diagnostic: Some(OldDiagnostic::new(kind, range, &self.source_file)),
|
||||
rule: T::rule(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a [`DiagnosticGuard`] for reporting a diagnostic if the corresponding rule is
|
||||
/// enabled.
|
||||
///
|
||||
/// The guard derefs to an [`OldDiagnostic`], so it can be used to further modify the diagnostic
|
||||
/// before it is added to the collection in the context on `Drop`.
|
||||
/// Prefer [`DiagnosticsCollector::report_diagnostic`] in general because the conversion from an
|
||||
/// `OldDiagnostic` to a `Rule` is somewhat expensive.
|
||||
pub(crate) fn report_diagnostic_if_enabled<'chk, T: Violation>(
|
||||
&'chk self,
|
||||
kind: T,
|
||||
range: TextRange,
|
||||
) -> Option<DiagnosticGuard<'chk, 'a>> {
|
||||
let rule = T::rule();
|
||||
if self.is_rule_enabled(rule) {
|
||||
if self.is_rule_enabled(T::rule()) {
|
||||
Some(DiagnosticGuard {
|
||||
context: self,
|
||||
diagnostic: Some(OldDiagnostic::new(kind, range, &self.source_file)),
|
||||
rule,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
@@ -3230,7 +3220,6 @@ pub(crate) struct DiagnosticGuard<'a, 'b> {
|
||||
///
|
||||
/// This is always `Some` until the `Drop` (or `defuse`) call.
|
||||
diagnostic: Option<OldDiagnostic>,
|
||||
rule: Rule,
|
||||
}
|
||||
|
||||
impl DiagnosticGuard<'_, '_> {
|
||||
@@ -3243,50 +3232,6 @@ impl DiagnosticGuard<'_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl DiagnosticGuard<'_, '_> {
|
||||
fn resolve_applicability(&self, fix: &Fix) -> Applicability {
|
||||
self.context
|
||||
.settings
|
||||
.fix_safety
|
||||
.resolve_applicability(self.rule, fix.applicability())
|
||||
}
|
||||
|
||||
/// Set the [`Fix`] used to fix the diagnostic.
|
||||
#[inline]
|
||||
pub(crate) fn set_fix(&mut self, fix: Fix) {
|
||||
if !self.context.rules.should_fix(self.rule) {
|
||||
self.fix = None;
|
||||
return;
|
||||
}
|
||||
let applicability = self.resolve_applicability(&fix);
|
||||
self.fix = Some(fix.with_applicability(applicability));
|
||||
}
|
||||
|
||||
/// Set the [`Fix`] used to fix the diagnostic, if the provided function returns `Ok`.
|
||||
/// Otherwise, log the error.
|
||||
#[inline]
|
||||
pub(crate) fn try_set_fix(&mut self, func: impl FnOnce() -> anyhow::Result<Fix>) {
|
||||
match func() {
|
||||
Ok(fix) => self.set_fix(fix),
|
||||
Err(err) => log::debug!("Failed to create fix for {}: {}", self.name(), err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the [`Fix`] used to fix the diagnostic, if the provided function returns `Ok`.
|
||||
/// Otherwise, log the error.
|
||||
#[inline]
|
||||
pub(crate) fn try_set_optional_fix(
|
||||
&mut self,
|
||||
func: impl FnOnce() -> anyhow::Result<Option<Fix>>,
|
||||
) {
|
||||
match func() {
|
||||
Ok(None) => {}
|
||||
Ok(Some(fix)) => self.set_fix(fix),
|
||||
Err(err) => log::debug!("Failed to create fix for {}: {}", self.name(), err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for DiagnosticGuard<'_, '_> {
|
||||
type Target = OldDiagnostic;
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ use crate::{Edit, Fix, Locator};
|
||||
|
||||
use super::ast::LintContext;
|
||||
|
||||
/// RUF100
|
||||
pub(crate) fn check_noqa(
|
||||
context: &mut LintContext,
|
||||
path: &Path,
|
||||
|
||||
@@ -3,8 +3,8 @@ use anyhow::{Result, bail};
|
||||
use libcst_native::{
|
||||
Arg, Attribute, Call, Comparison, CompoundStatement, Dict, Expression, FormattedString,
|
||||
FormattedStringContent, FormattedStringExpression, FunctionDef, GeneratorExp, If, Import,
|
||||
ImportAlias, ImportFrom, ImportNames, IndentedBlock, Lambda, ListComp, Module, SmallStatement,
|
||||
Statement, Suite, Tuple, With,
|
||||
ImportAlias, ImportFrom, ImportNames, IndentedBlock, Lambda, ListComp, Module, Name,
|
||||
SmallStatement, Statement, Suite, Tuple, With,
|
||||
};
|
||||
use ruff_python_codegen::Stylist;
|
||||
|
||||
@@ -104,6 +104,14 @@ pub(crate) fn match_attribute<'a, 'b>(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn match_name<'a, 'b>(expression: &'a Expression<'b>) -> Result<&'a Name<'b>> {
|
||||
if let Expression::Name(name) = expression {
|
||||
Ok(name)
|
||||
} else {
|
||||
bail!("Expected Expression::Name")
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn match_arg<'a, 'b>(call: &'a Call<'b>) -> Result<&'a Arg<'b>> {
|
||||
if let Some(arg) = call.args.first() {
|
||||
Ok(arg)
|
||||
|
||||
@@ -749,16 +749,15 @@ x = 1 \
|
||||
let diag = {
|
||||
use crate::rules::pycodestyle::rules::MissingNewlineAtEndOfFile;
|
||||
let mut iter = edits.into_iter();
|
||||
let mut diagnostic = OldDiagnostic::new(
|
||||
OldDiagnostic::new(
|
||||
MissingNewlineAtEndOfFile, // The choice of rule here is arbitrary.
|
||||
TextRange::default(),
|
||||
&SourceFileBuilder::new("<filename>", "<code>").finish(),
|
||||
);
|
||||
diagnostic.fix = Some(Fix::safe_edits(
|
||||
)
|
||||
.with_fix(Fix::safe_edits(
|
||||
iter.next().ok_or(anyhow!("expected edits nonempty"))?,
|
||||
iter,
|
||||
));
|
||||
diagnostic
|
||||
))
|
||||
};
|
||||
assert_eq!(apply_fixes([diag].iter(), &locator).code, expect);
|
||||
Ok(())
|
||||
|
||||
@@ -186,13 +186,12 @@ mod tests {
|
||||
edit.into_iter()
|
||||
.map(|edit| {
|
||||
// The choice of rule here is arbitrary.
|
||||
let mut diagnostic = OldDiagnostic::new(
|
||||
let diagnostic = OldDiagnostic::new(
|
||||
MissingNewlineAtEndOfFile,
|
||||
edit.range(),
|
||||
&SourceFileBuilder::new(filename, source).finish(),
|
||||
);
|
||||
diagnostic.fix = Some(Fix::safe_edit(edit));
|
||||
diagnostic
|
||||
diagnostic.with_fix(Fix::safe_edit(edit))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -378,7 +378,32 @@ pub fn check_path(
|
||||
|
||||
let (mut diagnostics, source_file) = context.into_parts();
|
||||
|
||||
if !parsed.has_valid_syntax() {
|
||||
if parsed.has_valid_syntax() {
|
||||
// Remove fixes for any rules marked as unfixable.
|
||||
for diagnostic in &mut diagnostics {
|
||||
if diagnostic
|
||||
.noqa_code()
|
||||
.and_then(|code| code.rule())
|
||||
.is_none_or(|rule| !settings.rules.should_fix(rule))
|
||||
{
|
||||
diagnostic.fix = None;
|
||||
}
|
||||
}
|
||||
|
||||
// Update fix applicability to account for overrides
|
||||
if !settings.fix_safety.is_empty() {
|
||||
for diagnostic in &mut diagnostics {
|
||||
if let Some(fix) = diagnostic.fix.take() {
|
||||
if let Some(rule) = diagnostic.noqa_code().and_then(|code| code.rule()) {
|
||||
let fixed_applicability = settings
|
||||
.fix_safety
|
||||
.resolve_applicability(rule, fix.applicability());
|
||||
diagnostic.set_fix(fix.with_applicability(fixed_applicability));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Avoid fixing in case the source code contains syntax errors.
|
||||
for diagnostic in &mut diagnostics {
|
||||
diagnostic.fix = None;
|
||||
|
||||
@@ -186,6 +186,41 @@ impl OldDiagnostic {
|
||||
)
|
||||
}
|
||||
|
||||
/// Consumes `self` and returns a new `Diagnostic` with the given `fix`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn with_fix(mut self, fix: Fix) -> Self {
|
||||
self.set_fix(fix);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the [`Fix`] used to fix the diagnostic.
|
||||
#[inline]
|
||||
pub fn set_fix(&mut self, fix: Fix) {
|
||||
self.fix = Some(fix);
|
||||
}
|
||||
|
||||
/// Set the [`Fix`] used to fix the diagnostic, if the provided function returns `Ok`.
|
||||
/// Otherwise, log the error.
|
||||
#[inline]
|
||||
pub fn try_set_fix(&mut self, func: impl FnOnce() -> anyhow::Result<Fix>) {
|
||||
match func() {
|
||||
Ok(fix) => self.fix = Some(fix),
|
||||
Err(err) => log::debug!("Failed to create fix for {}: {}", self.name(), err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the [`Fix`] used to fix the diagnostic, if the provided function returns `Ok`.
|
||||
/// Otherwise, log the error.
|
||||
#[inline]
|
||||
pub fn try_set_optional_fix(&mut self, func: impl FnOnce() -> anyhow::Result<Option<Fix>>) {
|
||||
match func() {
|
||||
Ok(None) => {}
|
||||
Ok(Some(fix)) => self.fix = Some(fix),
|
||||
Err(err) => log::debug!("Failed to create fix for {}: {}", self.name(), err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes `self` and returns a new `Diagnostic` with the given parent node.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
|
||||
@@ -50,11 +50,6 @@ pub(crate) const fn is_fix_manual_list_comprehension_enabled(settings: &LinterSe
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/18763
|
||||
pub(crate) const fn is_fix_os_path_getsize_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/11436
|
||||
// https://github.com/astral-sh/ruff/pull/11168
|
||||
pub(crate) const fn is_dunder_init_fix_unused_import_enabled(settings: &LinterSettings) -> bool {
|
||||
@@ -89,13 +84,3 @@ pub(crate) const fn is_ignore_init_files_in_useless_alias_enabled(
|
||||
) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/18547
|
||||
pub(crate) const fn is_invalid_async_mock_access_check_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/18867
|
||||
pub(crate) const fn is_raise_exception_byte_string_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ use crate::registry::Rule;
|
||||
use crate::rules::ruff::rules::InvalidPyprojectToml;
|
||||
use crate::settings::LinterSettings;
|
||||
|
||||
/// RUF200
|
||||
pub fn lint_pyproject_toml(
|
||||
source_file: &SourceFile,
|
||||
settings: &LinterSettings,
|
||||
|
||||
@@ -215,12 +215,6 @@ pub enum Linter {
|
||||
}
|
||||
|
||||
pub trait RuleNamespace: Sized {
|
||||
/// Returns the prefix that every single code that ruff uses to identify
|
||||
/// rules from this linter starts with. In the case that multiple
|
||||
/// `#[prefix]`es are configured for the variant in the `Linter` enum
|
||||
/// definition this is the empty string.
|
||||
fn common_prefix(&self) -> &'static str;
|
||||
|
||||
/// Attempts to parse the given rule code. If the prefix is recognized
|
||||
/// returns the respective variant along with the code with the common
|
||||
/// prefix stripped.
|
||||
|
||||
@@ -265,7 +265,6 @@ mod schema {
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::RuleSelector;
|
||||
use crate::registry::RuleNamespace;
|
||||
use crate::rule_selector::{Linter, RuleCodePrefix};
|
||||
|
||||
impl JsonSchema for RuleSelector {
|
||||
|
||||
@@ -59,17 +59,11 @@ use ruff_python_ast::PythonVersion;
|
||||
/// return commons
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
/// This fix is always unsafe, as adding/removing/changing a function parameter's
|
||||
/// default value can change runtime behavior. Additionally, comments inside the
|
||||
/// deprecated uses might be removed.
|
||||
///
|
||||
/// ## Availability
|
||||
///
|
||||
/// Because this rule relies on the third-party `typing_extensions` module for Python versions
|
||||
/// before 3.9, if the target version is < 3.9 and `typing_extensions` imports have been
|
||||
/// disabled by the [`lint.typing-extensions`] linter option the diagnostic will not be emitted
|
||||
/// and no fix will be offered.
|
||||
/// before 3.9, its diagnostic will not be emitted, and no fix will be offered, if
|
||||
/// `typing_extensions` imports have been disabled by the [`lint.typing-extensions`] linter option.
|
||||
///
|
||||
/// ## Options
|
||||
///
|
||||
|
||||
@@ -273,7 +273,9 @@ pub(crate) fn compare(checker: &Checker, left: &Expr, ops: &[CmpOp], comparators
|
||||
],
|
||||
) = (ops, comparators)
|
||||
{
|
||||
checker.report_diagnostic_if_enabled(SysVersionInfo1CmpInt, left.range());
|
||||
if checker.is_rule_enabled(Rule::SysVersionInfo1CmpInt) {
|
||||
checker.report_diagnostic(SysVersionInfo1CmpInt, left.range());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,7 +294,9 @@ pub(crate) fn compare(checker: &Checker, left: &Expr, ops: &[CmpOp], comparators
|
||||
],
|
||||
) = (ops, comparators)
|
||||
{
|
||||
checker.report_diagnostic_if_enabled(SysVersionInfoMinorCmpInt, left.range());
|
||||
if checker.is_rule_enabled(Rule::SysVersionInfoMinorCmpInt) {
|
||||
checker.report_diagnostic(SysVersionInfoMinorCmpInt, left.range());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,9 +310,11 @@ pub(crate) fn compare(checker: &Checker, left: &Expr, ops: &[CmpOp], comparators
|
||||
) = (ops, comparators)
|
||||
{
|
||||
if value.len() == 1 {
|
||||
checker.report_diagnostic_if_enabled(SysVersionCmpStr10, left.range());
|
||||
} else {
|
||||
checker.report_diagnostic_if_enabled(SysVersionCmpStr3, left.range());
|
||||
if checker.is_rule_enabled(Rule::SysVersionCmpStr10) {
|
||||
checker.report_diagnostic(SysVersionCmpStr10, left.range());
|
||||
}
|
||||
} else if checker.is_rule_enabled(Rule::SysVersionCmpStr3) {
|
||||
checker.report_diagnostic(SysVersionCmpStr3, left.range());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -648,9 +648,9 @@ pub(crate) fn definition(
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if !(checker.settings().flake8_annotations.suppress_dummy_args
|
||||
if !(checker.settings.flake8_annotations.suppress_dummy_args
|
||||
&& checker
|
||||
.settings()
|
||||
.settings
|
||||
.dummy_variable_rgx
|
||||
.is_match(parameter.name()))
|
||||
{
|
||||
@@ -670,15 +670,15 @@ pub(crate) fn definition(
|
||||
if let Some(arg) = ¶meters.vararg {
|
||||
if let Some(expr) = &arg.annotation {
|
||||
has_any_typed_arg = true;
|
||||
if !checker.settings().flake8_annotations.allow_star_arg_any {
|
||||
if !checker.settings.flake8_annotations.allow_star_arg_any {
|
||||
if checker.is_rule_enabled(Rule::AnyType) && !is_overridden {
|
||||
let name = &arg.name;
|
||||
check_dynamically_typed(checker, expr, || format!("*{name}"), &mut diagnostics);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !(checker.settings().flake8_annotations.suppress_dummy_args
|
||||
&& checker.settings().dummy_variable_rgx.is_match(&arg.name))
|
||||
if !(checker.settings.flake8_annotations.suppress_dummy_args
|
||||
&& checker.settings.dummy_variable_rgx.is_match(&arg.name))
|
||||
{
|
||||
if checker.is_rule_enabled(Rule::MissingTypeArgs) {
|
||||
diagnostics.push(checker.report_diagnostic(
|
||||
@@ -696,7 +696,7 @@ pub(crate) fn definition(
|
||||
if let Some(arg) = ¶meters.kwarg {
|
||||
if let Some(expr) = &arg.annotation {
|
||||
has_any_typed_arg = true;
|
||||
if !checker.settings().flake8_annotations.allow_star_arg_any {
|
||||
if !checker.settings.flake8_annotations.allow_star_arg_any {
|
||||
if checker.is_rule_enabled(Rule::AnyType) && !is_overridden {
|
||||
let name = &arg.name;
|
||||
check_dynamically_typed(
|
||||
@@ -708,8 +708,8 @@ pub(crate) fn definition(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !(checker.settings().flake8_annotations.suppress_dummy_args
|
||||
&& checker.settings().dummy_variable_rgx.is_match(&arg.name))
|
||||
if !(checker.settings.flake8_annotations.suppress_dummy_args
|
||||
&& checker.settings.dummy_variable_rgx.is_match(&arg.name))
|
||||
{
|
||||
if checker.is_rule_enabled(Rule::MissingTypeKwargs) {
|
||||
diagnostics.push(checker.report_diagnostic(
|
||||
@@ -732,13 +732,8 @@ pub(crate) fn definition(
|
||||
} else if !(
|
||||
// Allow omission of return annotation if the function only returns `None`
|
||||
// (explicitly or implicitly).
|
||||
checker
|
||||
.settings()
|
||||
.flake8_annotations
|
||||
.suppress_none_returning
|
||||
&& is_none_returning(body)
|
||||
checker.settings.flake8_annotations.suppress_none_returning && is_none_returning(body)
|
||||
) {
|
||||
// ANN206
|
||||
if is_method && visibility::is_classmethod(decorator_list, checker.semantic()) {
|
||||
if checker.is_rule_enabled(Rule::MissingReturnTypeClassMethod) {
|
||||
let return_type = if is_stub_function(function, checker) {
|
||||
@@ -766,7 +761,6 @@ pub(crate) fn definition(
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
} else if is_method && visibility::is_staticmethod(decorator_list, checker.semantic()) {
|
||||
// ANN205
|
||||
if checker.is_rule_enabled(Rule::MissingReturnTypeStaticMethod) {
|
||||
let return_type = if is_stub_function(function, checker) {
|
||||
None
|
||||
@@ -793,11 +787,10 @@ pub(crate) fn definition(
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
} else if is_method && visibility::is_init(name) {
|
||||
// ANN204
|
||||
// Allow omission of return annotation in `__init__` functions, as long as at
|
||||
// least one argument is typed.
|
||||
if checker.is_rule_enabled(Rule::MissingReturnTypeSpecialMethod) {
|
||||
if !(checker.settings().flake8_annotations.mypy_init_return && has_any_typed_arg) {
|
||||
if !(checker.settings.flake8_annotations.mypy_init_return && has_any_typed_arg) {
|
||||
let mut diagnostic = checker.report_diagnostic(
|
||||
MissingReturnTypeSpecialMethod {
|
||||
name: name.to_string(),
|
||||
@@ -908,7 +901,7 @@ pub(crate) fn definition(
|
||||
|
||||
// If settings say so, don't report any of the
|
||||
// diagnostics gathered here if there were no type annotations at all.
|
||||
let diagnostics_enabled = !checker.settings().flake8_annotations.ignore_fully_untyped
|
||||
let diagnostics_enabled = !checker.settings.flake8_annotations.ignore_fully_untyped
|
||||
|| has_any_typed_arg
|
||||
|| has_typed_return
|
||||
|| (is_method
|
||||
|
||||
@@ -84,7 +84,7 @@ pub(crate) fn hardcoded_tmp_directory(checker: &Checker, string: StringLike) {
|
||||
|
||||
fn check(checker: &Checker, value: &str, range: TextRange) {
|
||||
if !checker
|
||||
.settings()
|
||||
.settings
|
||||
.flake8_bandit
|
||||
.hardcoded_tmp_directory
|
||||
.iter()
|
||||
|
||||
@@ -312,21 +312,25 @@ pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) {
|
||||
Some(ShellKeyword {
|
||||
truthiness: truthiness @ (Truthiness::True | Truthiness::Truthy),
|
||||
}) => {
|
||||
checker.report_diagnostic_if_enabled(
|
||||
SubprocessPopenWithShellEqualsTrue {
|
||||
safety: Safety::from(arg),
|
||||
is_exact: matches!(truthiness, Truthiness::True),
|
||||
},
|
||||
call.func.range(),
|
||||
);
|
||||
if checker.is_rule_enabled(Rule::SubprocessPopenWithShellEqualsTrue) {
|
||||
checker.report_diagnostic(
|
||||
SubprocessPopenWithShellEqualsTrue {
|
||||
safety: Safety::from(arg),
|
||||
is_exact: matches!(truthiness, Truthiness::True),
|
||||
},
|
||||
call.func.range(),
|
||||
);
|
||||
}
|
||||
}
|
||||
// S603
|
||||
_ => {
|
||||
if !is_trusted_input(arg) {
|
||||
checker.report_diagnostic_if_enabled(
|
||||
SubprocessWithoutShellEqualsTrue,
|
||||
call.func.range(),
|
||||
);
|
||||
if checker.is_rule_enabled(Rule::SubprocessWithoutShellEqualsTrue) {
|
||||
checker.report_diagnostic(
|
||||
SubprocessWithoutShellEqualsTrue,
|
||||
call.func.range(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -336,12 +340,14 @@ pub(crate) fn shell_injection(checker: &Checker, call: &ast::ExprCall) {
|
||||
}) = shell_keyword
|
||||
{
|
||||
// S604
|
||||
checker.report_diagnostic_if_enabled(
|
||||
CallWithShellEqualsTrue {
|
||||
is_exact: matches!(truthiness, Truthiness::True),
|
||||
},
|
||||
call.func.range(),
|
||||
);
|
||||
if checker.is_rule_enabled(Rule::CallWithShellEqualsTrue) {
|
||||
checker.report_diagnostic(
|
||||
CallWithShellEqualsTrue {
|
||||
is_exact: matches!(truthiness, Truthiness::True),
|
||||
},
|
||||
call.func.range(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// S605
|
||||
|
||||
@@ -936,7 +936,7 @@ pub(crate) fn suspicious_function_call(checker: &Checker, call: &ExprCall) {
|
||||
}
|
||||
|
||||
pub(crate) fn suspicious_function_reference(checker: &Checker, func: &Expr) {
|
||||
if !is_suspicious_function_reference_enabled(checker.settings()) {
|
||||
if !is_suspicious_function_reference_enabled(checker.settings) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1270,7 +1270,7 @@ fn suspicious_function(
|
||||
/// S308
|
||||
pub(crate) fn suspicious_function_decorator(checker: &Checker, decorator: &Decorator) {
|
||||
// In preview mode, references are handled collectively by `suspicious_function_reference`
|
||||
if !is_suspicious_function_reference_enabled(checker.settings()) {
|
||||
if !is_suspicious_function_reference_enabled(checker.settings) {
|
||||
suspicious_function(checker, &decorator.expression, None, decorator.range);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ impl Violation for UnsafeMarkupUse {
|
||||
/// S704
|
||||
pub(crate) fn unsafe_markup_call(checker: &Checker, call: &ExprCall) {
|
||||
if checker
|
||||
.settings()
|
||||
.settings
|
||||
.flake8_bandit
|
||||
.extend_markup_names
|
||||
.is_empty()
|
||||
@@ -100,7 +100,7 @@ pub(crate) fn unsafe_markup_call(checker: &Checker, call: &ExprCall) {
|
||||
return;
|
||||
}
|
||||
|
||||
if !is_unsafe_call(call, checker.semantic(), checker.settings()) {
|
||||
if !is_unsafe_call(call, checker.semantic(), checker.settings) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ pub(crate) fn unsafe_markup_call(checker: &Checker, call: &ExprCall) {
|
||||
return;
|
||||
};
|
||||
|
||||
if !is_markup_call(&qualified_name, checker.settings()) {
|
||||
if !is_markup_call(&qualified_name, checker.settings) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ pub(crate) fn blind_except(
|
||||
}
|
||||
|
||||
// If the exception is logged, don't flag an error.
|
||||
let mut visitor = LogExceptionVisitor::new(semantic, &checker.settings().logger_objects);
|
||||
let mut visitor = LogExceptionVisitor::new(semantic, &checker.settings.logger_objects);
|
||||
visitor.visit_body(body);
|
||||
if visitor.seen() {
|
||||
return;
|
||||
|
||||
@@ -185,7 +185,7 @@ pub(super) fn allow_boolean_trap(call: &ast::ExprCall, checker: &Checker) -> boo
|
||||
}
|
||||
|
||||
// If the call is explicitly allowed by the user, then the boolean trap is allowed.
|
||||
if is_user_allowed_func_call(call, checker.semantic(), checker.settings()) {
|
||||
if is_user_allowed_func_call(call, checker.semantic(), checker.settings) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,6 @@ impl Violation for BooleanPositionalValueInCall {
|
||||
}
|
||||
}
|
||||
|
||||
/// FBT003
|
||||
pub(crate) fn boolean_positional_value_in_call(checker: &Checker, call: &ast::ExprCall) {
|
||||
if allow_boolean_trap(call, checker) {
|
||||
return;
|
||||
|
||||
@@ -88,8 +88,8 @@ pub(crate) fn cached_instance_method(checker: &Checker, function_def: &ast::Stmt
|
||||
&function_def.decorator_list,
|
||||
scope,
|
||||
checker.semantic(),
|
||||
&checker.settings().pep8_naming.classmethod_decorators,
|
||||
&checker.settings().pep8_naming.staticmethod_decorators,
|
||||
&checker.settings.pep8_naming.classmethod_decorators,
|
||||
&checker.settings.pep8_naming.staticmethod_decorators,
|
||||
);
|
||||
if !matches!(type_, function_type::FunctionType::Method) {
|
||||
return;
|
||||
|
||||
@@ -132,7 +132,7 @@ impl Visitor<'_> for ArgumentDefaultVisitor<'_, '_> {
|
||||
pub(crate) fn function_call_in_argument_default(checker: &Checker, parameters: &Parameters) {
|
||||
// Map immutable calls to (module, member) format.
|
||||
let extend_immutable_calls: Vec<QualifiedName> = checker
|
||||
.settings()
|
||||
.settings
|
||||
.flake8_bugbear
|
||||
.extend_immutable_calls
|
||||
.iter()
|
||||
|
||||
@@ -105,7 +105,7 @@ pub(crate) fn mutable_argument_default(checker: &Checker, function_def: &ast::St
|
||||
};
|
||||
|
||||
let extend_immutable_calls: Vec<QualifiedName> = checker
|
||||
.settings()
|
||||
.settings
|
||||
.flake8_bugbear
|
||||
.extend_immutable_calls
|
||||
.iter()
|
||||
|
||||
@@ -82,7 +82,7 @@ pub(crate) fn mutable_contextvar_default(checker: &Checker, call: &ast::ExprCall
|
||||
};
|
||||
|
||||
let extend_immutable_calls: Vec<QualifiedName> = checker
|
||||
.settings()
|
||||
.settings
|
||||
.flake8_bugbear
|
||||
.extend_immutable_calls
|
||||
.iter()
|
||||
|
||||
@@ -46,7 +46,7 @@ impl Violation for StaticKeyDictComprehension {
|
||||
}
|
||||
}
|
||||
|
||||
/// B035, RUF011
|
||||
/// RUF011
|
||||
pub(crate) fn static_key_dict_comprehension(checker: &Checker, dict_comp: &ast::ExprDictComp) {
|
||||
// Collect the bound names in the comprehension's generators.
|
||||
let names = {
|
||||
|
||||
@@ -93,7 +93,7 @@ pub(crate) fn unused_loop_control_variable(checker: &Checker, stmt_for: &ast::St
|
||||
|
||||
for (name, expr) in control_names {
|
||||
// Ignore names that are already underscore-prefixed.
|
||||
if checker.settings().dummy_variable_rgx.is_match(name) {
|
||||
if checker.settings.dummy_variable_rgx.is_match(name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ pub(crate) fn unused_loop_control_variable(checker: &Checker, stmt_for: &ast::St
|
||||
// violation in the next pass.
|
||||
let rename = format!("_{name}");
|
||||
let rename = checker
|
||||
.settings()
|
||||
.settings
|
||||
.dummy_variable_rgx
|
||||
.is_match(rename.as_str())
|
||||
.then_some(rename);
|
||||
|
||||
@@ -66,7 +66,7 @@ pub(crate) fn builtin_argument_shadowing(checker: &Checker, parameter: &Paramete
|
||||
if shadows_builtin(
|
||||
parameter.name(),
|
||||
checker.source_type,
|
||||
&checker.settings().flake8_builtins.ignorelist,
|
||||
&checker.settings.flake8_builtins.ignorelist,
|
||||
checker.target_version(),
|
||||
) {
|
||||
// Ignore parameters in lambda expressions.
|
||||
|
||||
@@ -97,7 +97,7 @@ pub(crate) fn builtin_attribute_shadowing(
|
||||
if shadows_builtin(
|
||||
name,
|
||||
checker.source_type,
|
||||
&checker.settings().flake8_builtins.ignorelist,
|
||||
&checker.settings.flake8_builtins.ignorelist,
|
||||
checker.target_version(),
|
||||
) {
|
||||
// Ignore explicit overrides.
|
||||
|
||||
@@ -59,7 +59,7 @@ pub(crate) fn builtin_import_shadowing(checker: &Checker, alias: &Alias) {
|
||||
if shadows_builtin(
|
||||
name.as_str(),
|
||||
checker.source_type,
|
||||
&checker.settings().flake8_builtins.ignorelist,
|
||||
&checker.settings.flake8_builtins.ignorelist,
|
||||
checker.target_version(),
|
||||
) {
|
||||
checker.report_diagnostic(
|
||||
|
||||
@@ -43,7 +43,7 @@ pub(crate) fn builtin_lambda_argument_shadowing(checker: &Checker, lambda: &Expr
|
||||
if shadows_builtin(
|
||||
name,
|
||||
checker.source_type,
|
||||
&checker.settings().flake8_builtins.ignorelist,
|
||||
&checker.settings.flake8_builtins.ignorelist,
|
||||
checker.target_version(),
|
||||
) {
|
||||
checker.report_diagnostic(
|
||||
|
||||
@@ -70,7 +70,7 @@ pub(crate) fn builtin_variable_shadowing(checker: &Checker, name: &str, range: T
|
||||
if shadows_builtin(
|
||||
name,
|
||||
checker.source_type,
|
||||
&checker.settings().flake8_builtins.ignorelist,
|
||||
&checker.settings.flake8_builtins.ignorelist,
|
||||
checker.target_version(),
|
||||
) {
|
||||
checker.report_diagnostic(
|
||||
|
||||
@@ -126,7 +126,7 @@ pub(crate) fn unnecessary_comprehension_in_call(
|
||||
if !(matches!(
|
||||
builtin_function,
|
||||
SupportedBuiltins::Any | SupportedBuiltins::All
|
||||
) || (is_comprehension_with_min_max_sum_enabled(checker.settings())
|
||||
) || (is_comprehension_with_min_max_sum_enabled(checker.settings)
|
||||
&& matches!(
|
||||
builtin_function,
|
||||
SupportedBuiltins::Sum | SupportedBuiltins::Min | SupportedBuiltins::Max
|
||||
|
||||
@@ -101,7 +101,7 @@ pub(crate) fn unnecessary_literal_within_tuple_call(
|
||||
let argument_kind = match argument {
|
||||
Expr::Tuple(_) => TupleLiteralKind::Tuple,
|
||||
Expr::List(_) => TupleLiteralKind::List,
|
||||
Expr::ListComp(_) if is_check_comprehensions_in_tuple_call_enabled(checker.settings()) => {
|
||||
Expr::ListComp(_) if is_check_comprehensions_in_tuple_call_enabled(checker.settings) => {
|
||||
TupleLiteralKind::ListComp
|
||||
}
|
||||
_ => return,
|
||||
|
||||
@@ -58,7 +58,6 @@ impl Violation for CallDateFromtimestamp {
|
||||
}
|
||||
}
|
||||
|
||||
/// DTZ012
|
||||
pub(crate) fn call_date_fromtimestamp(checker: &Checker, func: &Expr, location: TextRange) {
|
||||
if !checker.semantic().seen_module(Modules::DATETIME) {
|
||||
return;
|
||||
|
||||
@@ -57,7 +57,6 @@ impl Violation for CallDateToday {
|
||||
}
|
||||
}
|
||||
|
||||
/// DTZ011
|
||||
pub(crate) fn call_date_today(checker: &Checker, func: &Expr, location: TextRange) {
|
||||
if !checker.semantic().seen_module(Modules::DATETIME) {
|
||||
return;
|
||||
|
||||
@@ -69,7 +69,6 @@ impl Violation for CallDatetimeFromtimestamp {
|
||||
}
|
||||
}
|
||||
|
||||
/// DTZ006
|
||||
pub(crate) fn call_datetime_fromtimestamp(checker: &Checker, call: &ast::ExprCall) {
|
||||
if !checker.semantic().seen_module(Modules::DATETIME) {
|
||||
return;
|
||||
|
||||
@@ -67,7 +67,6 @@ impl Violation for CallDatetimeNowWithoutTzinfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// DTZ005
|
||||
pub(crate) fn call_datetime_now_without_tzinfo(checker: &Checker, call: &ast::ExprCall) {
|
||||
if !checker.semantic().seen_module(Modules::DATETIME) {
|
||||
return;
|
||||
|
||||
@@ -56,7 +56,6 @@ impl Violation for CallDatetimeToday {
|
||||
}
|
||||
}
|
||||
|
||||
/// DTZ002
|
||||
pub(crate) fn call_datetime_today(checker: &Checker, func: &Expr, location: TextRange) {
|
||||
if !checker.semantic().seen_module(Modules::DATETIME) {
|
||||
return;
|
||||
|
||||
@@ -60,7 +60,6 @@ impl Violation for CallDatetimeUtcfromtimestamp {
|
||||
}
|
||||
}
|
||||
|
||||
/// DTZ004
|
||||
pub(crate) fn call_datetime_utcfromtimestamp(checker: &Checker, func: &Expr, location: TextRange) {
|
||||
if !checker.semantic().seen_module(Modules::DATETIME) {
|
||||
return;
|
||||
|
||||
@@ -63,7 +63,6 @@ impl Violation for CallDatetimeWithoutTzinfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// DTZ001
|
||||
pub(crate) fn call_datetime_without_tzinfo(checker: &Checker, call: &ast::ExprCall) {
|
||||
if !checker.semantic().seen_module(Modules::DATETIME) {
|
||||
return;
|
||||
|
||||
@@ -46,7 +46,6 @@ impl Violation for Debugger {
|
||||
}
|
||||
}
|
||||
|
||||
/// T100
|
||||
/// Checks for the presence of a debugger call.
|
||||
pub(crate) fn debugger_call(checker: &Checker, expr: &Expr, func: &Expr) {
|
||||
if let Some(using_type) =
|
||||
@@ -65,7 +64,6 @@ pub(crate) fn debugger_call(checker: &Checker, expr: &Expr, func: &Expr) {
|
||||
}
|
||||
}
|
||||
|
||||
/// T100
|
||||
/// Checks for the presence of a debugger import.
|
||||
pub(crate) fn debugger_import(checker: &Checker, stmt: &Stmt, module: Option<&str>, name: &str) {
|
||||
if let Some(module) = module {
|
||||
|
||||
@@ -9,7 +9,6 @@ mod tests {
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::types::PreviewMode;
|
||||
use crate::test::test_path;
|
||||
use crate::{assert_diagnostics, settings};
|
||||
|
||||
@@ -45,17 +44,4 @@ mod tests {
|
||||
assert_diagnostics!("custom", diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preview_string_exception() -> Result<()> {
|
||||
let diagnostics = test_path(
|
||||
Path::new("flake8_errmsg/EM101_byte_string.py"),
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
..settings::LinterSettings::for_rule(Rule::RawStringInException)
|
||||
},
|
||||
)?;
|
||||
assert_diagnostics!("preview", diagnostics);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,16 +7,12 @@ use ruff_text_size::Ranged;
|
||||
|
||||
use crate::Locator;
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_raise_exception_byte_string_enabled;
|
||||
use crate::registry::Rule;
|
||||
use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for the use of string literals in exception constructors.
|
||||
///
|
||||
/// In [preview], this rule checks for byte string literals in
|
||||
/// exception constructors.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Python includes the `raise` in the default traceback (and formatters
|
||||
/// like Rich and IPython do too).
|
||||
@@ -51,8 +47,6 @@ use crate::{Edit, Fix, FixAvailability, Violation};
|
||||
/// raise RuntimeError(msg)
|
||||
/// RuntimeError: 'Some value' is incorrect
|
||||
/// ```
|
||||
///
|
||||
/// [preview]: https://docs.astral.sh/ruff/preview/
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct RawStringInException;
|
||||
|
||||
@@ -192,29 +186,7 @@ pub(crate) fn string_in_exception(checker: &Checker, stmt: &Stmt, exc: &Expr) {
|
||||
// Check for string literals.
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value: string, .. }) => {
|
||||
if checker.is_rule_enabled(Rule::RawStringInException) {
|
||||
if string.len() >= checker.settings().flake8_errmsg.max_string_length {
|
||||
let mut diagnostic =
|
||||
checker.report_diagnostic(RawStringInException, first.range());
|
||||
if let Some(indentation) =
|
||||
whitespace::indentation(checker.source(), stmt)
|
||||
{
|
||||
diagnostic.set_fix(generate_fix(
|
||||
stmt,
|
||||
first,
|
||||
indentation,
|
||||
checker.stylist(),
|
||||
checker.locator(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for byte string literals.
|
||||
Expr::BytesLiteral(ast::ExprBytesLiteral { value: bytes, .. }) => {
|
||||
if checker.settings().rules.enabled(Rule::RawStringInException) {
|
||||
if bytes.len() >= checker.settings().flake8_errmsg.max_string_length
|
||||
&& is_raise_exception_byte_string_enabled(checker.settings())
|
||||
{
|
||||
if string.len() >= checker.settings.flake8_errmsg.max_string_length {
|
||||
let mut diagnostic =
|
||||
checker.report_diagnostic(RawStringInException, first.range());
|
||||
if let Some(indentation) =
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user