Compare commits
22 Commits
logical-in
...
v0.1.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f460f9c5c0 | ||
|
|
2faac1e7a8 | ||
|
|
b7dbb9062c | ||
|
|
66794bc9fe | ||
|
|
dca430f4d2 | ||
|
|
841e6c889e | ||
|
|
bd99175fea | ||
|
|
4c86b155f2 | ||
|
|
e2109c1353 | ||
|
|
1fcccf82fc | ||
|
|
14e65afdc6 | ||
|
|
6d5d079a18 | ||
|
|
d1e88dc984 | ||
|
|
dda31b6996 | ||
|
|
b6a7787318 | ||
|
|
5fa961f670 | ||
|
|
2424188bb2 | ||
|
|
a59172528c | ||
|
|
cd29761b9c | ||
|
|
4ac78d5725 | ||
|
|
2083352ae3 | ||
|
|
0e2ece5217 |
@@ -1,37 +1,3 @@
|
||||
[alias]
|
||||
dev = "run --package ruff_dev --bin ruff_dev"
|
||||
benchmark = "bench -p ruff_benchmark --bench linter --bench formatter --"
|
||||
|
||||
[target.'cfg(all())']
|
||||
rustflags = [
|
||||
# CLIPPY LINT SETTINGS
|
||||
# This is a workaround to configure lints for the entire workspace, pending the ability to configure this via TOML.
|
||||
# See: `https://github.com/rust-lang/cargo/issues/5034`
|
||||
# `https://github.com/EmbarkStudios/rust-ecosystem/issues/22#issuecomment-947011395`
|
||||
"-Dunsafe_code",
|
||||
"-Wclippy::pedantic",
|
||||
# Allowed pedantic lints
|
||||
"-Wclippy::char_lit_as_u8",
|
||||
"-Aclippy::collapsible_else_if",
|
||||
"-Aclippy::collapsible_if",
|
||||
"-Aclippy::implicit_hasher",
|
||||
"-Aclippy::match_same_arms",
|
||||
"-Aclippy::missing_errors_doc",
|
||||
"-Aclippy::missing_panics_doc",
|
||||
"-Aclippy::module_name_repetitions",
|
||||
"-Aclippy::must_use_candidate",
|
||||
"-Aclippy::similar_names",
|
||||
"-Aclippy::too_many_lines",
|
||||
# Disallowed restriction lints
|
||||
"-Wclippy::print_stdout",
|
||||
"-Wclippy::print_stderr",
|
||||
"-Wclippy::dbg_macro",
|
||||
"-Wclippy::empty_drop",
|
||||
"-Wclippy::empty_structs_with_brackets",
|
||||
"-Wclippy::exit",
|
||||
"-Wclippy::get_unwrap",
|
||||
"-Wclippy::rc_buffer",
|
||||
"-Wclippy::rc_mutex",
|
||||
"-Wclippy::rest_pat_in_fully_bound_structs",
|
||||
"-Wunreachable_pub"
|
||||
]
|
||||
|
||||
73
CHANGELOG.md
73
CHANGELOG.md
@@ -1,5 +1,78 @@
|
||||
# Changelog
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Preview features
|
||||
|
||||
- \[`flake8-boolean-trap`\] Extend `boolean-type-hint-positional-argument` (`FBT001`) to include booleans in unions ([#7501](https://github.com/astral-sh/ruff/pull/7501))
|
||||
- \[`flake8-pie`\] Extend `reimplemented-list-builtin` (`PIE807`) to `dict` reimplementations ([#8608](https://github.com/astral-sh/ruff/pull/8608))
|
||||
- \[`flake8-pie`\] Extend `unnecessary-pass` (`PIE790`) to include ellipses (`...`) ([#8641](https://github.com/astral-sh/ruff/pull/8641))
|
||||
- \[`flake8-pie`\] Implement fix for `unnecessary-spread` (`PIE800`) ([#8668](https://github.com/astral-sh/ruff/pull/8668))
|
||||
- \[`flake8-quotes`\] Implement `unnecessary-escaped-quote` (`Q004`) ([#8630](https://github.com/astral-sh/ruff/pull/8630))
|
||||
- \[`pycodestyle`\] Implement fix for `multiple-spaces-after-keyword` (`E271`) and `multiple-spaces-before-keyword` (`E272`) ([#8622](https://github.com/astral-sh/ruff/pull/8622))
|
||||
- \[`pycodestyle`\] Implement fix for `multiple-spaces-after-operator` (`E222`) and `multiple-spaces-before-operator` (`E221`) ([#8623](https://github.com/astral-sh/ruff/pull/8623))
|
||||
- \[`pyflakes`\] Extend `is-literal` (`F632`) to include comparisons against mutable initializers ([#8607](https://github.com/astral-sh/ruff/pull/8607))
|
||||
- \[`pylint`\] Implement `redefined-argument-from-local` (`PLR1704`) ([#8159](https://github.com/astral-sh/ruff/pull/8159))
|
||||
- \[`pylint`\] Implement fix for `unnecessary-lambda` (`PLW0108`) ([#8621](https://github.com/astral-sh/ruff/pull/8621))
|
||||
- \[`refurb`\] Implement `if-expr-min-max` (`FURB136`) ([#8664](https://github.com/astral-sh/ruff/pull/8664))
|
||||
- \[`refurb`\] Implement `math-constant` (`FURB152`) ([#8727](https://github.com/astral-sh/ruff/pull/8727))
|
||||
|
||||
### Rule changes
|
||||
|
||||
- \[`flake8-annotations`\] Add autotyping-like return type inference for annotation rules ([#8643](https://github.com/astral-sh/ruff/pull/8643))
|
||||
- \[`flake8-future-annotations`\] Implement fix for `future-required-type-annotation` (`FA102`) ([#8711](https://github.com/astral-sh/ruff/pull/8711))
|
||||
- \[`flake8-implicit-namespace-package`\] Avoid missing namespace violations in scripts with shebangs ([#8710](https://github.com/astral-sh/ruff/pull/8710))
|
||||
- \[`pydocstyle`\] Update `over-indentation` (`D208`) to preserve indentation offsets when fixing overindented lines ([#8699](https://github.com/astral-sh/ruff/pull/8699))
|
||||
- \[`pyupgrade`\] Refine `timeout-error-alias` (`UP041`) to remove false positives ([#8587](https://github.com/astral-sh/ruff/pull/8587))
|
||||
|
||||
### Formatter
|
||||
|
||||
- Fix instability in `await` formatting with fluent style ([#8676](https://github.com/astral-sh/ruff/pull/8676))
|
||||
- Compare formatted and unformatted ASTs during formatter tests ([#8624](https://github.com/astral-sh/ruff/pull/8624))
|
||||
- Preserve trailing semicolon for Notebooks ([#8590](https://github.com/astral-sh/ruff/pull/8590))
|
||||
|
||||
### CLI
|
||||
|
||||
- Improve debug printing for resolving origin of config settings ([#8729](https://github.com/astral-sh/ruff/pull/8729))
|
||||
- Write unchanged, excluded files to stdout when read via stdin ([#8596](https://github.com/astral-sh/ruff/pull/8596))
|
||||
|
||||
### Configuration
|
||||
|
||||
- \[`isort`\] Support disabling sections with `no-sections = true` ([#8657](https://github.com/astral-sh/ruff/pull/8657))
|
||||
- \[`pep8-naming`\] Support local and dynamic class- and static-method decorators ([#8592](https://github.com/astral-sh/ruff/pull/8592))
|
||||
- \[`pydocstyle`\] Allow overriding pydocstyle convention rules ([#8586](https://github.com/astral-sh/ruff/pull/8586))
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Avoid syntax error via importing `trio.lowlevel` ([#8730](https://github.com/astral-sh/ruff/pull/8730))
|
||||
- Omit unrolled augmented assignments in `PIE794` ([#8634](https://github.com/astral-sh/ruff/pull/8634))
|
||||
- Slice source code instead of generating it for `EM` fixes ([#7746](https://github.com/astral-sh/ruff/pull/7746))
|
||||
- Allow whitespace around colon in slices for `whitespace-before-punctuation` (`E203`) ([#8654](https://github.com/astral-sh/ruff/pull/8654))
|
||||
- Use function range for `no-self-use` ([#8637](https://github.com/astral-sh/ruff/pull/8637))
|
||||
- F-strings doesn't contain bytes literal for `PLW0129` ([#8675](https://github.com/astral-sh/ruff/pull/8675))
|
||||
- Improve detection of `TYPE_CHECKING` blocks imported from `typing_extensions` or `_typeshed` ([#8429](https://github.com/astral-sh/ruff/pull/8429))
|
||||
- Treat display as a builtin in IPython ([#8707](https://github.com/astral-sh/ruff/pull/8707))
|
||||
- Avoid `FURB113` autofix if comments are present ([#8494](https://github.com/astral-sh/ruff/pull/8494))
|
||||
- Consider the new f-string tokens for `flake8-commas` ([#8582](https://github.com/astral-sh/ruff/pull/8582))
|
||||
- Remove erroneous bad-dunder-name reference ([#8742](https://github.com/astral-sh/ruff/pull/8742))
|
||||
- Avoid recommending Self usages in metaclasses ([#8639](https://github.com/astral-sh/ruff/pull/8639))
|
||||
- Detect runtime-evaluated base classes defined in the current file ([#8572](https://github.com/astral-sh/ruff/pull/8572))
|
||||
- Avoid inserting trailing commas within f-strings ([#8574](https://github.com/astral-sh/ruff/pull/8574))
|
||||
- Remove incorrect deprecation label for stdout and stderr ([#8743](https://github.com/astral-sh/ruff/pull/8743))
|
||||
- Fix unnecessary parentheses in UP007 fix ([#8610](https://github.com/astral-sh/ruff/pull/8610))
|
||||
- Remove repeated and erroneous scoped settings headers in docs ([#8670](https://github.com/astral-sh/ruff/pull/8670))
|
||||
- Trim trailing empty strings when converting to f-strings ([#8712](https://github.com/astral-sh/ruff/pull/8712))
|
||||
- Fix ordering for `force-sort-within-sections` ([#8665](https://github.com/astral-sh/ruff/pull/8665))
|
||||
- Run unicode prefix rule over tokens ([#8709](https://github.com/astral-sh/ruff/pull/8709))
|
||||
- Update UP032 to unescape curly braces in literal parts of converted strings ([#8697](https://github.com/astral-sh/ruff/pull/8697))
|
||||
- List all ipython builtins ([#8719](https://github.com/astral-sh/ruff/pull/8719))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Document conventions in the FAQ ([#8638](https://github.com/astral-sh/ruff/pull/8638))
|
||||
- Redirect from rule codes to rule pages in docs ([#8636](https://github.com/astral-sh/ruff/pull/8636))
|
||||
- Fix permalink to convention setting ([#8575](https://github.com/astral-sh/ruff/pull/8575))
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Preview features
|
||||
|
||||
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -808,7 +808,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2062,7 +2062,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_cli"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.2",
|
||||
"anyhow",
|
||||
@@ -2198,7 +2198,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_linter"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"annotate-snippets 0.9.2",
|
||||
@@ -2449,7 +2449,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_shrinking"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
|
||||
32
Cargo.toml
32
Cargo.toml
@@ -55,6 +55,38 @@ unicode-width = { version = "0.1.11" }
|
||||
uuid = { version = "1.5.0", features = ["v4", "fast-rng", "macro-diagnostics", "js"] }
|
||||
wsl = { version = "0.1.0" }
|
||||
|
||||
[workspace.lints.rust]
|
||||
unsafe_code = "warn"
|
||||
unreachable_pub = "warn"
|
||||
|
||||
[workspace.lints.clippy]
|
||||
pedantic = { level = "warn", priority = -2 }
|
||||
# Allowed pedantic lints
|
||||
char_lit_as_u8 = "allow"
|
||||
collapsible_else_if = "allow"
|
||||
collapsible_if = "allow"
|
||||
implicit_hasher = "allow"
|
||||
match_same_arms = "allow"
|
||||
missing_errors_doc = "allow"
|
||||
missing_panics_doc = "allow"
|
||||
module_name_repetitions = "allow"
|
||||
must_use_candidate = "allow"
|
||||
similar_names = "allow"
|
||||
too_many_lines = "allow"
|
||||
# To allow `#[allow(clippy::all)]` in `crates/ruff_python_parser/src/python.rs`.
|
||||
needless_raw_string_hashes = "allow"
|
||||
# Disallowed restriction lints
|
||||
print_stdout = "warn"
|
||||
print_stderr = "warn"
|
||||
dbg_macro = "warn"
|
||||
empty_drop = "warn"
|
||||
empty_structs_with_brackets = "warn"
|
||||
exit = "warn"
|
||||
get_unwrap = "warn"
|
||||
rc_buffer = "warn"
|
||||
rc_mutex = "warn"
|
||||
rest_pat_in_fully_bound_structs = "warn"
|
||||
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
|
||||
@@ -150,7 +150,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.1.5
|
||||
rev: v0.1.6
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
@@ -428,6 +428,7 @@ Ruff is used by a number of major open-source projects and companies, including:
|
||||
- [Pydantic](https://github.com/pydantic/pydantic)
|
||||
- [Pylint](https://github.com/PyCQA/pylint)
|
||||
- [Reflex](https://github.com/reflex-dev/reflex)
|
||||
- [River](https://github.com/online-ml/river)
|
||||
- [Rippling](https://rippling.com)
|
||||
- [Robyn](https://github.com/sansyrox/robyn)
|
||||
- [Saleor](https://github.com/saleor/saleor)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
description = """
|
||||
Convert Flake8 configuration files to Ruff configuration files.
|
||||
"""
|
||||
@@ -34,3 +34,6 @@ toml = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.3.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -46,6 +46,9 @@ ruff_python_formatter = { path = "../ruff_python_formatter" }
|
||||
ruff_python_index = { path = "../ruff_python_index" }
|
||||
ruff_python_parser = { path = "../ruff_python_parser" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
codspeed = ["codspeed-criterion-compat"]
|
||||
|
||||
|
||||
@@ -20,3 +20,6 @@ seahash = "4.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
ruff_macros = { path = "../ruff_macros" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_cli"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -76,3 +76,6 @@ mimalloc = "0.1.39"
|
||||
|
||||
[target.'cfg(all(not(target_os = "windows"), not(target_os = "openbsd"), any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64")))'.dependencies]
|
||||
tikv-jemallocator = "0.5.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -202,12 +202,12 @@ fn lint_path(
|
||||
match result {
|
||||
Ok(inner) => inner,
|
||||
Err(error) => {
|
||||
let message = r#"This indicates a bug in Ruff. If you could open an issue at:
|
||||
let message = r"This indicates a bug in Ruff. If you could open an issue at:
|
||||
|
||||
https://github.com/astral-sh/ruff/issues/new?title=%5BLinter%20panic%5D
|
||||
|
||||
...with the relevant file contents, the `pyproject.toml` settings, and the following stack trace, we'd be very appreciative!
|
||||
"#;
|
||||
";
|
||||
|
||||
error!(
|
||||
"{}{}{} {message}\n{error}",
|
||||
|
||||
@@ -660,12 +660,12 @@ impl Display for FormatCommandError {
|
||||
}
|
||||
}
|
||||
Self::Panic(path, err) => {
|
||||
let message = r#"This indicates a bug in Ruff. If you could open an issue at:
|
||||
let message = r"This indicates a bug in Ruff. If you could open an issue at:
|
||||
|
||||
https://github.com/astral-sh/ruff/issues/new?title=%5BFormatter%20panic%5D
|
||||
|
||||
...with the relevant file contents, the `pyproject.toml` settings, and the following stack trace, we'd be very appreciative!
|
||||
"#;
|
||||
";
|
||||
if let Some(path) = path {
|
||||
write!(
|
||||
f,
|
||||
|
||||
@@ -63,7 +63,7 @@ fn format_rule_text(rule: Rule) -> String {
|
||||
|
||||
if rule.is_preview() || rule.is_nursery() {
|
||||
output.push_str(
|
||||
r#"This rule is in preview and is not stable. The `--preview` flag is required for use."#,
|
||||
r"This rule is in preview and is not stable. The `--preview` flag is required for use.",
|
||||
);
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
|
||||
@@ -43,7 +43,7 @@ pub fn resolve(
|
||||
{
|
||||
let settings = resolve_root_settings(&pyproject, Relativity::Cwd, overrides)?;
|
||||
debug!(
|
||||
"Using user specified pyproject.toml at {}",
|
||||
"Using user-specified configuration file at: {}",
|
||||
pyproject.display()
|
||||
);
|
||||
return Ok(PyprojectConfig::new(
|
||||
@@ -63,7 +63,10 @@ pub fn resolve(
|
||||
.as_ref()
|
||||
.unwrap_or(&path_dedot::CWD.as_path()),
|
||||
)? {
|
||||
debug!("Using pyproject.toml (parent) at {}", pyproject.display());
|
||||
debug!(
|
||||
"Using configuration file (via parent) at: {}",
|
||||
pyproject.display()
|
||||
);
|
||||
let settings = resolve_root_settings(&pyproject, Relativity::Parent, overrides)?;
|
||||
return Ok(PyprojectConfig::new(
|
||||
PyprojectDiscoveryStrategy::Hierarchical,
|
||||
@@ -77,7 +80,10 @@ pub fn resolve(
|
||||
// end up the "closest" `pyproject.toml` file for every Python file later on, so
|
||||
// these act as the "default" settings.)
|
||||
if let Some(pyproject) = pyproject::find_user_settings_toml() {
|
||||
debug!("Using pyproject.toml (cwd) at {}", pyproject.display());
|
||||
debug!(
|
||||
"Using configuration file (via cwd) at: {}",
|
||||
pyproject.display()
|
||||
);
|
||||
let settings = resolve_root_settings(&pyproject, Relativity::Cwd, overrides)?;
|
||||
return Ok(PyprojectConfig::new(
|
||||
PyprojectDiscoveryStrategy::Hierarchical,
|
||||
|
||||
@@ -395,9 +395,9 @@ fn deprecated_options() -> Result<()> {
|
||||
let ruff_toml = tempdir.path().join("ruff.toml");
|
||||
fs::write(
|
||||
&ruff_toml,
|
||||
r#"
|
||||
r"
|
||||
tab-size = 2
|
||||
"#,
|
||||
",
|
||||
)?;
|
||||
|
||||
insta::with_settings!({filters => vec![
|
||||
@@ -407,10 +407,10 @@ tab-size = 2
|
||||
.args(["format", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.pass_stdin(r#"
|
||||
.pass_stdin(r"
|
||||
if True:
|
||||
pass
|
||||
"#), @r###"
|
||||
"), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -443,9 +443,9 @@ format = "json"
|
||||
.args(["check", "--select", "F401", "--no-cache", "--config"])
|
||||
.arg(&ruff_toml)
|
||||
.arg("-")
|
||||
.pass_stdin(r#"
|
||||
.pass_stdin(r"
|
||||
import os
|
||||
"#), @r###"
|
||||
"), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
@@ -48,9 +48,12 @@ tracing-indicatif = { workspace = true }
|
||||
tracing-subscriber = { workspace = true, features = ["env-filter"] }
|
||||
imara-diff = "0.1.5"
|
||||
|
||||
[dev-dependencies]
|
||||
indoc = "2.0.4"
|
||||
|
||||
[features]
|
||||
# Turn off rayon for profiling
|
||||
singlethreaded = []
|
||||
|
||||
[dev-dependencies]
|
||||
indoc = "2.0.4"
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -49,7 +49,7 @@ pub(crate) fn main(args: &Args) -> Result<()> {
|
||||
|
||||
if rule.is_preview() || rule.is_nursery() {
|
||||
output.push_str(
|
||||
r#"This rule is unstable and in [preview](../preview.md). The `--preview` flag is required for use."#,
|
||||
r"This rule is unstable and in [preview](../preview.md). The `--preview` flag is required for use.",
|
||||
);
|
||||
output.push('\n');
|
||||
output.push('\n');
|
||||
|
||||
@@ -29,3 +29,6 @@ insta = { workspace = true }
|
||||
[features]
|
||||
serde = ["dep:serde", "ruff_text_size/serde"]
|
||||
schemars = ["dep:schemars", "ruff_text_size/schemars"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1711,14 +1711,14 @@ mod tests {
|
||||
));
|
||||
|
||||
assert_eq!(
|
||||
r#"a
|
||||
"a
|
||||
b
|
||||
c
|
||||
d
|
||||
d
|
||||
c
|
||||
b
|
||||
a"#,
|
||||
a",
|
||||
formatted.as_code()
|
||||
);
|
||||
}
|
||||
@@ -2047,10 +2047,10 @@ two lines`,
|
||||
|
||||
assert_eq!(
|
||||
printed.as_code(),
|
||||
r#"Group with id-2
|
||||
"Group with id-2
|
||||
Group with id-1 does not fit on the line because it exceeds the line width of 80 characters by
|
||||
Group 2 fits
|
||||
Group 1 breaks"#
|
||||
Group 1 breaks"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,3 +17,6 @@ ruff_macros = { path = "../ruff_macros" }
|
||||
|
||||
[dev-dependencies]
|
||||
static_assertions = "1.1.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_linter"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
@@ -86,3 +86,6 @@ default = []
|
||||
schemars = ["dep:schemars"]
|
||||
# Enables the UnreachableCode rule
|
||||
unreachable-code = []
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# fixtures
|
||||
|
||||
Fixture files used for snapshot testing.
|
||||
@@ -1,9 +1,21 @@
|
||||
{"foo": 1, **{"bar": 1}} # PIE800
|
||||
|
||||
{**{"bar": 10}, "a": "b"} # PIE800
|
||||
|
||||
foo({**foo, **{"bar": True}}) # PIE800
|
||||
|
||||
{**foo, **{"bar": 10}} # PIE800
|
||||
|
||||
{ # PIE800
|
||||
"a": "b",
|
||||
# Preserve
|
||||
**{
|
||||
# all
|
||||
"bar": 10, # the
|
||||
# comments
|
||||
},
|
||||
}
|
||||
|
||||
{**foo, **buzz, **{bar: 10}} # PIE800
|
||||
|
||||
{**foo, "bar": True } # OK
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import trio
|
||||
from trio import sleep
|
||||
|
||||
|
||||
async def func():
|
||||
import trio
|
||||
from trio import sleep
|
||||
|
||||
await trio.sleep(0) # TRIO115
|
||||
await trio.sleep(1) # OK
|
||||
await trio.sleep(0, 1) # OK
|
||||
@@ -21,8 +20,11 @@ async def func():
|
||||
trio.sleep(bar)
|
||||
|
||||
|
||||
trio.sleep(0) # TRIO115
|
||||
|
||||
|
||||
def func():
|
||||
trio.run(trio.sleep(0)) # TRIO115
|
||||
|
||||
|
||||
from trio import Event, sleep
|
||||
|
||||
def func():
|
||||
sleep(0) # TRIO115
|
||||
|
||||
@@ -663,3 +663,55 @@ class CommentAfterDocstring:
|
||||
def newline_after_closing_quote(self):
|
||||
"We enforce a newline after the closing quote for a multi-line docstring \
|
||||
but continuations shouldn't be considered multi-line"
|
||||
|
||||
|
||||
|
||||
|
||||
def retain_extra_whitespace():
|
||||
"""Summary.
|
||||
|
||||
This is overindented
|
||||
And so is this, but it we should preserve the extra space on this line relative
|
||||
to the one before
|
||||
"""
|
||||
|
||||
|
||||
def retain_extra_whitespace_multiple():
|
||||
"""Summary.
|
||||
|
||||
This is overindented
|
||||
And so is this, but it we should preserve the extra space on this line relative
|
||||
to the one before
|
||||
This is also overindented
|
||||
And so is this, but it we should preserve the extra space on this line relative
|
||||
to the one before
|
||||
"""
|
||||
|
||||
|
||||
|
||||
def retain_extra_whitespace_deeper():
|
||||
"""Summary.
|
||||
|
||||
This is overindented
|
||||
And so is this, but it we should preserve the extra space on this line relative
|
||||
to the one before
|
||||
And the relative indent here should be preserved too
|
||||
"""
|
||||
|
||||
def retain_extra_whitespace_followed_by_same_offset():
|
||||
"""Summary.
|
||||
|
||||
This is overindented
|
||||
And so is this, but it we should preserve the extra space on this line relative
|
||||
This is overindented
|
||||
This is overindented
|
||||
"""
|
||||
|
||||
|
||||
def retain_extra_whitespace_not_overindented():
|
||||
"""Summary.
|
||||
|
||||
This is not overindented
|
||||
This is overindented, but since one line is not overindented this should not raise
|
||||
And so is this, but it we should preserve the extra space on this line relative
|
||||
"""
|
||||
|
||||
4
crates/ruff_linter/resources/test/fixtures/pyflakes/F821_21.py
vendored
Normal file
4
crates/ruff_linter/resources/test/fixtures/pyflakes/F821_21.py
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
"""Test for IPython-only builtins."""
|
||||
|
||||
x = 1
|
||||
display(x)
|
||||
74
crates/ruff_linter/resources/test/fixtures/pyflakes/F821_22.ipynb
vendored
Normal file
74
crates/ruff_linter/resources/test/fixtures/pyflakes/F821_22.ipynb
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "af8fee97-f9aa-47c2-b34c-b109a5f083d6",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "acd5eb1e-6991-42b8-806f-20d17d7e571f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"display(1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f8f3f599-030e-48b3-bcd3-31d97f725c68",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
extend-exclude = [
|
||||
"excluded_file.py",
|
||||
"migrations",
|
||||
"with_excluded_file/other_excluded_file.py",
|
||||
]
|
||||
|
||||
[tool.ruff.lint]
|
||||
per-file-ignores = { "__init__.py" = ["F401"] }
|
||||
@@ -25,3 +25,6 @@ u = u
|
||||
|
||||
def hello():
|
||||
return"Hello"
|
||||
|
||||
f"foo"u"bar"
|
||||
f"foo" u"bar"
|
||||
|
||||
@@ -207,3 +207,39 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
# The fixed string will exceed the line length, but it's still smaller than the
|
||||
# existing line length, so it's fine.
|
||||
"<Customer: {}, {}, {}, {}, {}>".format(self.internal_ids, self.external_ids, self.properties, self.tags, self.others)
|
||||
|
||||
# When fixing, trim the trailing empty string.
|
||||
raise ValueError("Conflicting configuration dicts: {!r} {!r}"
|
||||
"".format(new_dict, d))
|
||||
|
||||
# When fixing, trim the trailing empty string.
|
||||
raise ValueError("Conflicting configuration dicts: {!r} {!r}"
|
||||
.format(new_dict, d))
|
||||
|
||||
raise ValueError(
|
||||
"Conflicting configuration dicts: {!r} {!r}"
|
||||
"".format(new_dict, d)
|
||||
)
|
||||
|
||||
raise ValueError(
|
||||
"Conflicting configuration dicts: {!r} {!r}"
|
||||
"".format(new_dict, d)
|
||||
|
||||
)
|
||||
|
||||
# The first string will be converted to an f-string and the curly braces in the second should be converted to be unescaped
|
||||
(
|
||||
"{}"
|
||||
"{{}}"
|
||||
).format(a)
|
||||
|
||||
("{}" "{{}}").format(a)
|
||||
|
||||
|
||||
# Both strings will be converted to an f-string and the curly braces in the second should left escaped
|
||||
(
|
||||
"{}"
|
||||
"{{{}}}"
|
||||
).format(a, b)
|
||||
|
||||
("{}" "{{{}}}").format(a, b)
|
||||
|
||||
25
crates/ruff_linter/resources/test/fixtures/refurb/FURB136.py
vendored
Normal file
25
crates/ruff_linter/resources/test/fixtures/refurb/FURB136.py
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
x = 1
|
||||
y = 2
|
||||
|
||||
x if x > y else y # FURB136
|
||||
|
||||
x if x >= y else y # FURB136
|
||||
|
||||
x if x < y else y # FURB136
|
||||
|
||||
x if x <= y else y # FURB136
|
||||
|
||||
y if x > y else x # FURB136
|
||||
|
||||
y if x >= y else x # FURB136
|
||||
|
||||
y if x < y else x # FURB136
|
||||
|
||||
y if x <= y else x # FURB136
|
||||
|
||||
x + y if x > y else y # OK
|
||||
|
||||
x if (
|
||||
x
|
||||
> y
|
||||
) else y # FURB136
|
||||
7
crates/ruff_linter/resources/test/fixtures/refurb/FURB152.py
vendored
Normal file
7
crates/ruff_linter/resources/test/fixtures/refurb/FURB152.py
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
r = 3.1 # OK
|
||||
|
||||
A = 3.14 * r ** 2 # FURB152
|
||||
|
||||
C = 6.28 * r # FURB152
|
||||
|
||||
e = 2.71 # FURB152
|
||||
@@ -936,13 +936,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
flake8_trio::rules::zero_sleep_call(checker, call);
|
||||
}
|
||||
}
|
||||
Expr::Dict(
|
||||
dict @ ast::ExprDict {
|
||||
keys,
|
||||
values,
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
Expr::Dict(dict) => {
|
||||
if checker.any_enabled(&[
|
||||
Rule::MultiValueRepeatedKeyLiteral,
|
||||
Rule::MultiValueRepeatedKeyVariable,
|
||||
@@ -950,7 +944,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
pyflakes::rules::repeated_keys(checker, dict);
|
||||
}
|
||||
if checker.enabled(Rule::UnnecessarySpread) {
|
||||
flake8_pie::rules::unnecessary_spread(checker, keys, values);
|
||||
flake8_pie::rules::unnecessary_spread(checker, dict);
|
||||
}
|
||||
}
|
||||
Expr::Set(ast::ExprSet { elts, range: _ }) => {
|
||||
@@ -1251,10 +1245,13 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
refurb::rules::single_item_membership_test(checker, expr, left, ops, comparators);
|
||||
}
|
||||
}
|
||||
Expr::NumberLiteral(_) => {
|
||||
Expr::NumberLiteral(number_literal @ ast::ExprNumberLiteral { .. }) => {
|
||||
if checker.source_type.is_stub() && checker.enabled(Rule::NumericLiteralTooLong) {
|
||||
flake8_pyi::rules::numeric_literal_too_long(checker, expr);
|
||||
}
|
||||
if checker.enabled(Rule::MathConstant) {
|
||||
refurb::rules::math_constant(checker, number_literal);
|
||||
}
|
||||
}
|
||||
Expr::BytesLiteral(_) => {
|
||||
if checker.source_type.is_stub() && checker.enabled(Rule::StringOrBytesTooLong) {
|
||||
@@ -1272,21 +1269,20 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::HardcodedTempFile) {
|
||||
flake8_bandit::rules::hardcoded_tmp_directory(checker, string);
|
||||
}
|
||||
if checker.enabled(Rule::UnicodeKindPrefix) {
|
||||
pyupgrade::rules::unicode_kind_prefix(checker, string);
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.enabled(Rule::StringOrBytesTooLong) {
|
||||
flake8_pyi::rules::string_or_bytes_too_long(checker, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::IfExp(ast::ExprIfExp {
|
||||
test,
|
||||
body,
|
||||
orelse,
|
||||
range: _,
|
||||
}) => {
|
||||
Expr::IfExp(
|
||||
if_exp @ ast::ExprIfExp {
|
||||
test,
|
||||
body,
|
||||
orelse,
|
||||
range: _,
|
||||
},
|
||||
) => {
|
||||
if checker.enabled(Rule::IfElseBlockInsteadOfDictGet) {
|
||||
flake8_simplify::rules::if_exp_instead_of_dict_get(
|
||||
checker, expr, test, body, orelse,
|
||||
@@ -1301,6 +1297,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||
if checker.enabled(Rule::IfExprWithTwistedArms) {
|
||||
flake8_simplify::rules::twisted_arms_in_ifexpr(checker, expr, test, body, orelse);
|
||||
}
|
||||
if checker.enabled(Rule::IfExprMinMax) {
|
||||
refurb::rules::if_expr_min_max(checker, if_exp);
|
||||
}
|
||||
}
|
||||
Expr::ListComp(
|
||||
comp @ ast::ExprListComp {
|
||||
|
||||
@@ -54,7 +54,7 @@ use ruff_python_semantic::{
|
||||
ModuleKind, NodeId, ScopeId, ScopeKind, SemanticModel, SemanticModelFlags, Snapshot,
|
||||
StarImport, SubmoduleImport,
|
||||
};
|
||||
use ruff_python_stdlib::builtins::{BUILTINS, MAGIC_GLOBALS};
|
||||
use ruff_python_stdlib::builtins::{IPYTHON_BUILTINS, MAGIC_GLOBALS, PYTHON_BUILTINS};
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::checkers::ast::deferred::Deferred;
|
||||
@@ -1592,9 +1592,16 @@ impl<'a> Checker<'a> {
|
||||
}
|
||||
|
||||
fn bind_builtins(&mut self) {
|
||||
for builtin in BUILTINS
|
||||
for builtin in PYTHON_BUILTINS
|
||||
.iter()
|
||||
.chain(MAGIC_GLOBALS.iter())
|
||||
.chain(
|
||||
self.source_type
|
||||
.is_ipynb()
|
||||
.then_some(IPYTHON_BUILTINS)
|
||||
.into_iter()
|
||||
.flatten(),
|
||||
)
|
||||
.copied()
|
||||
.chain(self.settings.builtins.iter().map(String::as_str))
|
||||
{
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use std::path::Path;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_source_file::Locator;
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::rules::flake8_no_pep420::rules::implicit_namespace_package;
|
||||
@@ -10,15 +12,22 @@ use crate::settings::LinterSettings;
|
||||
pub(crate) fn check_file_path(
|
||||
path: &Path,
|
||||
package: Option<&Path>,
|
||||
locator: &Locator,
|
||||
indexer: &Indexer,
|
||||
settings: &LinterSettings,
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
|
||||
// flake8-no-pep420
|
||||
if settings.rules.enabled(Rule::ImplicitNamespacePackage) {
|
||||
if let Some(diagnostic) =
|
||||
implicit_namespace_package(path, package, &settings.project_root, &settings.src)
|
||||
{
|
||||
if let Some(diagnostic) = implicit_namespace_package(
|
||||
path,
|
||||
package,
|
||||
locator,
|
||||
indexer,
|
||||
&settings.project_root,
|
||||
&settings.src,
|
||||
) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,10 @@ pub(crate) fn check_tokens(
|
||||
pycodestyle::rules::tab_indentation(&mut diagnostics, tokens, locator, indexer);
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::UnicodeKindPrefix) {
|
||||
pyupgrade::rules::unicode_kind_prefix(&mut diagnostics, tokens);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
Rule::InvalidCharacterBackspace,
|
||||
Rule::InvalidCharacterSub,
|
||||
@@ -163,7 +167,7 @@ pub(crate) fn check_tokens(
|
||||
Rule::ShebangNotFirstLine,
|
||||
Rule::ShebangMissingPython,
|
||||
]) {
|
||||
flake8_executable::rules::from_tokens(tokens, path, locator, &mut diagnostics);
|
||||
flake8_executable::rules::from_tokens(&mut diagnostics, path, locator, indexer);
|
||||
}
|
||||
|
||||
if settings.rules.any_enabled(&[
|
||||
|
||||
@@ -947,9 +947,11 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||
(Refurb, "131") => (RuleGroup::Nursery, rules::refurb::rules::DeleteFullSlice),
|
||||
#[allow(deprecated)]
|
||||
(Refurb, "132") => (RuleGroup::Nursery, rules::refurb::rules::CheckAndRemoveFromSet),
|
||||
(Refurb, "136") => (RuleGroup::Preview, rules::refurb::rules::IfExprMinMax),
|
||||
(Refurb, "140") => (RuleGroup::Preview, rules::refurb::rules::ReimplementedStarmap),
|
||||
(Refurb, "145") => (RuleGroup::Preview, rules::refurb::rules::SliceCopy),
|
||||
(Refurb, "148") => (RuleGroup::Preview, rules::refurb::rules::UnnecessaryEnumerate),
|
||||
(Refurb, "152") => (RuleGroup::Preview, rules::refurb::rules::MathConstant),
|
||||
(Refurb, "168") => (RuleGroup::Preview, rules::refurb::rules::IsinstanceTypeNone),
|
||||
(Refurb, "169") => (RuleGroup::Preview, rules::refurb::rules::TypeNoneComparison),
|
||||
(Refurb, "171") => (RuleGroup::Preview, rules::refurb::rules::SingleItemMembershipTest),
|
||||
|
||||
@@ -171,7 +171,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn empty_file() {
|
||||
let locator = Locator::new(r#""#);
|
||||
let locator = Locator::new(r"");
|
||||
let diagnostics = create_diagnostics([]);
|
||||
let FixResult {
|
||||
code,
|
||||
@@ -225,10 +225,10 @@ print("hello world")
|
||||
#[test]
|
||||
fn apply_one_replacement() {
|
||||
let locator = Locator::new(
|
||||
r#"
|
||||
r"
|
||||
class A(object):
|
||||
...
|
||||
"#
|
||||
"
|
||||
.trim(),
|
||||
);
|
||||
let diagnostics = create_diagnostics([Edit::replacement(
|
||||
@@ -243,10 +243,10 @@ class A(object):
|
||||
} = apply_fixes(diagnostics.iter(), &locator);
|
||||
assert_eq!(
|
||||
code,
|
||||
r#"
|
||||
r"
|
||||
class A(Bar):
|
||||
...
|
||||
"#
|
||||
"
|
||||
.trim(),
|
||||
);
|
||||
assert_eq!(fixes.values().sum::<usize>(), 1);
|
||||
@@ -262,10 +262,10 @@ class A(Bar):
|
||||
#[test]
|
||||
fn apply_one_removal() {
|
||||
let locator = Locator::new(
|
||||
r#"
|
||||
r"
|
||||
class A(object):
|
||||
...
|
||||
"#
|
||||
"
|
||||
.trim(),
|
||||
);
|
||||
let diagnostics = create_diagnostics([Edit::deletion(TextSize::new(7), TextSize::new(15))]);
|
||||
@@ -276,10 +276,10 @@ class A(object):
|
||||
} = apply_fixes(diagnostics.iter(), &locator);
|
||||
assert_eq!(
|
||||
code,
|
||||
r#"
|
||||
r"
|
||||
class A:
|
||||
...
|
||||
"#
|
||||
"
|
||||
.trim()
|
||||
);
|
||||
assert_eq!(fixes.values().sum::<usize>(), 1);
|
||||
@@ -295,10 +295,10 @@ class A:
|
||||
#[test]
|
||||
fn apply_two_removals() {
|
||||
let locator = Locator::new(
|
||||
r#"
|
||||
r"
|
||||
class A(object, object, object):
|
||||
...
|
||||
"#
|
||||
"
|
||||
.trim(),
|
||||
);
|
||||
let diagnostics = create_diagnostics([
|
||||
@@ -313,10 +313,10 @@ class A(object, object, object):
|
||||
|
||||
assert_eq!(
|
||||
code,
|
||||
r#"
|
||||
r"
|
||||
class A(object):
|
||||
...
|
||||
"#
|
||||
"
|
||||
.trim()
|
||||
);
|
||||
assert_eq!(fixes.values().sum::<usize>(), 2);
|
||||
@@ -334,10 +334,10 @@ class A(object):
|
||||
#[test]
|
||||
fn ignore_overlapping_fixes() {
|
||||
let locator = Locator::new(
|
||||
r#"
|
||||
r"
|
||||
class A(object):
|
||||
...
|
||||
"#
|
||||
"
|
||||
.trim(),
|
||||
);
|
||||
let diagnostics = create_diagnostics([
|
||||
@@ -351,10 +351,10 @@ class A(object):
|
||||
} = apply_fixes(diagnostics.iter(), &locator);
|
||||
assert_eq!(
|
||||
code,
|
||||
r#"
|
||||
r"
|
||||
class A:
|
||||
...
|
||||
"#
|
||||
"
|
||||
.trim(),
|
||||
);
|
||||
assert_eq!(fixes.values().sum::<usize>(), 1);
|
||||
|
||||
@@ -373,18 +373,18 @@ mod tests {
|
||||
Insertion::own_line("", TextSize::from(40), "\n")
|
||||
);
|
||||
|
||||
let contents = r#"
|
||||
let contents = r"
|
||||
x = 1
|
||||
"#
|
||||
"
|
||||
.trim_start();
|
||||
assert_eq!(
|
||||
insert(contents)?,
|
||||
Insertion::own_line("", TextSize::from(0), "\n")
|
||||
);
|
||||
|
||||
let contents = r#"
|
||||
let contents = r"
|
||||
#!/usr/bin/env python3
|
||||
"#
|
||||
"
|
||||
.trim_start();
|
||||
assert_eq!(
|
||||
insert(contents)?,
|
||||
@@ -457,10 +457,10 @@ x = 1
|
||||
Insertion::inline("", TextSize::from(9), "; ")
|
||||
);
|
||||
|
||||
let contents = r#"
|
||||
let contents = r"
|
||||
if True:
|
||||
pass
|
||||
"#
|
||||
"
|
||||
.trim_start();
|
||||
assert_eq!(
|
||||
insert(contents, TextSize::from(0)),
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::error::Error;
|
||||
|
||||
use anyhow::Result;
|
||||
use libcst_native::{ImportAlias, Name, NameOrAttribute};
|
||||
use ruff_python_ast::{self as ast, PySourceType, Stmt, Suite};
|
||||
use ruff_python_ast::{self as ast, PySourceType, Stmt};
|
||||
use ruff_text_size::{Ranged, TextSize};
|
||||
|
||||
use ruff_diagnostics::Edit;
|
||||
@@ -26,7 +26,7 @@ mod insertion;
|
||||
|
||||
pub(crate) struct Importer<'a> {
|
||||
/// The Python AST to which we are adding imports.
|
||||
python_ast: &'a Suite,
|
||||
python_ast: &'a [Stmt],
|
||||
/// The [`Locator`] for the Python AST.
|
||||
locator: &'a Locator<'a>,
|
||||
/// The [`Stylist`] for the Python AST.
|
||||
@@ -39,7 +39,7 @@ pub(crate) struct Importer<'a> {
|
||||
|
||||
impl<'a> Importer<'a> {
|
||||
pub(crate) fn new(
|
||||
python_ast: &'a Suite,
|
||||
python_ast: &'a [Stmt],
|
||||
locator: &'a Locator<'a>,
|
||||
stylist: &'a Stylist<'a>,
|
||||
) -> Self {
|
||||
|
||||
@@ -117,7 +117,7 @@ pub fn check_path(
|
||||
.iter_enabled()
|
||||
.any(|rule_code| rule_code.lint_source().is_filesystem())
|
||||
{
|
||||
diagnostics.extend(check_file_path(path, package, settings));
|
||||
diagnostics.extend(check_file_path(path, package, locator, indexer, settings));
|
||||
}
|
||||
|
||||
// Run the logical line-based rules.
|
||||
@@ -639,7 +639,7 @@ mod tests {
|
||||
|
||||
use crate::registry::Rule;
|
||||
use crate::source_kind::SourceKind;
|
||||
use crate::test::{test_contents, test_notebook_path, TestedNotebook};
|
||||
use crate::test::{assert_notebook_path, test_contents, TestedNotebook};
|
||||
use crate::{assert_messages, settings};
|
||||
|
||||
/// Construct a path to a Jupyter notebook in the `resources/test/fixtures/jupyter` directory.
|
||||
@@ -655,7 +655,7 @@ mod tests {
|
||||
messages,
|
||||
source_notebook,
|
||||
..
|
||||
} = test_notebook_path(
|
||||
} = assert_notebook_path(
|
||||
&actual,
|
||||
expected,
|
||||
&settings::LinterSettings::for_rule(Rule::UnsortedImports),
|
||||
@@ -672,7 +672,7 @@ mod tests {
|
||||
messages,
|
||||
source_notebook,
|
||||
..
|
||||
} = test_notebook_path(
|
||||
} = assert_notebook_path(
|
||||
&actual,
|
||||
expected,
|
||||
&settings::LinterSettings::for_rule(Rule::UnusedImport),
|
||||
@@ -689,7 +689,7 @@ mod tests {
|
||||
messages,
|
||||
source_notebook,
|
||||
..
|
||||
} = test_notebook_path(
|
||||
} = assert_notebook_path(
|
||||
&actual,
|
||||
expected,
|
||||
&settings::LinterSettings::for_rule(Rule::UnusedVariable),
|
||||
@@ -706,7 +706,7 @@ mod tests {
|
||||
let TestedNotebook {
|
||||
linted_notebook: fixed_notebook,
|
||||
..
|
||||
} = test_notebook_path(
|
||||
} = assert_notebook_path(
|
||||
actual_path,
|
||||
&expected_path,
|
||||
&settings::LinterSettings::for_rule(Rule::UnusedImport),
|
||||
|
||||
@@ -199,7 +199,7 @@ def fibonacci(n):
|
||||
TextSize::from(99),
|
||||
)));
|
||||
|
||||
let file_2 = r#"if a == 1: pass"#;
|
||||
let file_2 = r"if a == 1: pass";
|
||||
|
||||
let undefined_name = Diagnostic::new(
|
||||
DiagnosticKind {
|
||||
|
||||
@@ -297,6 +297,7 @@ impl Rule {
|
||||
| Rule::TabIndentation
|
||||
| Rule::TrailingCommaOnBareTuple
|
||||
| Rule::TypeCommentInStub
|
||||
| Rule::UnicodeKindPrefix
|
||||
| Rule::UselessSemicolon
|
||||
| Rule::UTF8EncodingDeclaration => LintSource::Tokens,
|
||||
Rule::IOError => LintSource::Io,
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
use ruff_python_stdlib::builtins::is_builtin;
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_python_stdlib::builtins::{is_ipython_builtin, is_python_builtin};
|
||||
|
||||
pub(super) fn shadows_builtin(name: &str, ignorelist: &[String]) -> bool {
|
||||
is_builtin(name) && ignorelist.iter().all(|ignore| ignore != name)
|
||||
pub(super) fn shadows_builtin(
|
||||
name: &str,
|
||||
ignorelist: &[String],
|
||||
source_type: PySourceType,
|
||||
) -> bool {
|
||||
if is_python_builtin(name) || source_type.is_ipynb() && is_ipython_builtin(name) {
|
||||
ignorelist.iter().all(|ignore| ignore != name)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ pub(crate) fn builtin_argument_shadowing(checker: &mut Checker, parameter: &Para
|
||||
if shadows_builtin(
|
||||
parameter.name.as_str(),
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.source_type,
|
||||
) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
BuiltinArgumentShadowing {
|
||||
|
||||
@@ -74,7 +74,11 @@ pub(crate) fn builtin_attribute_shadowing(
|
||||
name: &str,
|
||||
range: TextRange,
|
||||
) {
|
||||
if shadows_builtin(name, &checker.settings.flake8_builtins.builtins_ignorelist) {
|
||||
if shadows_builtin(
|
||||
name,
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.source_type,
|
||||
) {
|
||||
// Ignore shadowing within `TypedDict` definitions, since these are only accessible through
|
||||
// subscripting and not through attribute access.
|
||||
if class_def
|
||||
@@ -102,7 +106,11 @@ pub(crate) fn builtin_method_shadowing(
|
||||
decorator_list: &[Decorator],
|
||||
range: TextRange,
|
||||
) {
|
||||
if shadows_builtin(name, &checker.settings.flake8_builtins.builtins_ignorelist) {
|
||||
if shadows_builtin(
|
||||
name,
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.source_type,
|
||||
) {
|
||||
// Ignore some standard-library methods. Ideally, we'd ignore all overridden methods, since
|
||||
// those should be flagged on the superclass, but that's more difficult.
|
||||
if is_standard_library_override(name, class_def, checker.semantic()) {
|
||||
|
||||
@@ -60,7 +60,11 @@ impl Violation for BuiltinVariableShadowing {
|
||||
|
||||
/// A001
|
||||
pub(crate) fn builtin_variable_shadowing(checker: &mut Checker, name: &str, range: TextRange) {
|
||||
if shadows_builtin(name, &checker.settings.flake8_builtins.builtins_ignorelist) {
|
||||
if shadows_builtin(
|
||||
name,
|
||||
&checker.settings.flake8_builtins.builtins_ignorelist,
|
||||
checker.source_type,
|
||||
) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
BuiltinVariableShadowing {
|
||||
name: name.to_string(),
|
||||
|
||||
@@ -12,11 +12,11 @@ mod tests {
|
||||
#[test]
|
||||
fn notice() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
r"
|
||||
# Copyright 2023
|
||||
|
||||
import os
|
||||
"#
|
||||
"
|
||||
.trim(),
|
||||
&settings::LinterSettings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
@@ -26,11 +26,11 @@ import os
|
||||
#[test]
|
||||
fn notice_with_c() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
r"
|
||||
# Copyright (C) 2023
|
||||
|
||||
import os
|
||||
"#
|
||||
"
|
||||
.trim(),
|
||||
&settings::LinterSettings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
@@ -40,11 +40,11 @@ import os
|
||||
#[test]
|
||||
fn notice_with_caps() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
r"
|
||||
# COPYRIGHT (C) 2023
|
||||
|
||||
import os
|
||||
"#
|
||||
"
|
||||
.trim(),
|
||||
&settings::LinterSettings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
@@ -54,11 +54,11 @@ import os
|
||||
#[test]
|
||||
fn notice_with_range() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
r"
|
||||
# Copyright (C) 2021-2023
|
||||
|
||||
import os
|
||||
"#
|
||||
"
|
||||
.trim(),
|
||||
&settings::LinterSettings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
@@ -68,11 +68,11 @@ import os
|
||||
#[test]
|
||||
fn valid_author() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
r"
|
||||
# Copyright (C) 2023 Ruff
|
||||
|
||||
import os
|
||||
"#
|
||||
"
|
||||
.trim(),
|
||||
&settings::LinterSettings {
|
||||
flake8_copyright: super::settings::Settings {
|
||||
@@ -88,11 +88,11 @@ import os
|
||||
#[test]
|
||||
fn invalid_author() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
r"
|
||||
# Copyright (C) 2023 Some Author
|
||||
|
||||
import os
|
||||
"#
|
||||
"
|
||||
.trim(),
|
||||
&settings::LinterSettings {
|
||||
flake8_copyright: super::settings::Settings {
|
||||
@@ -108,9 +108,9 @@ import os
|
||||
#[test]
|
||||
fn small_file() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
r"
|
||||
import os
|
||||
"#
|
||||
"
|
||||
.trim(),
|
||||
&settings::LinterSettings {
|
||||
flake8_copyright: super::settings::Settings {
|
||||
@@ -126,7 +126,7 @@ import os
|
||||
#[test]
|
||||
fn late_notice() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"
|
||||
r"
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
@@ -149,7 +149,7 @@ import os
|
||||
# Content Content Content Content Content Content Content Content Content Content
|
||||
|
||||
# Copyright 2023
|
||||
"#
|
||||
"
|
||||
.trim(),
|
||||
&settings::LinterSettings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
@@ -159,8 +159,8 @@ import os
|
||||
#[test]
|
||||
fn char_boundary() {
|
||||
let diagnostics = test_snippet(
|
||||
r#"কককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককক
|
||||
"#
|
||||
r"কককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককককক
|
||||
"
|
||||
.trim(),
|
||||
&settings::LinterSettings::for_rules(vec![Rule::MissingCopyrightNotice]),
|
||||
);
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use std::path::Path;
|
||||
|
||||
use ruff_python_parser::lexer::LexResult;
|
||||
use ruff_python_parser::Tok;
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_source_file::Locator;
|
||||
pub(crate) use shebang_leading_whitespace::*;
|
||||
pub(crate) use shebang_missing_executable_file::*;
|
||||
@@ -20,32 +18,31 @@ mod shebang_not_executable;
|
||||
mod shebang_not_first_line;
|
||||
|
||||
pub(crate) fn from_tokens(
|
||||
tokens: &[LexResult],
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
path: &Path,
|
||||
locator: &Locator,
|
||||
diagnostics: &mut Vec<Diagnostic>,
|
||||
indexer: &Indexer,
|
||||
) {
|
||||
let mut has_any_shebang = false;
|
||||
for (tok, range) in tokens.iter().flatten() {
|
||||
if let Tok::Comment(comment) = tok {
|
||||
if let Some(shebang) = ShebangDirective::try_extract(comment) {
|
||||
has_any_shebang = true;
|
||||
for range in indexer.comment_ranges() {
|
||||
let comment = locator.slice(*range);
|
||||
if let Some(shebang) = ShebangDirective::try_extract(comment) {
|
||||
has_any_shebang = true;
|
||||
|
||||
if let Some(diagnostic) = shebang_missing_python(*range, &shebang) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
if let Some(diagnostic) = shebang_missing_python(*range, &shebang) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
if let Some(diagnostic) = shebang_not_executable(path, *range) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
if let Some(diagnostic) = shebang_not_executable(path, *range) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
if let Some(diagnostic) = shebang_leading_whitespace(*range, locator) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
if let Some(diagnostic) = shebang_leading_whitespace(*range, locator) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
if let Some(diagnostic) = shebang_not_first_line(*range, locator) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
if let Some(diagnostic) = shebang_not_first_line(*range, locator) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use ruff_python_ast::Expr;
|
||||
use std::fmt;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_text_size::Ranged;
|
||||
use ruff_python_ast::imports::{AnyImport, ImportFrom};
|
||||
use ruff_python_ast::Expr;
|
||||
use ruff_text_size::{Ranged, TextSize};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::importer::Importer;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of PEP 585- and PEP 604-style type annotations in Python
|
||||
@@ -42,6 +44,10 @@ use crate::checkers::ast::Checker;
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
/// This rule's fix is marked as unsafe, as adding `from __future__ import annotations`
|
||||
/// may change the semantics of the program.
|
||||
///
|
||||
/// ## Options
|
||||
/// - `target-version`
|
||||
#[violation]
|
||||
@@ -66,18 +72,28 @@ impl fmt::Display for Reason {
|
||||
}
|
||||
}
|
||||
|
||||
impl Violation for FutureRequiredTypeAnnotation {
|
||||
impl AlwaysFixableViolation for FutureRequiredTypeAnnotation {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let FutureRequiredTypeAnnotation { reason } = self;
|
||||
format!("Missing `from __future__ import annotations`, but uses {reason}")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> String {
|
||||
format!("Add `from __future__ import annotations`")
|
||||
}
|
||||
}
|
||||
|
||||
/// FA102
|
||||
pub(crate) fn future_required_type_annotation(checker: &mut Checker, expr: &Expr, reason: Reason) {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
FutureRequiredTypeAnnotation { reason },
|
||||
expr.range(),
|
||||
));
|
||||
let mut diagnostic = Diagnostic::new(FutureRequiredTypeAnnotation { reason }, expr.range());
|
||||
if let Some(python_ast) = checker.semantic().definitions.python_ast() {
|
||||
let required_import =
|
||||
AnyImport::ImportFrom(ImportFrom::member("__future__", "annotations"));
|
||||
diagnostic.set_fix(Fix::unsafe_edit(
|
||||
Importer::new(python_ast, checker.locator(), checker.stylist())
|
||||
.add_import(&required_import, TextSize::default()),
|
||||
));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,33 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_future_annotations/mod.rs
|
||||
---
|
||||
no_future_import_uses_lowercase.py:2:13: FA102 Missing `from __future__ import annotations`, but uses PEP 585 collection
|
||||
no_future_import_uses_lowercase.py:2:13: FA102 [*] Missing `from __future__ import annotations`, but uses PEP 585 collection
|
||||
|
|
||||
1 | def main() -> None:
|
||||
2 | a_list: list[str] = []
|
||||
| ^^^^^^^^^ FA102
|
||||
3 | a_list.append("hello")
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
no_future_import_uses_lowercase.py:6:14: FA102 Missing `from __future__ import annotations`, but uses PEP 585 collection
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | def main() -> None:
|
||||
2 3 | a_list: list[str] = []
|
||||
3 4 | a_list.append("hello")
|
||||
|
||||
no_future_import_uses_lowercase.py:6:14: FA102 [*] Missing `from __future__ import annotations`, but uses PEP 585 collection
|
||||
|
|
||||
6 | def hello(y: dict[str, int]) -> None:
|
||||
| ^^^^^^^^^^^^^^ FA102
|
||||
7 | del y
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | def main() -> None:
|
||||
2 3 | a_list: list[str] = []
|
||||
3 4 | a_list.append("hello")
|
||||
|
||||
|
||||
|
||||
@@ -1,34 +1,62 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_future_annotations/mod.rs
|
||||
---
|
||||
no_future_import_uses_union.py:2:13: FA102 Missing `from __future__ import annotations`, but uses PEP 585 collection
|
||||
no_future_import_uses_union.py:2:13: FA102 [*] Missing `from __future__ import annotations`, but uses PEP 585 collection
|
||||
|
|
||||
1 | def main() -> None:
|
||||
2 | a_list: list[str] | None = []
|
||||
| ^^^^^^^^^ FA102
|
||||
3 | a_list.append("hello")
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
no_future_import_uses_union.py:2:13: FA102 Missing `from __future__ import annotations`, but uses PEP 604 union
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | def main() -> None:
|
||||
2 3 | a_list: list[str] | None = []
|
||||
3 4 | a_list.append("hello")
|
||||
|
||||
no_future_import_uses_union.py:2:13: FA102 [*] Missing `from __future__ import annotations`, but uses PEP 604 union
|
||||
|
|
||||
1 | def main() -> None:
|
||||
2 | a_list: list[str] | None = []
|
||||
| ^^^^^^^^^^^^^^^^ FA102
|
||||
3 | a_list.append("hello")
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
no_future_import_uses_union.py:6:14: FA102 Missing `from __future__ import annotations`, but uses PEP 585 collection
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | def main() -> None:
|
||||
2 3 | a_list: list[str] | None = []
|
||||
3 4 | a_list.append("hello")
|
||||
|
||||
no_future_import_uses_union.py:6:14: FA102 [*] Missing `from __future__ import annotations`, but uses PEP 585 collection
|
||||
|
|
||||
6 | def hello(y: dict[str, int] | None) -> None:
|
||||
| ^^^^^^^^^^^^^^ FA102
|
||||
7 | del y
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
no_future_import_uses_union.py:6:14: FA102 Missing `from __future__ import annotations`, but uses PEP 604 union
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | def main() -> None:
|
||||
2 3 | a_list: list[str] | None = []
|
||||
3 4 | a_list.append("hello")
|
||||
|
||||
no_future_import_uses_union.py:6:14: FA102 [*] Missing `from __future__ import annotations`, but uses PEP 604 union
|
||||
|
|
||||
6 | def hello(y: dict[str, int] | None) -> None:
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ FA102
|
||||
7 | del y
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | def main() -> None:
|
||||
2 3 | a_list: list[str] | None = []
|
||||
3 4 | a_list.append("hello")
|
||||
|
||||
|
||||
|
||||
@@ -1,52 +1,94 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_future_annotations/mod.rs
|
||||
---
|
||||
no_future_import_uses_union_inner.py:2:13: FA102 Missing `from __future__ import annotations`, but uses PEP 585 collection
|
||||
no_future_import_uses_union_inner.py:2:13: FA102 [*] Missing `from __future__ import annotations`, but uses PEP 585 collection
|
||||
|
|
||||
1 | def main() -> None:
|
||||
2 | a_list: list[str | None] = []
|
||||
| ^^^^^^^^^^^^^^^^ FA102
|
||||
3 | a_list.append("hello")
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
no_future_import_uses_union_inner.py:2:18: FA102 Missing `from __future__ import annotations`, but uses PEP 604 union
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | def main() -> None:
|
||||
2 3 | a_list: list[str | None] = []
|
||||
3 4 | a_list.append("hello")
|
||||
|
||||
no_future_import_uses_union_inner.py:2:18: FA102 [*] Missing `from __future__ import annotations`, but uses PEP 604 union
|
||||
|
|
||||
1 | def main() -> None:
|
||||
2 | a_list: list[str | None] = []
|
||||
| ^^^^^^^^^^ FA102
|
||||
3 | a_list.append("hello")
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
no_future_import_uses_union_inner.py:6:14: FA102 Missing `from __future__ import annotations`, but uses PEP 585 collection
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | def main() -> None:
|
||||
2 3 | a_list: list[str | None] = []
|
||||
3 4 | a_list.append("hello")
|
||||
|
||||
no_future_import_uses_union_inner.py:6:14: FA102 [*] Missing `from __future__ import annotations`, but uses PEP 585 collection
|
||||
|
|
||||
6 | def hello(y: dict[str | None, int]) -> None:
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ FA102
|
||||
7 | z: tuple[str, str | None, str] = tuple(y)
|
||||
8 | del z
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
no_future_import_uses_union_inner.py:6:19: FA102 Missing `from __future__ import annotations`, but uses PEP 604 union
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | def main() -> None:
|
||||
2 3 | a_list: list[str | None] = []
|
||||
3 4 | a_list.append("hello")
|
||||
|
||||
no_future_import_uses_union_inner.py:6:19: FA102 [*] Missing `from __future__ import annotations`, but uses PEP 604 union
|
||||
|
|
||||
6 | def hello(y: dict[str | None, int]) -> None:
|
||||
| ^^^^^^^^^^ FA102
|
||||
7 | z: tuple[str, str | None, str] = tuple(y)
|
||||
8 | del z
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
no_future_import_uses_union_inner.py:7:8: FA102 Missing `from __future__ import annotations`, but uses PEP 585 collection
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | def main() -> None:
|
||||
2 3 | a_list: list[str | None] = []
|
||||
3 4 | a_list.append("hello")
|
||||
|
||||
no_future_import_uses_union_inner.py:7:8: FA102 [*] Missing `from __future__ import annotations`, but uses PEP 585 collection
|
||||
|
|
||||
6 | def hello(y: dict[str | None, int]) -> None:
|
||||
7 | z: tuple[str, str | None, str] = tuple(y)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ FA102
|
||||
8 | del z
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
no_future_import_uses_union_inner.py:7:19: FA102 Missing `from __future__ import annotations`, but uses PEP 604 union
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | def main() -> None:
|
||||
2 3 | a_list: list[str | None] = []
|
||||
3 4 | a_list.append("hello")
|
||||
|
||||
no_future_import_uses_union_inner.py:7:19: FA102 [*] Missing `from __future__ import annotations`, but uses PEP 604 union
|
||||
|
|
||||
6 | def hello(y: dict[str | None, int]) -> None:
|
||||
7 | z: tuple[str, str | None, str] = tuple(y)
|
||||
| ^^^^^^^^^^ FA102
|
||||
8 | del z
|
||||
|
|
||||
= help: Add `from __future__ import annotations`
|
||||
|
||||
ℹ Unsafe fix
|
||||
1 |+from __future__ import annotations
|
||||
1 2 | def main() -> None:
|
||||
2 3 | a_list: list[str | None] = []
|
||||
3 4 | a_list.append("hello")
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ mod tests {
|
||||
#[test_case(Path::new("test_pass_init"), Path::new("example.py"))]
|
||||
#[test_case(Path::new("test_fail_empty"), Path::new("example.py"))]
|
||||
#[test_case(Path::new("test_fail_nonempty"), Path::new("example.py"))]
|
||||
#[test_case(Path::new("test_fail_shebang"), Path::new("example.py"))]
|
||||
#[test_case(Path::new("test_pass_shebang"), Path::new("example.py"))]
|
||||
#[test_case(Path::new("test_ignored"), Path::new("example.py"))]
|
||||
#[test_case(Path::new("test_pass_namespace_package"), Path::new("example.py"))]
|
||||
#[test_case(Path::new("test_pass_pyi"), Path::new("example.pyi"))]
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_index::Indexer;
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
use crate::comments::shebang::ShebangDirective;
|
||||
use crate::fs;
|
||||
|
||||
/// ## What it does
|
||||
@@ -42,6 +44,8 @@ impl Violation for ImplicitNamespacePackage {
|
||||
pub(crate) fn implicit_namespace_package(
|
||||
path: &Path,
|
||||
package: Option<&Path>,
|
||||
locator: &Locator,
|
||||
indexer: &Indexer,
|
||||
project_root: &Path,
|
||||
src: &[PathBuf],
|
||||
) -> Option<Diagnostic> {
|
||||
@@ -56,6 +60,11 @@ pub(crate) fn implicit_namespace_package(
|
||||
&& !path
|
||||
.parent()
|
||||
.is_some_and( |parent| src.iter().any(|src| src == parent))
|
||||
// Ignore files that contain a shebang.
|
||||
&& !indexer
|
||||
.comment_ranges()
|
||||
.first().filter(|range| range.start() == TextSize::from(0))
|
||||
.is_some_and(|range| ShebangDirective::try_extract(locator.slice(*range)).is_some())
|
||||
{
|
||||
#[cfg(all(test, windows))]
|
||||
let path = path
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_no_pep420/mod.rs
|
||||
---
|
||||
example.py:1:1: INP001 File `./resources/test/fixtures/flake8_no_pep420/test_fail_shebang/example.py` is part of an implicit namespace package. Add an `__init__.py`.
|
||||
|
|
||||
1 | #!/bin/env/python
|
||||
| INP001
|
||||
2 | print('hi')
|
||||
|
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_no_pep420/mod.rs
|
||||
---
|
||||
|
||||
@@ -32,6 +32,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test_case(Rule::UnnecessaryPlaceholder, Path::new("PIE790.py"))]
|
||||
#[test_case(Rule::UnnecessarySpread, Path::new("PIE800.py"))]
|
||||
#[test_case(Rule::ReimplementedContainerBuiltin, Path::new("PIE807.py"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use ruff_python_ast::Expr;
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_diagnostics::Violation;
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_text_size::Ranged;
|
||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::{Ranged, TextSize};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
@@ -32,22 +33,76 @@ use crate::checkers::ast::Checker;
|
||||
pub struct UnnecessarySpread;
|
||||
|
||||
impl Violation for UnnecessarySpread {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Unnecessary spread `**`")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
Some(format!("Remove unnecessary dict"))
|
||||
}
|
||||
}
|
||||
|
||||
/// PIE800
|
||||
pub(crate) fn unnecessary_spread(checker: &mut Checker, keys: &[Option<Expr>], values: &[Expr]) {
|
||||
for item in keys.iter().zip(values.iter()) {
|
||||
pub(crate) fn unnecessary_spread(checker: &mut Checker, dict: &ast::ExprDict) {
|
||||
// The first "end" is the start of the dictionary, immediately following the open bracket.
|
||||
let mut prev_end = dict.start() + TextSize::from(1);
|
||||
for item in dict.keys.iter().zip(dict.values.iter()) {
|
||||
if let (None, value) = item {
|
||||
// We only care about when the key is None which indicates a spread `**`
|
||||
// inside a dict.
|
||||
if let Expr::Dict(_) = value {
|
||||
let diagnostic = Diagnostic::new(UnnecessarySpread, value.range());
|
||||
if let Expr::Dict(inner) = value {
|
||||
let mut diagnostic = Diagnostic::new(UnnecessarySpread, value.range());
|
||||
if checker.settings.preview.is_enabled() {
|
||||
if let Some(fix) = unnecessary_spread_fix(inner, prev_end, checker.locator()) {
|
||||
diagnostic.set_fix(fix);
|
||||
}
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
prev_end = item.1.end();
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a [`Fix`] to remove an unnecessary dictionary spread.
|
||||
fn unnecessary_spread_fix(
|
||||
dict: &ast::ExprDict,
|
||||
prev_end: TextSize,
|
||||
locator: &Locator,
|
||||
) -> Option<Fix> {
|
||||
// Find the `**` token preceding the spread.
|
||||
let doublestar = SimpleTokenizer::starts_at(prev_end, locator.contents())
|
||||
.find(|tok| matches!(tok.kind(), SimpleTokenKind::DoubleStar))?;
|
||||
|
||||
if let Some(last) = dict.values.last() {
|
||||
// Ex) `**{a: 1, b: 2}`
|
||||
let mut edits = vec![];
|
||||
for tok in SimpleTokenizer::starts_at(last.end(), locator.contents()).skip_trivia() {
|
||||
match tok.kind() {
|
||||
SimpleTokenKind::Comma => {
|
||||
edits.push(Edit::range_deletion(tok.range()));
|
||||
}
|
||||
SimpleTokenKind::RBrace => {
|
||||
edits.push(Edit::range_deletion(tok.range()));
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Some(Fix::safe_edits(
|
||||
// Delete the first `**{`
|
||||
Edit::deletion(doublestar.start(), dict.start() + TextSize::from(1)),
|
||||
// Delete the trailing `}`
|
||||
edits,
|
||||
))
|
||||
} else {
|
||||
// Ex) `**{}`
|
||||
Some(Fix::safe_edit(Edit::deletion(
|
||||
doublestar.start(),
|
||||
dict.end(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,37 +6,67 @@ PIE800.py:1:14: PIE800 Unnecessary spread `**`
|
||||
1 | {"foo": 1, **{"bar": 1}} # PIE800
|
||||
| ^^^^^^^^^^ PIE800
|
||||
2 |
|
||||
3 | foo({**foo, **{"bar": True}}) # PIE800
|
||||
3 | {**{"bar": 10}, "a": "b"} # PIE800
|
||||
|
|
||||
= help: Remove unnecessary dict
|
||||
|
||||
PIE800.py:3:15: PIE800 Unnecessary spread `**`
|
||||
PIE800.py:3:4: PIE800 Unnecessary spread `**`
|
||||
|
|
||||
1 | {"foo": 1, **{"bar": 1}} # PIE800
|
||||
2 |
|
||||
3 | foo({**foo, **{"bar": True}}) # PIE800
|
||||
3 | {**{"bar": 10}, "a": "b"} # PIE800
|
||||
| ^^^^^^^^^^^ PIE800
|
||||
4 |
|
||||
5 | foo({**foo, **{"bar": True}}) # PIE800
|
||||
|
|
||||
= help: Remove unnecessary dict
|
||||
|
||||
PIE800.py:5:15: PIE800 Unnecessary spread `**`
|
||||
|
|
||||
3 | {**{"bar": 10}, "a": "b"} # PIE800
|
||||
4 |
|
||||
5 | foo({**foo, **{"bar": True}}) # PIE800
|
||||
| ^^^^^^^^^^^^^ PIE800
|
||||
4 |
|
||||
5 | {**foo, **{"bar": 10}} # PIE800
|
||||
6 |
|
||||
7 | {**foo, **{"bar": 10}} # PIE800
|
||||
|
|
||||
= help: Remove unnecessary dict
|
||||
|
||||
PIE800.py:5:11: PIE800 Unnecessary spread `**`
|
||||
PIE800.py:7:11: PIE800 Unnecessary spread `**`
|
||||
|
|
||||
3 | foo({**foo, **{"bar": True}}) # PIE800
|
||||
4 |
|
||||
5 | {**foo, **{"bar": 10}} # PIE800
|
||||
5 | foo({**foo, **{"bar": True}}) # PIE800
|
||||
6 |
|
||||
7 | {**foo, **{"bar": 10}} # PIE800
|
||||
| ^^^^^^^^^^^ PIE800
|
||||
6 |
|
||||
7 | {**foo, **buzz, **{bar: 10}} # PIE800
|
||||
|
|
||||
|
||||
PIE800.py:7:19: PIE800 Unnecessary spread `**`
|
||||
|
|
||||
5 | {**foo, **{"bar": 10}} # PIE800
|
||||
6 |
|
||||
7 | {**foo, **buzz, **{bar: 10}} # PIE800
|
||||
| ^^^^^^^^^ PIE800
|
||||
8 |
|
||||
9 | {**foo, "bar": True } # OK
|
||||
9 | { # PIE800
|
||||
|
|
||||
= help: Remove unnecessary dict
|
||||
|
||||
PIE800.py:12:7: PIE800 Unnecessary spread `**`
|
||||
|
|
||||
10 | "a": "b",
|
||||
11 | # Preserve
|
||||
12 | **{
|
||||
| _______^
|
||||
13 | | # all
|
||||
14 | | "bar": 10, # the
|
||||
15 | | # comments
|
||||
16 | | },
|
||||
| |_____^ PIE800
|
||||
17 | }
|
||||
|
|
||||
= help: Remove unnecessary dict
|
||||
|
||||
PIE800.py:19:19: PIE800 Unnecessary spread `**`
|
||||
|
|
||||
17 | }
|
||||
18 |
|
||||
19 | {**foo, **buzz, **{bar: 10}} # PIE800
|
||||
| ^^^^^^^^^ PIE800
|
||||
20 |
|
||||
21 | {**foo, "bar": True } # OK
|
||||
|
|
||||
= help: Remove unnecessary dict
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_pie/mod.rs
|
||||
---
|
||||
PIE800.py:1:14: PIE800 [*] Unnecessary spread `**`
|
||||
|
|
||||
1 | {"foo": 1, **{"bar": 1}} # PIE800
|
||||
| ^^^^^^^^^^ PIE800
|
||||
2 |
|
||||
3 | {**{"bar": 10}, "a": "b"} # PIE800
|
||||
|
|
||||
= help: Remove unnecessary dict
|
||||
|
||||
ℹ Safe fix
|
||||
1 |-{"foo": 1, **{"bar": 1}} # PIE800
|
||||
1 |+{"foo": 1, "bar": 1} # PIE800
|
||||
2 2 |
|
||||
3 3 | {**{"bar": 10}, "a": "b"} # PIE800
|
||||
4 4 |
|
||||
|
||||
PIE800.py:3:4: PIE800 [*] Unnecessary spread `**`
|
||||
|
|
||||
1 | {"foo": 1, **{"bar": 1}} # PIE800
|
||||
2 |
|
||||
3 | {**{"bar": 10}, "a": "b"} # PIE800
|
||||
| ^^^^^^^^^^^ PIE800
|
||||
4 |
|
||||
5 | foo({**foo, **{"bar": True}}) # PIE800
|
||||
|
|
||||
= help: Remove unnecessary dict
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | {"foo": 1, **{"bar": 1}} # PIE800
|
||||
2 2 |
|
||||
3 |-{**{"bar": 10}, "a": "b"} # PIE800
|
||||
3 |+{"bar": 10, "a": "b"} # PIE800
|
||||
4 4 |
|
||||
5 5 | foo({**foo, **{"bar": True}}) # PIE800
|
||||
6 6 |
|
||||
|
||||
PIE800.py:5:15: PIE800 [*] Unnecessary spread `**`
|
||||
|
|
||||
3 | {**{"bar": 10}, "a": "b"} # PIE800
|
||||
4 |
|
||||
5 | foo({**foo, **{"bar": True}}) # PIE800
|
||||
| ^^^^^^^^^^^^^ PIE800
|
||||
6 |
|
||||
7 | {**foo, **{"bar": 10}} # PIE800
|
||||
|
|
||||
= help: Remove unnecessary dict
|
||||
|
||||
ℹ Safe fix
|
||||
2 2 |
|
||||
3 3 | {**{"bar": 10}, "a": "b"} # PIE800
|
||||
4 4 |
|
||||
5 |-foo({**foo, **{"bar": True}}) # PIE800
|
||||
5 |+foo({**foo, "bar": True}) # PIE800
|
||||
6 6 |
|
||||
7 7 | {**foo, **{"bar": 10}} # PIE800
|
||||
8 8 |
|
||||
|
||||
PIE800.py:7:11: PIE800 [*] Unnecessary spread `**`
|
||||
|
|
||||
5 | foo({**foo, **{"bar": True}}) # PIE800
|
||||
6 |
|
||||
7 | {**foo, **{"bar": 10}} # PIE800
|
||||
| ^^^^^^^^^^^ PIE800
|
||||
8 |
|
||||
9 | { # PIE800
|
||||
|
|
||||
= help: Remove unnecessary dict
|
||||
|
||||
ℹ Safe fix
|
||||
4 4 |
|
||||
5 5 | foo({**foo, **{"bar": True}}) # PIE800
|
||||
6 6 |
|
||||
7 |-{**foo, **{"bar": 10}} # PIE800
|
||||
7 |+{**foo, "bar": 10} # PIE800
|
||||
8 8 |
|
||||
9 9 | { # PIE800
|
||||
10 10 | "a": "b",
|
||||
|
||||
PIE800.py:12:7: PIE800 [*] Unnecessary spread `**`
|
||||
|
|
||||
10 | "a": "b",
|
||||
11 | # Preserve
|
||||
12 | **{
|
||||
| _______^
|
||||
13 | | # all
|
||||
14 | | "bar": 10, # the
|
||||
15 | | # comments
|
||||
16 | | },
|
||||
| |_____^ PIE800
|
||||
17 | }
|
||||
|
|
||||
= help: Remove unnecessary dict
|
||||
|
||||
ℹ Safe fix
|
||||
9 9 | { # PIE800
|
||||
10 10 | "a": "b",
|
||||
11 11 | # Preserve
|
||||
12 |- **{
|
||||
12 |+
|
||||
13 13 | # all
|
||||
14 |- "bar": 10, # the
|
||||
14 |+ "bar": 10 # the
|
||||
15 15 | # comments
|
||||
16 |- },
|
||||
16 |+ ,
|
||||
17 17 | }
|
||||
18 18 |
|
||||
19 19 | {**foo, **buzz, **{bar: 10}} # PIE800
|
||||
|
||||
PIE800.py:19:19: PIE800 [*] Unnecessary spread `**`
|
||||
|
|
||||
17 | }
|
||||
18 |
|
||||
19 | {**foo, **buzz, **{bar: 10}} # PIE800
|
||||
| ^^^^^^^^^ PIE800
|
||||
20 |
|
||||
21 | {**foo, "bar": True } # OK
|
||||
|
|
||||
= help: Remove unnecessary dict
|
||||
|
||||
ℹ Safe fix
|
||||
16 16 | },
|
||||
17 17 | }
|
||||
18 18 |
|
||||
19 |-{**foo, **buzz, **{bar: 10}} # PIE800
|
||||
19 |+{**foo, **buzz, bar: 10} # PIE800
|
||||
20 20 |
|
||||
21 21 | {**foo, "bar": True } # OK
|
||||
22 22 |
|
||||
|
||||
|
||||
@@ -97,11 +97,12 @@ pub(crate) fn zero_sleep_call(checker: &mut Checker, call: &ExprCall) {
|
||||
let mut diagnostic = Diagnostic::new(TrioZeroSleepCall, call.range());
|
||||
diagnostic.try_set_fix(|| {
|
||||
let (import_edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("trio", "lowlevel.checkpoint"),
|
||||
&ImportRequest::import_from("trio", "lowlevel"),
|
||||
call.func.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
let reference_edit = Edit::range_replacement(binding, call.func.range());
|
||||
let reference_edit =
|
||||
Edit::range_replacement(format!("{binding}.checkpoint"), call.func.range());
|
||||
let arg_edit = Edit::range_deletion(call.arguments.range);
|
||||
Ok(Fix::safe_edits(import_edit, [reference_edit, arg_edit]))
|
||||
});
|
||||
|
||||
@@ -1,119 +1,107 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_trio/mod.rs
|
||||
---
|
||||
TRIO115.py:6:11: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)`
|
||||
TRIO115.py:5:11: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)`
|
||||
|
|
||||
5 | async def func():
|
||||
6 | await trio.sleep(0) # TRIO115
|
||||
3 | from trio import sleep
|
||||
4 |
|
||||
5 | await trio.sleep(0) # TRIO115
|
||||
| ^^^^^^^^^^^^^ TRIO115
|
||||
7 | await trio.sleep(1) # OK
|
||||
8 | await trio.sleep(0, 1) # OK
|
||||
6 | await trio.sleep(1) # OK
|
||||
7 | await trio.sleep(0, 1) # OK
|
||||
|
|
||||
= help: Replace with `trio.lowlevel.checkpoint()`
|
||||
|
||||
ℹ Safe fix
|
||||
3 3 |
|
||||
2 2 | import trio
|
||||
3 3 | from trio import sleep
|
||||
4 4 |
|
||||
5 5 | async def func():
|
||||
6 |- await trio.sleep(0) # TRIO115
|
||||
6 |+ await trio.lowlevel.checkpoint # TRIO115
|
||||
7 7 | await trio.sleep(1) # OK
|
||||
8 8 | await trio.sleep(0, 1) # OK
|
||||
9 9 | await trio.sleep(...) # OK
|
||||
5 |- await trio.sleep(0) # TRIO115
|
||||
5 |+ await trio.lowlevel.checkpoint # TRIO115
|
||||
6 6 | await trio.sleep(1) # OK
|
||||
7 7 | await trio.sleep(0, 1) # OK
|
||||
8 8 | await trio.sleep(...) # OK
|
||||
|
||||
TRIO115.py:12:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)`
|
||||
TRIO115.py:11:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)`
|
||||
|
|
||||
10 | await trio.sleep() # OK
|
||||
11 |
|
||||
12 | trio.sleep(0) # TRIO115
|
||||
9 | await trio.sleep() # OK
|
||||
10 |
|
||||
11 | trio.sleep(0) # TRIO115
|
||||
| ^^^^^^^^^^^^^ TRIO115
|
||||
13 | foo = 0
|
||||
14 | trio.sleep(foo) # TRIO115
|
||||
12 | foo = 0
|
||||
13 | trio.sleep(foo) # TRIO115
|
||||
|
|
||||
= help: Replace with `trio.lowlevel.checkpoint()`
|
||||
|
||||
ℹ Safe fix
|
||||
9 9 | await trio.sleep(...) # OK
|
||||
10 10 | await trio.sleep() # OK
|
||||
11 11 |
|
||||
12 |- trio.sleep(0) # TRIO115
|
||||
12 |+ trio.lowlevel.checkpoint # TRIO115
|
||||
13 13 | foo = 0
|
||||
14 14 | trio.sleep(foo) # TRIO115
|
||||
15 15 | trio.sleep(1) # OK
|
||||
8 8 | await trio.sleep(...) # OK
|
||||
9 9 | await trio.sleep() # OK
|
||||
10 10 |
|
||||
11 |- trio.sleep(0) # TRIO115
|
||||
11 |+ trio.lowlevel.checkpoint # TRIO115
|
||||
12 12 | foo = 0
|
||||
13 13 | trio.sleep(foo) # TRIO115
|
||||
14 14 | trio.sleep(1) # OK
|
||||
|
||||
TRIO115.py:14:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)`
|
||||
TRIO115.py:13:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)`
|
||||
|
|
||||
12 | trio.sleep(0) # TRIO115
|
||||
13 | foo = 0
|
||||
14 | trio.sleep(foo) # TRIO115
|
||||
11 | trio.sleep(0) # TRIO115
|
||||
12 | foo = 0
|
||||
13 | trio.sleep(foo) # TRIO115
|
||||
| ^^^^^^^^^^^^^^^ TRIO115
|
||||
15 | trio.sleep(1) # OK
|
||||
16 | time.sleep(0) # OK
|
||||
14 | trio.sleep(1) # OK
|
||||
15 | time.sleep(0) # OK
|
||||
|
|
||||
= help: Replace with `trio.lowlevel.checkpoint()`
|
||||
|
||||
ℹ Safe fix
|
||||
11 11 |
|
||||
12 12 | trio.sleep(0) # TRIO115
|
||||
13 13 | foo = 0
|
||||
14 |- trio.sleep(foo) # TRIO115
|
||||
14 |+ trio.lowlevel.checkpoint # TRIO115
|
||||
15 15 | trio.sleep(1) # OK
|
||||
16 16 | time.sleep(0) # OK
|
||||
17 17 |
|
||||
10 10 |
|
||||
11 11 | trio.sleep(0) # TRIO115
|
||||
12 12 | foo = 0
|
||||
13 |- trio.sleep(foo) # TRIO115
|
||||
13 |+ trio.lowlevel.checkpoint # TRIO115
|
||||
14 14 | trio.sleep(1) # OK
|
||||
15 15 | time.sleep(0) # OK
|
||||
16 16 |
|
||||
|
||||
TRIO115.py:18:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)`
|
||||
TRIO115.py:17:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)`
|
||||
|
|
||||
16 | time.sleep(0) # OK
|
||||
17 |
|
||||
18 | sleep(0) # TRIO115
|
||||
15 | time.sleep(0) # OK
|
||||
16 |
|
||||
17 | sleep(0) # TRIO115
|
||||
| ^^^^^^^^ TRIO115
|
||||
19 |
|
||||
20 | bar = "bar"
|
||||
18 |
|
||||
19 | bar = "bar"
|
||||
|
|
||||
= help: Replace with `trio.lowlevel.checkpoint()`
|
||||
|
||||
ℹ Safe fix
|
||||
15 15 | trio.sleep(1) # OK
|
||||
16 16 | time.sleep(0) # OK
|
||||
17 17 |
|
||||
18 |- sleep(0) # TRIO115
|
||||
18 |+ trio.lowlevel.checkpoint # TRIO115
|
||||
19 19 |
|
||||
20 20 | bar = "bar"
|
||||
21 21 | trio.sleep(bar)
|
||||
14 14 | trio.sleep(1) # OK
|
||||
15 15 | time.sleep(0) # OK
|
||||
16 16 |
|
||||
17 |- sleep(0) # TRIO115
|
||||
17 |+ trio.lowlevel.checkpoint # TRIO115
|
||||
18 18 |
|
||||
19 19 | bar = "bar"
|
||||
20 20 | trio.sleep(bar)
|
||||
|
||||
TRIO115.py:24:1: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)`
|
||||
TRIO115.py:30:5: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)`
|
||||
|
|
||||
24 | trio.sleep(0) # TRIO115
|
||||
| ^^^^^^^^^^^^^ TRIO115
|
||||
29 | def func():
|
||||
30 | sleep(0) # TRIO115
|
||||
| ^^^^^^^^ TRIO115
|
||||
|
|
||||
= help: Replace with `trio.lowlevel.checkpoint()`
|
||||
|
||||
ℹ Safe fix
|
||||
21 21 | trio.sleep(bar)
|
||||
22 22 |
|
||||
23 23 |
|
||||
24 |-trio.sleep(0) # TRIO115
|
||||
24 |+trio.lowlevel.checkpoint # TRIO115
|
||||
24 24 | trio.run(trio.sleep(0)) # TRIO115
|
||||
25 25 |
|
||||
26 26 |
|
||||
27 27 | def func():
|
||||
|
||||
TRIO115.py:28:14: TRIO115 [*] Use `trio.lowlevel.checkpoint()` instead of `trio.sleep(0)`
|
||||
|
|
||||
27 | def func():
|
||||
28 | trio.run(trio.sleep(0)) # TRIO115
|
||||
| ^^^^^^^^^^^^^ TRIO115
|
||||
|
|
||||
= help: Replace with `trio.lowlevel.checkpoint()`
|
||||
|
||||
ℹ Safe fix
|
||||
25 25 |
|
||||
26 26 |
|
||||
27 27 | def func():
|
||||
28 |- trio.run(trio.sleep(0)) # TRIO115
|
||||
28 |+ trio.run(trio.lowlevel.checkpoint) # TRIO115
|
||||
27 |-from trio import Event, sleep
|
||||
27 |+from trio import Event, sleep, lowlevel
|
||||
28 28 |
|
||||
29 29 | def func():
|
||||
30 |- sleep(0) # TRIO115
|
||||
30 |+ lowlevel.checkpoint # TRIO115
|
||||
|
||||
|
||||
|
||||
@@ -171,18 +171,18 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
from __future__ import annotations
|
||||
|
||||
import pandas as pd
|
||||
|
||||
def f(x: pd.DataFrame):
|
||||
pass
|
||||
"#,
|
||||
",
|
||||
"no_typing_import"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
@@ -191,11 +191,11 @@ mod tests {
|
||||
|
||||
def f(x: pd.DataFrame):
|
||||
pass
|
||||
"#,
|
||||
",
|
||||
"typing_import_before_package_import"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
from __future__ import annotations
|
||||
|
||||
import pandas as pd
|
||||
@@ -204,11 +204,11 @@ mod tests {
|
||||
|
||||
def f(x: pd.DataFrame):
|
||||
pass
|
||||
"#,
|
||||
",
|
||||
"typing_import_after_package_import"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
from __future__ import annotations
|
||||
|
||||
import pandas as pd
|
||||
@@ -217,11 +217,11 @@ mod tests {
|
||||
pass
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
"#,
|
||||
",
|
||||
"typing_import_after_usage"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
@@ -233,11 +233,11 @@ mod tests {
|
||||
|
||||
def f(x: pd.DataFrame):
|
||||
pass
|
||||
"#,
|
||||
",
|
||||
"type_checking_block_own_line"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
@@ -248,11 +248,11 @@ mod tests {
|
||||
|
||||
def f(x: pd.DataFrame):
|
||||
pass
|
||||
"#,
|
||||
",
|
||||
"type_checking_block_inline"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
@@ -265,11 +265,11 @@ mod tests {
|
||||
|
||||
def f(x: pd.DataFrame):
|
||||
pass
|
||||
"#,
|
||||
",
|
||||
"type_checking_block_comment"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
@@ -281,11 +281,11 @@ mod tests {
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import os
|
||||
"#,
|
||||
",
|
||||
"type_checking_block_after_usage"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
from __future__ import annotations
|
||||
|
||||
from pandas import (
|
||||
@@ -295,11 +295,11 @@ mod tests {
|
||||
|
||||
def f(x: DataFrame):
|
||||
pass
|
||||
"#,
|
||||
",
|
||||
"import_from"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
@@ -314,11 +314,11 @@ mod tests {
|
||||
|
||||
def f(x: DataFrame):
|
||||
pass
|
||||
"#,
|
||||
",
|
||||
"import_from_type_checking_block"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
@@ -330,11 +330,11 @@ mod tests {
|
||||
|
||||
def f(x: DataFrame, y: Series):
|
||||
pass
|
||||
"#,
|
||||
",
|
||||
"multiple_members"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
@@ -343,11 +343,11 @@ mod tests {
|
||||
|
||||
def f(x: os, y: sys):
|
||||
pass
|
||||
"#,
|
||||
",
|
||||
"multiple_modules_same_type"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
@@ -356,7 +356,7 @@ mod tests {
|
||||
|
||||
def f(x: os, y: pandas):
|
||||
pass
|
||||
"#,
|
||||
",
|
||||
"multiple_modules_different_types"
|
||||
)]
|
||||
fn contents(contents: &str, snapshot: &str) {
|
||||
|
||||
@@ -166,10 +166,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn trivial() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
def trivial():
|
||||
pass
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(get_complexity_number(&stmts), 1);
|
||||
Ok(())
|
||||
@@ -177,10 +177,10 @@ def trivial():
|
||||
|
||||
#[test]
|
||||
fn expr_as_statement() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
def expr_as_statement():
|
||||
0xF00D
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(get_complexity_number(&stmts), 1);
|
||||
Ok(())
|
||||
@@ -188,12 +188,12 @@ def expr_as_statement():
|
||||
|
||||
#[test]
|
||||
fn sequential() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
def sequential(n):
|
||||
k = n + 4
|
||||
s = k + n
|
||||
return s
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(get_complexity_number(&stmts), 1);
|
||||
Ok(())
|
||||
@@ -234,11 +234,11 @@ def nested_ifs():
|
||||
|
||||
#[test]
|
||||
fn for_loop() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
def for_loop():
|
||||
for i in range(10):
|
||||
print(i)
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(get_complexity_number(&stmts), 2);
|
||||
Ok(())
|
||||
@@ -246,13 +246,13 @@ def for_loop():
|
||||
|
||||
#[test]
|
||||
fn for_else() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
def for_else(mylist):
|
||||
for i in mylist:
|
||||
print(i)
|
||||
else:
|
||||
print(None)
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(get_complexity_number(&stmts), 2);
|
||||
Ok(())
|
||||
@@ -260,13 +260,13 @@ def for_else(mylist):
|
||||
|
||||
#[test]
|
||||
fn recursive() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
def recursive(n):
|
||||
if n > 4:
|
||||
return f(n - 1)
|
||||
else:
|
||||
return n
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(get_complexity_number(&stmts), 2);
|
||||
Ok(())
|
||||
@@ -274,7 +274,7 @@ def recursive(n):
|
||||
|
||||
#[test]
|
||||
fn nested_functions() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
def nested_functions():
|
||||
def a():
|
||||
def b():
|
||||
@@ -283,7 +283,7 @@ def nested_functions():
|
||||
b()
|
||||
|
||||
a()
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(get_complexity_number(&stmts), 3);
|
||||
Ok(())
|
||||
@@ -291,7 +291,7 @@ def nested_functions():
|
||||
|
||||
#[test]
|
||||
fn try_else() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
def try_else():
|
||||
try:
|
||||
print(1)
|
||||
@@ -301,7 +301,7 @@ def try_else():
|
||||
print(3)
|
||||
else:
|
||||
print(4)
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(get_complexity_number(&stmts), 4);
|
||||
Ok(())
|
||||
@@ -309,7 +309,7 @@ def try_else():
|
||||
|
||||
#[test]
|
||||
fn nested_try_finally() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
def nested_try_finally():
|
||||
try:
|
||||
try:
|
||||
@@ -318,7 +318,7 @@ def nested_try_finally():
|
||||
print(2)
|
||||
finally:
|
||||
print(3)
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(get_complexity_number(&stmts), 1);
|
||||
Ok(())
|
||||
@@ -326,7 +326,7 @@ def nested_try_finally():
|
||||
|
||||
#[test]
|
||||
fn foobar() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
async def foobar(a, b, c):
|
||||
await whatever(a, b, c)
|
||||
if await b:
|
||||
@@ -335,7 +335,7 @@ async def foobar(a, b, c):
|
||||
pass
|
||||
async for x in a:
|
||||
pass
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(get_complexity_number(&stmts), 3);
|
||||
Ok(())
|
||||
@@ -343,10 +343,10 @@ async def foobar(a, b, c):
|
||||
|
||||
#[test]
|
||||
fn annotated_assign() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
def annotated_assign():
|
||||
x: Any = None
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(get_complexity_number(&stmts), 1);
|
||||
Ok(())
|
||||
@@ -354,7 +354,7 @@ def annotated_assign():
|
||||
|
||||
#[test]
|
||||
fn class() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
class Class:
|
||||
def handle(self, *args, **options):
|
||||
if args:
|
||||
@@ -382,7 +382,7 @@ class Class:
|
||||
pass
|
||||
|
||||
return ServiceProvider(Logger())
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(get_complexity_number(&stmts), 9);
|
||||
Ok(())
|
||||
@@ -390,13 +390,13 @@ class Class:
|
||||
|
||||
#[test]
|
||||
fn finally() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
def process_detect_lines():
|
||||
try:
|
||||
pass
|
||||
finally:
|
||||
pass
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(get_complexity_number(&stmts), 1);
|
||||
Ok(())
|
||||
@@ -419,12 +419,12 @@ def process_detect_lines():
|
||||
|
||||
#[test]
|
||||
fn with() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
def with_lock():
|
||||
with lock:
|
||||
if foo:
|
||||
print('bar')
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(get_complexity_number(&stmts), 2);
|
||||
Ok(())
|
||||
|
||||
@@ -30,17 +30,17 @@ mod tests {
|
||||
"PD002_fail"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
nas = pd.isna(val)
|
||||
"#,
|
||||
",
|
||||
"PD003_pass"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
nulls = pd.isnull(val)
|
||||
"#,
|
||||
",
|
||||
"PD003_fail"
|
||||
)]
|
||||
#[test_case(
|
||||
@@ -51,17 +51,17 @@ mod tests {
|
||||
"PD003_allows_other_calls"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
not_nas = pd.notna(val)
|
||||
"#,
|
||||
",
|
||||
"PD004_pass"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
not_nulls = pd.notnull(val)
|
||||
"#,
|
||||
",
|
||||
"PD004_fail"
|
||||
)]
|
||||
#[test_case(
|
||||
@@ -73,11 +73,11 @@ mod tests {
|
||||
"PD007_pass_loc"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
x = pd.DataFrame()
|
||||
new_x = x.iloc[[1, 3, 5], [1, 3]]
|
||||
"#,
|
||||
",
|
||||
"PD007_pass_iloc"
|
||||
)]
|
||||
#[test_case(
|
||||
@@ -134,19 +134,19 @@ mod tests {
|
||||
"PD008_fail"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
x = pd.DataFrame()
|
||||
index = x.iloc[:, 1:3]
|
||||
"#,
|
||||
",
|
||||
"PD009_pass"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
x = pd.DataFrame()
|
||||
index = x.iat[:, 1:3]
|
||||
"#,
|
||||
",
|
||||
"PD009_fail"
|
||||
)]
|
||||
#[test_case(
|
||||
@@ -178,80 +178,80 @@ mod tests {
|
||||
"PD010_fail_pivot"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
x = pd.DataFrame()
|
||||
result = x.to_array()
|
||||
"#,
|
||||
",
|
||||
"PD011_pass_to_array"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
x = pd.DataFrame()
|
||||
result = x.array
|
||||
"#,
|
||||
",
|
||||
"PD011_pass_array"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
x = pd.DataFrame()
|
||||
result = x.values
|
||||
"#,
|
||||
",
|
||||
"PD011_fail_values"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
x = pd.DataFrame()
|
||||
result = x.values()
|
||||
"#,
|
||||
",
|
||||
"PD011_pass_values_call"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
x = pd.DataFrame()
|
||||
x.values = 1
|
||||
"#,
|
||||
",
|
||||
"PD011_pass_values_store"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
class Class:
|
||||
def __init__(self, values: str) -> None:
|
||||
self.values = values
|
||||
print(self.values)
|
||||
"#,
|
||||
",
|
||||
"PD011_pass_values_instance"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
result = {}.values
|
||||
"#,
|
||||
",
|
||||
"PD011_pass_values_dict"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
result = pd.values
|
||||
"#,
|
||||
",
|
||||
"PD011_pass_values_import"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
result = x.values
|
||||
"#,
|
||||
",
|
||||
"PD011_pass_values_unbound"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
result = values
|
||||
"#,
|
||||
",
|
||||
"PD011_pass_node_name"
|
||||
)]
|
||||
#[test_case(
|
||||
@@ -267,33 +267,33 @@ mod tests {
|
||||
"PD013_pass"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import numpy as np
|
||||
arrays = [np.random.randn(3, 4) for _ in range(10)]
|
||||
np.stack(arrays, axis=0).shape
|
||||
"#,
|
||||
",
|
||||
"PD013_pass_numpy"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
y = x.stack(level=-1, dropna=True)
|
||||
"#,
|
||||
",
|
||||
"PD013_pass_unbound"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
x = pd.DataFrame()
|
||||
y = x.stack(level=-1, dropna=True)
|
||||
"#,
|
||||
",
|
||||
"PD013_fail_stack"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
pd.stack(
|
||||
"#,
|
||||
",
|
||||
"PD015_pass_merge_on_dataframe"
|
||||
)]
|
||||
#[test_case(
|
||||
@@ -306,12 +306,12 @@ mod tests {
|
||||
"PD015_pass_merge_on_dataframe_with_multiple_args"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
x = pd.DataFrame()
|
||||
y = pd.DataFrame()
|
||||
pd.merge(x, y)
|
||||
"#,
|
||||
",
|
||||
"PD015_fail_merge_on_pandas_object"
|
||||
)]
|
||||
#[test_case(
|
||||
@@ -321,31 +321,31 @@ mod tests {
|
||||
"PD015_pass_other_pd_function"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
employees = pd.DataFrame(employee_dict)
|
||||
"#,
|
||||
",
|
||||
"PD901_pass_non_df"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
employees_df = pd.DataFrame(employee_dict)
|
||||
"#,
|
||||
",
|
||||
"PD901_pass_part_df"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
my_function(df=data)
|
||||
"#,
|
||||
",
|
||||
"PD901_pass_df_param"
|
||||
)]
|
||||
#[test_case(
|
||||
r#"
|
||||
r"
|
||||
import pandas as pd
|
||||
df = pd.DataFrame()
|
||||
"#,
|
||||
",
|
||||
"PD901_fail_df_var"
|
||||
)]
|
||||
fn contents(contents: &str, snapshot: &str) {
|
||||
|
||||
@@ -24,13 +24,13 @@ use crate::settings::types::IdentifierPattern;
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// def MY_FUNCTION():
|
||||
/// def my_function(A, myArg):
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// def my_function():
|
||||
/// def my_function(a, my_arg):
|
||||
/// pass
|
||||
/// ```
|
||||
///
|
||||
|
||||
@@ -518,10 +518,10 @@ mod tests {
|
||||
#[test]
|
||||
fn multi_line() {
|
||||
assert_logical_lines(
|
||||
r#"
|
||||
r"
|
||||
x = 1
|
||||
y = 2
|
||||
z = x + 1"#
|
||||
z = x + 1"
|
||||
.trim(),
|
||||
&["x = 1", "y = 2", "z = x + 1"],
|
||||
);
|
||||
@@ -530,14 +530,14 @@ z = x + 1"#
|
||||
#[test]
|
||||
fn indented() {
|
||||
assert_logical_lines(
|
||||
r#"
|
||||
r"
|
||||
x = [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
y = 2
|
||||
z = x + 1"#
|
||||
z = x + 1"
|
||||
.trim(),
|
||||
&["x = [\n 1,\n 2,\n 3,\n]", "y = 2", "z = x + 1"],
|
||||
);
|
||||
@@ -551,11 +551,11 @@ z = x + 1"#
|
||||
#[test]
|
||||
fn function_definition() {
|
||||
assert_logical_lines(
|
||||
r#"
|
||||
r"
|
||||
def f():
|
||||
x = 1
|
||||
f()"#
|
||||
.trim(),
|
||||
f()"
|
||||
.trim(),
|
||||
&["def f():", "x = 1", "f()"],
|
||||
);
|
||||
}
|
||||
@@ -583,11 +583,11 @@ f()"#
|
||||
#[test]
|
||||
fn empty_line() {
|
||||
assert_logical_lines(
|
||||
r#"
|
||||
r"
|
||||
if False:
|
||||
|
||||
print()
|
||||
"#
|
||||
"
|
||||
.trim(),
|
||||
&["if False:", "print()", ""],
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ use ruff_diagnostics::{Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::docstrings::{clean_space, leading_space};
|
||||
use ruff_source_file::NewlineWithTrailingNewline;
|
||||
use ruff_text_size::Ranged;
|
||||
use ruff_text_size::{Ranged, TextSize};
|
||||
use ruff_text_size::{TextLen, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -172,6 +172,7 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
|
||||
let mut has_seen_tab = docstring.indentation.contains('\t');
|
||||
let mut is_over_indented = true;
|
||||
let mut over_indented_lines = vec![];
|
||||
let mut over_indented_offset = TextSize::from(u32::MAX);
|
||||
|
||||
for i in 0..lines.len() {
|
||||
// First lines and continuations doesn't need any indentation.
|
||||
@@ -217,7 +218,13 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
|
||||
// the over-indentation status of every line.
|
||||
if i < lines.len() - 1 {
|
||||
if line_indent.len() > docstring.indentation.len() {
|
||||
over_indented_lines.push(TextRange::at(line.start(), line_indent.text_len()));
|
||||
over_indented_lines.push(line);
|
||||
|
||||
// Track the _smallest_ offset we see
|
||||
over_indented_offset = std::cmp::min(
|
||||
line_indent.text_len() - docstring.indentation.text_len(),
|
||||
over_indented_offset,
|
||||
);
|
||||
} else {
|
||||
is_over_indented = false;
|
||||
}
|
||||
@@ -235,16 +242,21 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
|
||||
if checker.enabled(Rule::OverIndentation) {
|
||||
// If every line (except the last) is over-indented...
|
||||
if is_over_indented {
|
||||
for over_indented in over_indented_lines {
|
||||
for line in over_indented_lines {
|
||||
let line_indent = leading_space(line);
|
||||
let indent = clean_space(docstring.indentation);
|
||||
|
||||
// We report over-indentation on every line. This isn't great, but
|
||||
// enables fix.
|
||||
let mut diagnostic =
|
||||
Diagnostic::new(OverIndentation, TextRange::empty(over_indented.start()));
|
||||
let indent = clean_space(docstring.indentation);
|
||||
Diagnostic::new(OverIndentation, TextRange::empty(line.start()));
|
||||
let edit = if indent.is_empty() {
|
||||
Edit::range_deletion(over_indented)
|
||||
Edit::range_deletion(TextRange::at(line.start(), line_indent.text_len()))
|
||||
} else {
|
||||
Edit::range_replacement(indent, over_indented)
|
||||
Edit::range_replacement(
|
||||
indent.clone(),
|
||||
TextRange::at(line.start(), indent.text_len() + over_indented_offset),
|
||||
)
|
||||
};
|
||||
diagnostic.set_fix(Fix::safe_edit(edit));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
|
||||
@@ -44,7 +44,7 @@ impl Violation for TripleSingleQuotes {
|
||||
let TripleSingleQuotes { expected_quote } = self;
|
||||
match expected_quote {
|
||||
Quote::Double => format!(r#"Use triple double quotes `"""`"#),
|
||||
Quote::Single => format!(r#"Use triple single quotes `'''`"#),
|
||||
Quote::Single => format!(r"Use triple single quotes `'''`"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,4 +62,353 @@ D.py:272:1: D208 [*] Docstring is over-indented
|
||||
274 274 | """
|
||||
275 275 |
|
||||
|
||||
D.py:673:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
671 | """Summary.
|
||||
672 |
|
||||
673 | This is overindented
|
||||
| D208
|
||||
674 | And so is this, but it we should preserve the extra space on this line relative
|
||||
675 | to the one before
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
670 670 | def retain_extra_whitespace():
|
||||
671 671 | """Summary.
|
||||
672 672 |
|
||||
673 |- This is overindented
|
||||
673 |+ This is overindented
|
||||
674 674 | And so is this, but it we should preserve the extra space on this line relative
|
||||
675 675 | to the one before
|
||||
676 676 | """
|
||||
|
||||
D.py:674:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
673 | This is overindented
|
||||
674 | And so is this, but it we should preserve the extra space on this line relative
|
||||
| D208
|
||||
675 | to the one before
|
||||
676 | """
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
671 671 | """Summary.
|
||||
672 672 |
|
||||
673 673 | This is overindented
|
||||
674 |- And so is this, but it we should preserve the extra space on this line relative
|
||||
674 |+ And so is this, but it we should preserve the extra space on this line relative
|
||||
675 675 | to the one before
|
||||
676 676 | """
|
||||
677 677 |
|
||||
|
||||
D.py:675:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
673 | This is overindented
|
||||
674 | And so is this, but it we should preserve the extra space on this line relative
|
||||
675 | to the one before
|
||||
| D208
|
||||
676 | """
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
672 672 |
|
||||
673 673 | This is overindented
|
||||
674 674 | And so is this, but it we should preserve the extra space on this line relative
|
||||
675 |- to the one before
|
||||
675 |+ to the one before
|
||||
676 676 | """
|
||||
677 677 |
|
||||
678 678 |
|
||||
|
||||
D.py:682:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
680 | """Summary.
|
||||
681 |
|
||||
682 | This is overindented
|
||||
| D208
|
||||
683 | And so is this, but it we should preserve the extra space on this line relative
|
||||
684 | to the one before
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
679 679 | def retain_extra_whitespace_multiple():
|
||||
680 680 | """Summary.
|
||||
681 681 |
|
||||
682 |- This is overindented
|
||||
682 |+ This is overindented
|
||||
683 683 | And so is this, but it we should preserve the extra space on this line relative
|
||||
684 684 | to the one before
|
||||
685 685 | This is also overindented
|
||||
|
||||
D.py:683:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
682 | This is overindented
|
||||
683 | And so is this, but it we should preserve the extra space on this line relative
|
||||
| D208
|
||||
684 | to the one before
|
||||
685 | This is also overindented
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
680 680 | """Summary.
|
||||
681 681 |
|
||||
682 682 | This is overindented
|
||||
683 |- And so is this, but it we should preserve the extra space on this line relative
|
||||
683 |+ And so is this, but it we should preserve the extra space on this line relative
|
||||
684 684 | to the one before
|
||||
685 685 | This is also overindented
|
||||
686 686 | And so is this, but it we should preserve the extra space on this line relative
|
||||
|
||||
D.py:684:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
682 | This is overindented
|
||||
683 | And so is this, but it we should preserve the extra space on this line relative
|
||||
684 | to the one before
|
||||
| D208
|
||||
685 | This is also overindented
|
||||
686 | And so is this, but it we should preserve the extra space on this line relative
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
681 681 |
|
||||
682 682 | This is overindented
|
||||
683 683 | And so is this, but it we should preserve the extra space on this line relative
|
||||
684 |- to the one before
|
||||
684 |+ to the one before
|
||||
685 685 | This is also overindented
|
||||
686 686 | And so is this, but it we should preserve the extra space on this line relative
|
||||
687 687 | to the one before
|
||||
|
||||
D.py:685:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
683 | And so is this, but it we should preserve the extra space on this line relative
|
||||
684 | to the one before
|
||||
685 | This is also overindented
|
||||
| D208
|
||||
686 | And so is this, but it we should preserve the extra space on this line relative
|
||||
687 | to the one before
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
682 682 | This is overindented
|
||||
683 683 | And so is this, but it we should preserve the extra space on this line relative
|
||||
684 684 | to the one before
|
||||
685 |- This is also overindented
|
||||
685 |+ This is also overindented
|
||||
686 686 | And so is this, but it we should preserve the extra space on this line relative
|
||||
687 687 | to the one before
|
||||
688 688 | """
|
||||
|
||||
D.py:686:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
684 | to the one before
|
||||
685 | This is also overindented
|
||||
686 | And so is this, but it we should preserve the extra space on this line relative
|
||||
| D208
|
||||
687 | to the one before
|
||||
688 | """
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
683 683 | And so is this, but it we should preserve the extra space on this line relative
|
||||
684 684 | to the one before
|
||||
685 685 | This is also overindented
|
||||
686 |- And so is this, but it we should preserve the extra space on this line relative
|
||||
686 |+ And so is this, but it we should preserve the extra space on this line relative
|
||||
687 687 | to the one before
|
||||
688 688 | """
|
||||
689 689 |
|
||||
|
||||
D.py:687:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
685 | This is also overindented
|
||||
686 | And so is this, but it we should preserve the extra space on this line relative
|
||||
687 | to the one before
|
||||
| D208
|
||||
688 | """
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
684 684 | to the one before
|
||||
685 685 | This is also overindented
|
||||
686 686 | And so is this, but it we should preserve the extra space on this line relative
|
||||
687 |- to the one before
|
||||
687 |+ to the one before
|
||||
688 688 | """
|
||||
689 689 |
|
||||
690 690 |
|
||||
|
||||
D.py:695:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
693 | """Summary.
|
||||
694 |
|
||||
695 | This is overindented
|
||||
| D208
|
||||
696 | And so is this, but it we should preserve the extra space on this line relative
|
||||
697 | to the one before
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
692 692 | def retain_extra_whitespace_deeper():
|
||||
693 693 | """Summary.
|
||||
694 694 |
|
||||
695 |- This is overindented
|
||||
695 |+ This is overindented
|
||||
696 696 | And so is this, but it we should preserve the extra space on this line relative
|
||||
697 697 | to the one before
|
||||
698 698 | And the relative indent here should be preserved too
|
||||
|
||||
D.py:696:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
695 | This is overindented
|
||||
696 | And so is this, but it we should preserve the extra space on this line relative
|
||||
| D208
|
||||
697 | to the one before
|
||||
698 | And the relative indent here should be preserved too
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
693 693 | """Summary.
|
||||
694 694 |
|
||||
695 695 | This is overindented
|
||||
696 |- And so is this, but it we should preserve the extra space on this line relative
|
||||
696 |+ And so is this, but it we should preserve the extra space on this line relative
|
||||
697 697 | to the one before
|
||||
698 698 | And the relative indent here should be preserved too
|
||||
699 699 | """
|
||||
|
||||
D.py:697:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
695 | This is overindented
|
||||
696 | And so is this, but it we should preserve the extra space on this line relative
|
||||
697 | to the one before
|
||||
| D208
|
||||
698 | And the relative indent here should be preserved too
|
||||
699 | """
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
694 694 |
|
||||
695 695 | This is overindented
|
||||
696 696 | And so is this, but it we should preserve the extra space on this line relative
|
||||
697 |- to the one before
|
||||
697 |+ to the one before
|
||||
698 698 | And the relative indent here should be preserved too
|
||||
699 699 | """
|
||||
700 700 |
|
||||
|
||||
D.py:698:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
696 | And so is this, but it we should preserve the extra space on this line relative
|
||||
697 | to the one before
|
||||
698 | And the relative indent here should be preserved too
|
||||
| D208
|
||||
699 | """
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
695 695 | This is overindented
|
||||
696 696 | And so is this, but it we should preserve the extra space on this line relative
|
||||
697 697 | to the one before
|
||||
698 |- And the relative indent here should be preserved too
|
||||
698 |+ And the relative indent here should be preserved too
|
||||
699 699 | """
|
||||
700 700 |
|
||||
701 701 | def retain_extra_whitespace_followed_by_same_offset():
|
||||
|
||||
D.py:704:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
702 | """Summary.
|
||||
703 |
|
||||
704 | This is overindented
|
||||
| D208
|
||||
705 | And so is this, but it we should preserve the extra space on this line relative
|
||||
706 | This is overindented
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
701 701 | def retain_extra_whitespace_followed_by_same_offset():
|
||||
702 702 | """Summary.
|
||||
703 703 |
|
||||
704 |- This is overindented
|
||||
704 |+ This is overindented
|
||||
705 705 | And so is this, but it we should preserve the extra space on this line relative
|
||||
706 706 | This is overindented
|
||||
707 707 | This is overindented
|
||||
|
||||
D.py:705:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
704 | This is overindented
|
||||
705 | And so is this, but it we should preserve the extra space on this line relative
|
||||
| D208
|
||||
706 | This is overindented
|
||||
707 | This is overindented
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
702 702 | """Summary.
|
||||
703 703 |
|
||||
704 704 | This is overindented
|
||||
705 |- And so is this, but it we should preserve the extra space on this line relative
|
||||
705 |+ And so is this, but it we should preserve the extra space on this line relative
|
||||
706 706 | This is overindented
|
||||
707 707 | This is overindented
|
||||
708 708 | """
|
||||
|
||||
D.py:706:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
704 | This is overindented
|
||||
705 | And so is this, but it we should preserve the extra space on this line relative
|
||||
706 | This is overindented
|
||||
| D208
|
||||
707 | This is overindented
|
||||
708 | """
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
703 703 |
|
||||
704 704 | This is overindented
|
||||
705 705 | And so is this, but it we should preserve the extra space on this line relative
|
||||
706 |- This is overindented
|
||||
706 |+ This is overindented
|
||||
707 707 | This is overindented
|
||||
708 708 | """
|
||||
709 709 |
|
||||
|
||||
D.py:707:1: D208 [*] Docstring is over-indented
|
||||
|
|
||||
705 | And so is this, but it we should preserve the extra space on this line relative
|
||||
706 | This is overindented
|
||||
707 | This is overindented
|
||||
| D208
|
||||
708 | """
|
||||
|
|
||||
= help: Remove over-indentation
|
||||
|
||||
ℹ Safe fix
|
||||
704 704 | This is overindented
|
||||
705 705 | And so is this, but it we should preserve the extra space on this line relative
|
||||
706 706 | This is overindented
|
||||
707 |- This is overindented
|
||||
707 |+ This is overindented
|
||||
708 708 | """
|
||||
709 709 |
|
||||
710 710 |
|
||||
|
||||
|
||||
|
||||
@@ -547,4 +547,136 @@ D.py:615:5: D213 [*] Multi-line docstring summary should start at the second lin
|
||||
617 618 | """
|
||||
618 619 |
|
||||
|
||||
D.py:671:5: D213 [*] Multi-line docstring summary should start at the second line
|
||||
|
|
||||
670 | def retain_extra_whitespace():
|
||||
671 | """Summary.
|
||||
| _____^
|
||||
672 | |
|
||||
673 | | This is overindented
|
||||
674 | | And so is this, but it we should preserve the extra space on this line relative
|
||||
675 | | to the one before
|
||||
676 | | """
|
||||
| |_______^ D213
|
||||
|
|
||||
= help: Insert line break and indentation after opening quotes
|
||||
|
||||
ℹ Safe fix
|
||||
668 668 |
|
||||
669 669 |
|
||||
670 670 | def retain_extra_whitespace():
|
||||
671 |- """Summary.
|
||||
671 |+ """
|
||||
672 |+ Summary.
|
||||
672 673 |
|
||||
673 674 | This is overindented
|
||||
674 675 | And so is this, but it we should preserve the extra space on this line relative
|
||||
|
||||
D.py:680:5: D213 [*] Multi-line docstring summary should start at the second line
|
||||
|
|
||||
679 | def retain_extra_whitespace_multiple():
|
||||
680 | """Summary.
|
||||
| _____^
|
||||
681 | |
|
||||
682 | | This is overindented
|
||||
683 | | And so is this, but it we should preserve the extra space on this line relative
|
||||
684 | | to the one before
|
||||
685 | | This is also overindented
|
||||
686 | | And so is this, but it we should preserve the extra space on this line relative
|
||||
687 | | to the one before
|
||||
688 | | """
|
||||
| |_______^ D213
|
||||
|
|
||||
= help: Insert line break and indentation after opening quotes
|
||||
|
||||
ℹ Safe fix
|
||||
677 677 |
|
||||
678 678 |
|
||||
679 679 | def retain_extra_whitespace_multiple():
|
||||
680 |- """Summary.
|
||||
680 |+ """
|
||||
681 |+ Summary.
|
||||
681 682 |
|
||||
682 683 | This is overindented
|
||||
683 684 | And so is this, but it we should preserve the extra space on this line relative
|
||||
|
||||
D.py:693:5: D213 [*] Multi-line docstring summary should start at the second line
|
||||
|
|
||||
692 | def retain_extra_whitespace_deeper():
|
||||
693 | """Summary.
|
||||
| _____^
|
||||
694 | |
|
||||
695 | | This is overindented
|
||||
696 | | And so is this, but it we should preserve the extra space on this line relative
|
||||
697 | | to the one before
|
||||
698 | | And the relative indent here should be preserved too
|
||||
699 | | """
|
||||
| |_______^ D213
|
||||
700 |
|
||||
701 | def retain_extra_whitespace_followed_by_same_offset():
|
||||
|
|
||||
= help: Insert line break and indentation after opening quotes
|
||||
|
||||
ℹ Safe fix
|
||||
690 690 |
|
||||
691 691 |
|
||||
692 692 | def retain_extra_whitespace_deeper():
|
||||
693 |- """Summary.
|
||||
693 |+ """
|
||||
694 |+ Summary.
|
||||
694 695 |
|
||||
695 696 | This is overindented
|
||||
696 697 | And so is this, but it we should preserve the extra space on this line relative
|
||||
|
||||
D.py:702:5: D213 [*] Multi-line docstring summary should start at the second line
|
||||
|
|
||||
701 | def retain_extra_whitespace_followed_by_same_offset():
|
||||
702 | """Summary.
|
||||
| _____^
|
||||
703 | |
|
||||
704 | | This is overindented
|
||||
705 | | And so is this, but it we should preserve the extra space on this line relative
|
||||
706 | | This is overindented
|
||||
707 | | This is overindented
|
||||
708 | | """
|
||||
| |_______^ D213
|
||||
|
|
||||
= help: Insert line break and indentation after opening quotes
|
||||
|
||||
ℹ Safe fix
|
||||
699 699 | """
|
||||
700 700 |
|
||||
701 701 | def retain_extra_whitespace_followed_by_same_offset():
|
||||
702 |- """Summary.
|
||||
702 |+ """
|
||||
703 |+ Summary.
|
||||
703 704 |
|
||||
704 705 | This is overindented
|
||||
705 706 | And so is this, but it we should preserve the extra space on this line relative
|
||||
|
||||
D.py:712:5: D213 [*] Multi-line docstring summary should start at the second line
|
||||
|
|
||||
711 | def retain_extra_whitespace_not_overindented():
|
||||
712 | """Summary.
|
||||
| _____^
|
||||
713 | |
|
||||
714 | | This is not overindented
|
||||
715 | | This is overindented, but since one line is not overindented this should not raise
|
||||
716 | | And so is this, but it we should preserve the extra space on this line relative
|
||||
717 | | """
|
||||
| |_______^ D213
|
||||
|
|
||||
= help: Insert line break and indentation after opening quotes
|
||||
|
||||
ℹ Safe fix
|
||||
709 709 |
|
||||
710 710 |
|
||||
711 711 | def retain_extra_whitespace_not_overindented():
|
||||
712 |- """Summary.
|
||||
712 |+ """
|
||||
713 |+ Summary.
|
||||
713 714 |
|
||||
714 715 | This is not overindented
|
||||
715 716 | This is overindented, but since one line is not overindented this should not raise
|
||||
|
||||
|
||||
|
||||
@@ -326,5 +326,8 @@ D.py:664:5: D400 [*] First line should end with a period
|
||||
664 664 | "We enforce a newline after the closing quote for a multi-line docstring \
|
||||
665 |- but continuations shouldn't be considered multi-line"
|
||||
665 |+ but continuations shouldn't be considered multi-line."
|
||||
666 666 |
|
||||
667 667 |
|
||||
668 668 |
|
||||
|
||||
|
||||
|
||||
@@ -308,5 +308,8 @@ D.py:664:5: D415 [*] First line should end with a period, question mark, or excl
|
||||
664 664 | "We enforce a newline after the closing quote for a multi-line docstring \
|
||||
665 |- but continuations shouldn't be considered multi-line"
|
||||
665 |+ but continuations shouldn't be considered multi-line."
|
||||
666 666 |
|
||||
667 667 |
|
||||
668 668 |
|
||||
|
||||
|
||||
|
||||
@@ -196,5 +196,8 @@ D.py:664:5: D300 [*] Use triple double quotes `"""`
|
||||
665 |- but continuations shouldn't be considered multi-line"
|
||||
664 |+ """We enforce a newline after the closing quote for a multi-line docstring \
|
||||
665 |+ but continuations shouldn't be considered multi-line"""
|
||||
666 666 |
|
||||
667 667 |
|
||||
668 668 |
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
F821_21.py:4:1: F821 Undefined name `display`
|
||||
|
|
||||
3 | x = 1
|
||||
4 | display(x)
|
||||
| ^^^^^^^ F821
|
||||
|
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
|
||||
@@ -44,7 +44,7 @@ impl Violation for BadDunderMethodName {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let BadDunderMethodName { name } = self;
|
||||
format!("Bad or misspelled dunder method name `{name}`. (bad-dunder-name)")
|
||||
format!("Bad or misspelled dunder method name `{name}`")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn if_else_nested_if_else() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
if x == 0: # 3
|
||||
return
|
||||
else:
|
||||
@@ -195,19 +195,19 @@ else:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
"#;
|
||||
";
|
||||
test_helper(source, 4)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_else() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
for _ in range(x): # 2
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
"#;
|
||||
";
|
||||
|
||||
test_helper(source, 2)?;
|
||||
Ok(())
|
||||
@@ -215,14 +215,14 @@ else:
|
||||
|
||||
#[test]
|
||||
fn while_if_else_if() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
while x < 1: # 4
|
||||
if x:
|
||||
pass
|
||||
else:
|
||||
if x:
|
||||
pass
|
||||
"#;
|
||||
";
|
||||
|
||||
test_helper(source, 4)?;
|
||||
Ok(())
|
||||
@@ -230,7 +230,7 @@ else:
|
||||
|
||||
#[test]
|
||||
fn nested_def() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
if x: # 2
|
||||
pass
|
||||
else:
|
||||
@@ -241,7 +241,7 @@ def g(x):
|
||||
pass
|
||||
|
||||
return 1
|
||||
"#;
|
||||
";
|
||||
|
||||
test_helper(source, 2)?;
|
||||
Ok(())
|
||||
@@ -249,7 +249,7 @@ return 1
|
||||
|
||||
#[test]
|
||||
fn try_except_except_else_finally() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
try:
|
||||
pass
|
||||
except:
|
||||
@@ -260,7 +260,7 @@ else:
|
||||
pass
|
||||
finally:
|
||||
pass
|
||||
"#;
|
||||
";
|
||||
|
||||
test_helper(source, 5)?;
|
||||
Ok(())
|
||||
|
||||
@@ -9,8 +9,8 @@ use crate::checkers::ast::Checker;
|
||||
/// ## What it does
|
||||
/// Checks for classes with too many public methods
|
||||
///
|
||||
/// By default, this rule allows up to 20 statements, as configured by the
|
||||
/// [`pylint.max-public-methods`] option.
|
||||
/// By default, this rule allows up to 20 public methods, as configured by
|
||||
/// the [`pylint.max-public-methods`] option.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Classes with many public methods are harder to understand
|
||||
|
||||
@@ -110,7 +110,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn if_() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
x = 1
|
||||
if x == 1: # 9
|
||||
return
|
||||
@@ -130,7 +130,7 @@ if x == 8:
|
||||
return
|
||||
if x == 9:
|
||||
return
|
||||
"#;
|
||||
";
|
||||
|
||||
test_helper(source, 9)?;
|
||||
Ok(())
|
||||
@@ -138,12 +138,12 @@ if x == 9:
|
||||
|
||||
#[test]
|
||||
fn for_else() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
for _i in range(10):
|
||||
return
|
||||
else:
|
||||
return
|
||||
"#;
|
||||
";
|
||||
|
||||
test_helper(source, 2)?;
|
||||
Ok(())
|
||||
@@ -151,12 +151,12 @@ else:
|
||||
|
||||
#[test]
|
||||
fn async_for_else() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
async for _i in range(10):
|
||||
return
|
||||
else:
|
||||
return
|
||||
"#;
|
||||
";
|
||||
|
||||
test_helper(source, 2)?;
|
||||
Ok(())
|
||||
@@ -164,7 +164,7 @@ else:
|
||||
|
||||
#[test]
|
||||
fn nested_def_ignored() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
def f():
|
||||
return
|
||||
|
||||
@@ -173,7 +173,7 @@ if x == 1:
|
||||
print()
|
||||
else:
|
||||
print()
|
||||
"#;
|
||||
";
|
||||
|
||||
test_helper(source, 0)?;
|
||||
Ok(())
|
||||
@@ -181,7 +181,7 @@ else:
|
||||
|
||||
#[test]
|
||||
fn while_nested_if() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
x = 1
|
||||
while x < 10:
|
||||
print()
|
||||
@@ -189,7 +189,7 @@ while x < 10:
|
||||
return
|
||||
x += 1
|
||||
return
|
||||
"#;
|
||||
";
|
||||
|
||||
test_helper(source, 2)?;
|
||||
Ok(())
|
||||
@@ -197,12 +197,12 @@ return
|
||||
|
||||
#[test]
|
||||
fn with_if() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
with a as f:
|
||||
return
|
||||
if f == 1:
|
||||
return
|
||||
"#;
|
||||
";
|
||||
|
||||
test_helper(source, 2)?;
|
||||
Ok(())
|
||||
@@ -210,12 +210,12 @@ with a as f:
|
||||
|
||||
#[test]
|
||||
fn async_with_if() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
async with a as f:
|
||||
return
|
||||
if f == 1:
|
||||
return
|
||||
"#;
|
||||
";
|
||||
|
||||
test_helper(source, 2)?;
|
||||
Ok(())
|
||||
@@ -223,7 +223,7 @@ async with a as f:
|
||||
|
||||
#[test]
|
||||
fn try_except_except_else_finally() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
try:
|
||||
print()
|
||||
return
|
||||
@@ -235,7 +235,7 @@ else:
|
||||
return
|
||||
finally:
|
||||
return
|
||||
"#;
|
||||
";
|
||||
|
||||
test_helper(source, 5)?;
|
||||
Ok(())
|
||||
@@ -243,14 +243,14 @@ finally:
|
||||
|
||||
#[test]
|
||||
fn class_def_ignored() -> Result<()> {
|
||||
let source = r#"
|
||||
let source = r"
|
||||
class A:
|
||||
def f(self):
|
||||
return
|
||||
|
||||
def g(self):
|
||||
return
|
||||
"#;
|
||||
";
|
||||
|
||||
test_helper(source, 0)?;
|
||||
Ok(())
|
||||
|
||||
@@ -163,10 +163,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn pass() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
pass
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 2);
|
||||
Ok(())
|
||||
@@ -174,13 +174,13 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn if_else() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
if a:
|
||||
print()
|
||||
else:
|
||||
print()
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 5);
|
||||
Ok(())
|
||||
@@ -188,14 +188,14 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn if_else_if_corner() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
if a:
|
||||
print()
|
||||
else:
|
||||
if a:
|
||||
print()
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 6);
|
||||
Ok(())
|
||||
@@ -203,13 +203,13 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn if_elif() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
if a:
|
||||
print()
|
||||
elif a:
|
||||
print()
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 5);
|
||||
Ok(())
|
||||
@@ -217,7 +217,7 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn if_elif_else() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
if a:
|
||||
print()
|
||||
@@ -227,7 +227,7 @@ def f():
|
||||
print()
|
||||
else:
|
||||
print()
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 9);
|
||||
Ok(())
|
||||
@@ -235,7 +235,7 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn many_statements() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
async def f():
|
||||
a = 1
|
||||
b = 2
|
||||
@@ -256,7 +256,7 @@ async def f():
|
||||
a -= 1
|
||||
import time
|
||||
pass
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 19);
|
||||
Ok(())
|
||||
@@ -264,11 +264,11 @@ async def f():
|
||||
|
||||
#[test]
|
||||
fn for_() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
for i in range(10):
|
||||
pass
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 2);
|
||||
Ok(())
|
||||
@@ -276,13 +276,13 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn for_else() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
for i in range(10):
|
||||
print()
|
||||
else:
|
||||
print()
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 3);
|
||||
Ok(())
|
||||
@@ -290,14 +290,14 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn nested_def() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
def g():
|
||||
print()
|
||||
print()
|
||||
|
||||
print()
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 5);
|
||||
Ok(())
|
||||
@@ -305,7 +305,7 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn nested_class() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
class A:
|
||||
def __init__(self):
|
||||
@@ -315,7 +315,7 @@ def f():
|
||||
pass
|
||||
|
||||
print()
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 3);
|
||||
Ok(())
|
||||
@@ -323,10 +323,10 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn return_not_counted() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
return
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 1);
|
||||
Ok(())
|
||||
@@ -334,7 +334,7 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn with() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
with a:
|
||||
if a:
|
||||
@@ -342,7 +342,7 @@ def f():
|
||||
else:
|
||||
print()
|
||||
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 6);
|
||||
Ok(())
|
||||
@@ -350,13 +350,13 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn try_except() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
try:
|
||||
print()
|
||||
except Exception:
|
||||
raise
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 5);
|
||||
Ok(())
|
||||
@@ -364,7 +364,7 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn try_except_else() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
try:
|
||||
print()
|
||||
@@ -372,7 +372,7 @@ def f():
|
||||
pass
|
||||
else:
|
||||
print()
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 7);
|
||||
Ok(())
|
||||
@@ -380,7 +380,7 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn try_except_else_finally() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
try:
|
||||
print()
|
||||
@@ -390,7 +390,7 @@ def f():
|
||||
print()
|
||||
finally:
|
||||
pass
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 10);
|
||||
Ok(())
|
||||
@@ -398,7 +398,7 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn try_except_except() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
try:
|
||||
print()
|
||||
@@ -406,7 +406,7 @@ def f():
|
||||
pass
|
||||
except Exception:
|
||||
raise
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 8);
|
||||
Ok(())
|
||||
@@ -414,7 +414,7 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn try_except_except_finally() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
try:
|
||||
print()
|
||||
@@ -424,7 +424,7 @@ def f():
|
||||
pass
|
||||
finally:
|
||||
print()
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 11);
|
||||
Ok(())
|
||||
@@ -432,11 +432,11 @@ def f():
|
||||
|
||||
#[test]
|
||||
fn yield_() -> Result<()> {
|
||||
let source: &str = r#"
|
||||
let source: &str = r"
|
||||
def f():
|
||||
for i in range(10):
|
||||
yield i
|
||||
"#;
|
||||
";
|
||||
let stmts = parse_suite(source, "<filename>")?;
|
||||
assert_eq!(num_statements(&stmts), 2);
|
||||
Ok(())
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pylint/mod.rs
|
||||
---
|
||||
bad_dunder_method_name.py:5:9: PLW3201 Bad or misspelled dunder method name `_init_`. (bad-dunder-name)
|
||||
bad_dunder_method_name.py:5:9: PLW3201 Bad or misspelled dunder method name `_init_`
|
||||
|
|
||||
4 | class Apples:
|
||||
5 | def _init_(self): # [bad-dunder-name]
|
||||
@@ -9,7 +9,7 @@ bad_dunder_method_name.py:5:9: PLW3201 Bad or misspelled dunder method name `_in
|
||||
6 | pass
|
||||
|
|
||||
|
||||
bad_dunder_method_name.py:8:9: PLW3201 Bad or misspelled dunder method name `__hello__`. (bad-dunder-name)
|
||||
bad_dunder_method_name.py:8:9: PLW3201 Bad or misspelled dunder method name `__hello__`
|
||||
|
|
||||
6 | pass
|
||||
7 |
|
||||
@@ -18,7 +18,7 @@ bad_dunder_method_name.py:8:9: PLW3201 Bad or misspelled dunder method name `__h
|
||||
9 | print("hello")
|
||||
|
|
||||
|
||||
bad_dunder_method_name.py:11:9: PLW3201 Bad or misspelled dunder method name `__init_`. (bad-dunder-name)
|
||||
bad_dunder_method_name.py:11:9: PLW3201 Bad or misspelled dunder method name `__init_`
|
||||
|
|
||||
9 | print("hello")
|
||||
10 |
|
||||
@@ -28,7 +28,7 @@ bad_dunder_method_name.py:11:9: PLW3201 Bad or misspelled dunder method name `__
|
||||
13 | pass
|
||||
|
|
||||
|
||||
bad_dunder_method_name.py:15:9: PLW3201 Bad or misspelled dunder method name `_init_`. (bad-dunder-name)
|
||||
bad_dunder_method_name.py:15:9: PLW3201 Bad or misspelled dunder method name `_init_`
|
||||
|
|
||||
13 | pass
|
||||
14 |
|
||||
@@ -38,7 +38,7 @@ bad_dunder_method_name.py:15:9: PLW3201 Bad or misspelled dunder method name `_i
|
||||
17 | pass
|
||||
|
|
||||
|
||||
bad_dunder_method_name.py:19:9: PLW3201 Bad or misspelled dunder method name `___neg__`. (bad-dunder-name)
|
||||
bad_dunder_method_name.py:19:9: PLW3201 Bad or misspelled dunder method name `___neg__`
|
||||
|
|
||||
17 | pass
|
||||
18 |
|
||||
@@ -48,7 +48,7 @@ bad_dunder_method_name.py:19:9: PLW3201 Bad or misspelled dunder method name `__
|
||||
21 | pass
|
||||
|
|
||||
|
||||
bad_dunder_method_name.py:23:9: PLW3201 Bad or misspelled dunder method name `__inv__`. (bad-dunder-name)
|
||||
bad_dunder_method_name.py:23:9: PLW3201 Bad or misspelled dunder method name `__inv__`
|
||||
|
|
||||
21 | pass
|
||||
22 |
|
||||
|
||||
@@ -104,145 +104,145 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn once() {
|
||||
let source = r#"from foo import bar, baz, bop, qux as q"#;
|
||||
let expected = r#"from foo import bar, baz, qux as q"#;
|
||||
let source = r"from foo import bar, baz, bop, qux as q";
|
||||
let expected = r"from foo import bar, baz, qux as q";
|
||||
let actual = remove_import_members(source, &["bop"]);
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn twice() {
|
||||
let source = r#"from foo import bar, baz, bop, qux as q"#;
|
||||
let expected = r#"from foo import bar, qux as q"#;
|
||||
let source = r"from foo import bar, baz, bop, qux as q";
|
||||
let expected = r"from foo import bar, qux as q";
|
||||
let actual = remove_import_members(source, &["baz", "bop"]);
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aliased() {
|
||||
let source = r#"from foo import bar, baz, bop as boop, qux as q"#;
|
||||
let expected = r#"from foo import bar, baz, qux as q"#;
|
||||
let source = r"from foo import bar, baz, bop as boop, qux as q";
|
||||
let expected = r"from foo import bar, baz, qux as q";
|
||||
let actual = remove_import_members(source, &["bop"]);
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parenthesized() {
|
||||
let source = r#"from foo import (bar, baz, bop, qux as q)"#;
|
||||
let expected = r#"from foo import (bar, baz, qux as q)"#;
|
||||
let source = r"from foo import (bar, baz, bop, qux as q)";
|
||||
let expected = r"from foo import (bar, baz, qux as q)";
|
||||
let actual = remove_import_members(source, &["bop"]);
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn last_import() {
|
||||
let source = r#"from foo import bar, baz, bop, qux as q"#;
|
||||
let expected = r#"from foo import bar, baz, bop"#;
|
||||
let source = r"from foo import bar, baz, bop, qux as q";
|
||||
let expected = r"from foo import bar, baz, bop";
|
||||
let actual = remove_import_members(source, &["qux"]);
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_import() {
|
||||
let source = r#"from foo import bar, baz, bop, qux as q"#;
|
||||
let expected = r#"from foo import baz, bop, qux as q"#;
|
||||
let source = r"from foo import bar, baz, bop, qux as q";
|
||||
let expected = r"from foo import baz, bop, qux as q";
|
||||
let actual = remove_import_members(source, &["bar"]);
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_two_imports() {
|
||||
let source = r#"from foo import bar, baz, bop, qux as q"#;
|
||||
let expected = r#"from foo import bop, qux as q"#;
|
||||
let source = r"from foo import bar, baz, bop, qux as q";
|
||||
let expected = r"from foo import bop, qux as q";
|
||||
let actual = remove_import_members(source, &["bar", "baz"]);
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_two_imports_multiline() {
|
||||
let source = r#"from foo import (
|
||||
let source = r"from foo import (
|
||||
bar,
|
||||
baz,
|
||||
bop,
|
||||
qux as q
|
||||
)"#;
|
||||
let expected = r#"from foo import (
|
||||
)";
|
||||
let expected = r"from foo import (
|
||||
bop,
|
||||
qux as q
|
||||
)"#;
|
||||
)";
|
||||
let actual = remove_import_members(source, &["bar", "baz"]);
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiline_once() {
|
||||
let source = r#"from foo import (
|
||||
let source = r"from foo import (
|
||||
bar,
|
||||
baz,
|
||||
bop,
|
||||
qux as q,
|
||||
)"#;
|
||||
let expected = r#"from foo import (
|
||||
)";
|
||||
let expected = r"from foo import (
|
||||
bar,
|
||||
baz,
|
||||
qux as q,
|
||||
)"#;
|
||||
)";
|
||||
let actual = remove_import_members(source, &["bop"]);
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiline_twice() {
|
||||
let source = r#"from foo import (
|
||||
let source = r"from foo import (
|
||||
bar,
|
||||
baz,
|
||||
bop,
|
||||
qux as q,
|
||||
)"#;
|
||||
let expected = r#"from foo import (
|
||||
)";
|
||||
let expected = r"from foo import (
|
||||
bar,
|
||||
qux as q,
|
||||
)"#;
|
||||
)";
|
||||
let actual = remove_import_members(source, &["baz", "bop"]);
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiline_comment() {
|
||||
let source = r#"from foo import (
|
||||
let source = r"from foo import (
|
||||
bar,
|
||||
baz,
|
||||
# This comment should be removed.
|
||||
bop,
|
||||
# This comment should be retained.
|
||||
qux as q,
|
||||
)"#;
|
||||
let expected = r#"from foo import (
|
||||
)";
|
||||
let expected = r"from foo import (
|
||||
bar,
|
||||
baz,
|
||||
# This comment should be retained.
|
||||
qux as q,
|
||||
)"#;
|
||||
)";
|
||||
let actual = remove_import_members(source, &["bop"]);
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_comment_first_import() {
|
||||
let source = r#"from foo import (
|
||||
let source = r"from foo import (
|
||||
# This comment should be retained.
|
||||
bar,
|
||||
# This comment should be removed.
|
||||
baz,
|
||||
bop,
|
||||
qux as q,
|
||||
)"#;
|
||||
let expected = r#"from foo import (
|
||||
)";
|
||||
let expected = r"from foo import (
|
||||
# This comment should be retained.
|
||||
baz,
|
||||
bop,
|
||||
qux as q,
|
||||
)"#;
|
||||
)";
|
||||
let actual = remove_import_members(source, &["bar"]);
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
@@ -19,3 +19,16 @@ pub(super) fn curly_escape(text: &str) -> Cow<'_, str> {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
static DOUBLE_CURLY_BRACES: Lazy<Regex> = Lazy::new(|| Regex::new(r"((\{\{)|(\}\}))").unwrap());
|
||||
|
||||
pub(super) fn curly_unescape(text: &str) -> Cow<'_, str> {
|
||||
// Match all double curly braces and replace with a single
|
||||
DOUBLE_CURLY_BRACES.replace_all(text, |caps: &Captures| {
|
||||
if &caps[1] == "{{" {
|
||||
"{".to_string()
|
||||
} else {
|
||||
"}".to_string()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::checkers::ast::Checker;
|
||||
use crate::fix::edits::fits_or_shrinks;
|
||||
|
||||
use crate::rules::pyflakes::format::FormatSummary;
|
||||
use crate::rules::pyupgrade::helpers::curly_escape;
|
||||
use crate::rules::pyupgrade::helpers::{curly_escape, curly_unescape};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `str.format` calls that can be replaced with f-strings.
|
||||
@@ -357,9 +357,11 @@ pub(crate) fn f_strings(
|
||||
Some((Tok::String { .. }, range)) => {
|
||||
match try_convert_to_f_string(range, &mut summary, checker.locator()) {
|
||||
Ok(Some(fstring)) => patches.push((range, fstring)),
|
||||
// Skip any strings that don't require conversion (e.g., literal segments of an
|
||||
// implicit concatenation).
|
||||
Ok(None) => continue,
|
||||
// Convert escaped curly brackets e.g. `{{` to `{` in literal string parts
|
||||
Ok(None) => patches.push((
|
||||
range,
|
||||
curly_unescape(checker.locator().slice(range)).to_string(),
|
||||
)),
|
||||
// If any of the segments fail to convert, then we can't convert the entire
|
||||
// expression.
|
||||
Err(_) => return,
|
||||
@@ -384,7 +386,21 @@ pub(crate) fn f_strings(
|
||||
contents.push_str(&fstring);
|
||||
prev_end = range.end();
|
||||
}
|
||||
contents.push_str(checker.locator().slice(TextRange::new(prev_end, end)));
|
||||
|
||||
// If the remainder is non-empty, add it to the contents.
|
||||
let rest = checker.locator().slice(TextRange::new(prev_end, end));
|
||||
if !lexer::lex_starts_at(rest, Mode::Expression, prev_end)
|
||||
.flatten()
|
||||
.all(|(token, _)| match token {
|
||||
Tok::Comment(_) | Tok::Newline | Tok::NonLogicalNewline | Tok::Indent | Tok::Dedent => {
|
||||
true
|
||||
}
|
||||
Tok::String { value, .. } => value.is_empty(),
|
||||
_ => false,
|
||||
})
|
||||
{
|
||||
contents.push_str(rest);
|
||||
}
|
||||
|
||||
// If necessary, add a space between any leading keyword (`return`, `yield`, `assert`, etc.)
|
||||
// and the string. For example, `return"foo"` is valid, but `returnf"foo"` is not.
|
||||
|
||||
@@ -43,7 +43,7 @@ impl Violation for ReplaceStdoutStderr {
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
format!("Sending `stdout` and `stderr` to `PIPE` is deprecated, use `capture_output`")
|
||||
format!("Prefer `capture_output` over sending `stdout` and `stderr` to `PIPE`")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::ExprStringLiteral;
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use ruff_python_parser::lexer::LexResult;
|
||||
use ruff_python_parser::{StringKind, Tok};
|
||||
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for uses of the Unicode kind prefix (`u`) in strings.
|
||||
@@ -39,13 +40,19 @@ impl AlwaysFixableViolation for UnicodeKindPrefix {
|
||||
}
|
||||
|
||||
/// UP025
|
||||
pub(crate) fn unicode_kind_prefix(checker: &mut Checker, string: &ExprStringLiteral) {
|
||||
if string.unicode {
|
||||
let mut diagnostic = Diagnostic::new(UnicodeKindPrefix, string.range);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(TextRange::at(
|
||||
string.start(),
|
||||
TextSize::from(1),
|
||||
))));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
pub(crate) fn unicode_kind_prefix(diagnostics: &mut Vec<Diagnostic>, tokens: &[LexResult]) {
|
||||
for (token, range) in tokens.iter().flatten() {
|
||||
if let Tok::String {
|
||||
kind: StringKind::Unicode,
|
||||
..
|
||||
} = token
|
||||
{
|
||||
let mut diagnostic = Diagnostic::new(UnicodeKindPrefix, *range);
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(TextRange::at(
|
||||
range.start(),
|
||||
TextSize::from(1),
|
||||
))));
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
UP022.py:4:10: UP022 [*] Sending `stdout` and `stderr` to `PIPE` is deprecated, use `capture_output`
|
||||
UP022.py:4:10: UP022 [*] Prefer `capture_output` over sending `stdout` and `stderr` to `PIPE`
|
||||
|
|
||||
2 | import subprocess
|
||||
3 |
|
||||
@@ -22,7 +22,7 @@ UP022.py:4:10: UP022 [*] Sending `stdout` and `stderr` to `PIPE` is deprecated,
|
||||
6 6 | output = subprocess.run(["foo"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
7 7 |
|
||||
|
||||
UP022.py:6:10: UP022 [*] Sending `stdout` and `stderr` to `PIPE` is deprecated, use `capture_output`
|
||||
UP022.py:6:10: UP022 [*] Prefer `capture_output` over sending `stdout` and `stderr` to `PIPE`
|
||||
|
|
||||
4 | output = run(["foo"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
5 |
|
||||
@@ -43,7 +43,7 @@ UP022.py:6:10: UP022 [*] Sending `stdout` and `stderr` to `PIPE` is deprecated,
|
||||
8 8 | output = subprocess.run(stdout=subprocess.PIPE, args=["foo"], stderr=subprocess.PIPE)
|
||||
9 9 |
|
||||
|
||||
UP022.py:8:10: UP022 [*] Sending `stdout` and `stderr` to `PIPE` is deprecated, use `capture_output`
|
||||
UP022.py:8:10: UP022 [*] Prefer `capture_output` over sending `stdout` and `stderr` to `PIPE`
|
||||
|
|
||||
6 | output = subprocess.run(["foo"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
7 |
|
||||
@@ -64,7 +64,7 @@ UP022.py:8:10: UP022 [*] Sending `stdout` and `stderr` to `PIPE` is deprecated,
|
||||
10 10 | output = subprocess.run(
|
||||
11 11 | ["foo"], stdout=subprocess.PIPE, check=True, stderr=subprocess.PIPE
|
||||
|
||||
UP022.py:10:10: UP022 [*] Sending `stdout` and `stderr` to `PIPE` is deprecated, use `capture_output`
|
||||
UP022.py:10:10: UP022 [*] Prefer `capture_output` over sending `stdout` and `stderr` to `PIPE`
|
||||
|
|
||||
8 | output = subprocess.run(stdout=subprocess.PIPE, args=["foo"], stderr=subprocess.PIPE)
|
||||
9 |
|
||||
@@ -88,7 +88,7 @@ UP022.py:10:10: UP022 [*] Sending `stdout` and `stderr` to `PIPE` is deprecated,
|
||||
13 13 |
|
||||
14 14 | output = subprocess.run(
|
||||
|
||||
UP022.py:14:10: UP022 [*] Sending `stdout` and `stderr` to `PIPE` is deprecated, use `capture_output`
|
||||
UP022.py:14:10: UP022 [*] Prefer `capture_output` over sending `stdout` and `stderr` to `PIPE`
|
||||
|
|
||||
12 | )
|
||||
13 |
|
||||
@@ -112,7 +112,7 @@ UP022.py:14:10: UP022 [*] Sending `stdout` and `stderr` to `PIPE` is deprecated,
|
||||
17 17 |
|
||||
18 18 | output = subprocess.run(
|
||||
|
||||
UP022.py:18:10: UP022 [*] Sending `stdout` and `stderr` to `PIPE` is deprecated, use `capture_output`
|
||||
UP022.py:18:10: UP022 [*] Prefer `capture_output` over sending `stdout` and `stderr` to `PIPE`
|
||||
|
|
||||
16 | )
|
||||
17 |
|
||||
@@ -144,7 +144,7 @@ UP022.py:18:10: UP022 [*] Sending `stdout` and `stderr` to `PIPE` is deprecated,
|
||||
24 23 | encoding="utf-8",
|
||||
25 24 | close_fds=True,
|
||||
|
||||
UP022.py:29:14: UP022 [*] Sending `stdout` and `stderr` to `PIPE` is deprecated, use `capture_output`
|
||||
UP022.py:29:14: UP022 [*] Prefer `capture_output` over sending `stdout` and `stderr` to `PIPE`
|
||||
|
|
||||
28 | if output:
|
||||
29 | output = subprocess.run(
|
||||
@@ -174,7 +174,7 @@ UP022.py:29:14: UP022 [*] Sending `stdout` and `stderr` to `PIPE` is deprecated,
|
||||
35 34 | encoding="utf-8",
|
||||
36 35 | )
|
||||
|
||||
UP022.py:38:10: UP022 Sending `stdout` and `stderr` to `PIPE` is deprecated, use `capture_output`
|
||||
UP022.py:38:10: UP022 Prefer `capture_output` over sending `stdout` and `stderr` to `PIPE`
|
||||
|
|
||||
36 | )
|
||||
37 |
|
||||
@@ -188,7 +188,7 @@ UP022.py:38:10: UP022 Sending `stdout` and `stderr` to `PIPE` is deprecated, use
|
||||
|
|
||||
= help: Replace with `capture_output` keyword argument
|
||||
|
||||
UP022.py:42:10: UP022 Sending `stdout` and `stderr` to `PIPE` is deprecated, use `capture_output`
|
||||
UP022.py:42:10: UP022 Prefer `capture_output` over sending `stdout` and `stderr` to `PIPE`
|
||||
|
|
||||
40 | )
|
||||
41 |
|
||||
@@ -202,7 +202,7 @@ UP022.py:42:10: UP022 Sending `stdout` and `stderr` to `PIPE` is deprecated, use
|
||||
|
|
||||
= help: Replace with `capture_output` keyword argument
|
||||
|
||||
UP022.py:46:10: UP022 Sending `stdout` and `stderr` to `PIPE` is deprecated, use `capture_output`
|
||||
UP022.py:46:10: UP022 Prefer `capture_output` over sending `stdout` and `stderr` to `PIPE`
|
||||
|
|
||||
44 | )
|
||||
45 |
|
||||
|
||||
@@ -248,4 +248,37 @@ UP025.py:19:5: UP025 [*] Remove unicode literals from strings
|
||||
21 21 | # These should not change
|
||||
22 22 | u = "Hello"
|
||||
|
||||
UP025.py:29:7: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
27 | return"Hello"
|
||||
28 |
|
||||
29 | f"foo"u"bar"
|
||||
| ^^^^^^ UP025
|
||||
30 | f"foo" u"bar"
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
26 26 | def hello():
|
||||
27 27 | return"Hello"
|
||||
28 28 |
|
||||
29 |-f"foo"u"bar"
|
||||
29 |+f"foo""bar"
|
||||
30 30 | f"foo" u"bar"
|
||||
|
||||
UP025.py:30:8: UP025 [*] Remove unicode literals from strings
|
||||
|
|
||||
29 | f"foo"u"bar"
|
||||
30 | f"foo" u"bar"
|
||||
| ^^^^^^ UP025
|
||||
|
|
||||
= help: Remove unicode prefix
|
||||
|
||||
ℹ Safe fix
|
||||
27 27 | return"Hello"
|
||||
28 28 |
|
||||
29 29 | f"foo"u"bar"
|
||||
30 |-f"foo" u"bar"
|
||||
30 |+f"foo" "bar"
|
||||
|
||||
|
||||
|
||||
@@ -962,6 +962,8 @@ UP032_0.py:209:1: UP032 [*] Use f-string instead of `format` call
|
||||
208 | # existing line length, so it's fine.
|
||||
209 | "<Customer: {}, {}, {}, {}, {}>".format(self.internal_ids, self.external_ids, self.properties, self.tags, self.others)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP032
|
||||
210 |
|
||||
211 | # When fixing, trim the trailing empty string.
|
||||
|
|
||||
= help: Convert to f-string
|
||||
|
||||
@@ -971,5 +973,189 @@ UP032_0.py:209:1: UP032 [*] Use f-string instead of `format` call
|
||||
208 208 | # existing line length, so it's fine.
|
||||
209 |-"<Customer: {}, {}, {}, {}, {}>".format(self.internal_ids, self.external_ids, self.properties, self.tags, self.others)
|
||||
209 |+f"<Customer: {self.internal_ids}, {self.external_ids}, {self.properties}, {self.tags}, {self.others}>"
|
||||
210 210 |
|
||||
211 211 | # When fixing, trim the trailing empty string.
|
||||
212 212 | raise ValueError("Conflicting configuration dicts: {!r} {!r}"
|
||||
|
||||
UP032_0.py:212:18: UP032 [*] Use f-string instead of `format` call
|
||||
|
|
||||
211 | # When fixing, trim the trailing empty string.
|
||||
212 | raise ValueError("Conflicting configuration dicts: {!r} {!r}"
|
||||
| __________________^
|
||||
213 | | "".format(new_dict, d))
|
||||
| |_______________________________________^ UP032
|
||||
214 |
|
||||
215 | # When fixing, trim the trailing empty string.
|
||||
|
|
||||
= help: Convert to f-string
|
||||
|
||||
ℹ Safe fix
|
||||
209 209 | "<Customer: {}, {}, {}, {}, {}>".format(self.internal_ids, self.external_ids, self.properties, self.tags, self.others)
|
||||
210 210 |
|
||||
211 211 | # When fixing, trim the trailing empty string.
|
||||
212 |-raise ValueError("Conflicting configuration dicts: {!r} {!r}"
|
||||
213 |- "".format(new_dict, d))
|
||||
212 |+raise ValueError(f"Conflicting configuration dicts: {new_dict!r} {d!r}"
|
||||
213 |+ "")
|
||||
214 214 |
|
||||
215 215 | # When fixing, trim the trailing empty string.
|
||||
216 216 | raise ValueError("Conflicting configuration dicts: {!r} {!r}"
|
||||
|
||||
UP032_0.py:216:18: UP032 [*] Use f-string instead of `format` call
|
||||
|
|
||||
215 | # When fixing, trim the trailing empty string.
|
||||
216 | raise ValueError("Conflicting configuration dicts: {!r} {!r}"
|
||||
| __________________^
|
||||
217 | | .format(new_dict, d))
|
||||
| |_____________________________________^ UP032
|
||||
218 |
|
||||
219 | raise ValueError(
|
||||
|
|
||||
= help: Convert to f-string
|
||||
|
||||
ℹ Safe fix
|
||||
213 213 | "".format(new_dict, d))
|
||||
214 214 |
|
||||
215 215 | # When fixing, trim the trailing empty string.
|
||||
216 |-raise ValueError("Conflicting configuration dicts: {!r} {!r}"
|
||||
217 |- .format(new_dict, d))
|
||||
216 |+raise ValueError(f"Conflicting configuration dicts: {new_dict!r} {d!r}")
|
||||
218 217 |
|
||||
219 218 | raise ValueError(
|
||||
220 219 | "Conflicting configuration dicts: {!r} {!r}"
|
||||
|
||||
UP032_0.py:220:5: UP032 [*] Use f-string instead of `format` call
|
||||
|
|
||||
219 | raise ValueError(
|
||||
220 | "Conflicting configuration dicts: {!r} {!r}"
|
||||
| _____^
|
||||
221 | | "".format(new_dict, d)
|
||||
| |__________________________^ UP032
|
||||
222 | )
|
||||
|
|
||||
= help: Convert to f-string
|
||||
|
||||
ℹ Safe fix
|
||||
217 217 | .format(new_dict, d))
|
||||
218 218 |
|
||||
219 219 | raise ValueError(
|
||||
220 |- "Conflicting configuration dicts: {!r} {!r}"
|
||||
221 |- "".format(new_dict, d)
|
||||
220 |+ f"Conflicting configuration dicts: {new_dict!r} {d!r}"
|
||||
221 |+ ""
|
||||
222 222 | )
|
||||
223 223 |
|
||||
224 224 | raise ValueError(
|
||||
|
||||
UP032_0.py:225:5: UP032 [*] Use f-string instead of `format` call
|
||||
|
|
||||
224 | raise ValueError(
|
||||
225 | "Conflicting configuration dicts: {!r} {!r}"
|
||||
| _____^
|
||||
226 | | "".format(new_dict, d)
|
||||
| |__________________________^ UP032
|
||||
227 |
|
||||
228 | )
|
||||
|
|
||||
= help: Convert to f-string
|
||||
|
||||
ℹ Safe fix
|
||||
222 222 | )
|
||||
223 223 |
|
||||
224 224 | raise ValueError(
|
||||
225 |- "Conflicting configuration dicts: {!r} {!r}"
|
||||
226 |- "".format(new_dict, d)
|
||||
225 |+ f"Conflicting configuration dicts: {new_dict!r} {d!r}"
|
||||
226 |+ ""
|
||||
227 227 |
|
||||
228 228 | )
|
||||
229 229 |
|
||||
|
||||
UP032_0.py:231:1: UP032 [*] Use f-string instead of `format` call
|
||||
|
|
||||
230 | # The first string will be converted to an f-string and the curly braces in the second should be converted to be unescaped
|
||||
231 | / (
|
||||
232 | | "{}"
|
||||
233 | | "{{}}"
|
||||
234 | | ).format(a)
|
||||
| |___________^ UP032
|
||||
235 |
|
||||
236 | ("{}" "{{}}").format(a)
|
||||
|
|
||||
= help: Convert to f-string
|
||||
|
||||
ℹ Safe fix
|
||||
229 229 |
|
||||
230 230 | # The first string will be converted to an f-string and the curly braces in the second should be converted to be unescaped
|
||||
231 231 | (
|
||||
232 |+ f"{a}"
|
||||
232 233 | "{}"
|
||||
233 |- "{{}}"
|
||||
234 |-).format(a)
|
||||
234 |+)
|
||||
235 235 |
|
||||
236 236 | ("{}" "{{}}").format(a)
|
||||
237 237 |
|
||||
|
||||
UP032_0.py:236:1: UP032 [*] Use f-string instead of `format` call
|
||||
|
|
||||
234 | ).format(a)
|
||||
235 |
|
||||
236 | ("{}" "{{}}").format(a)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ UP032
|
||||
|
|
||||
= help: Convert to f-string
|
||||
|
||||
ℹ Safe fix
|
||||
233 233 | "{{}}"
|
||||
234 234 | ).format(a)
|
||||
235 235 |
|
||||
236 |-("{}" "{{}}").format(a)
|
||||
236 |+(f"{a}" "{}")
|
||||
237 237 |
|
||||
238 238 |
|
||||
239 239 | # Both strings will be converted to an f-string and the curly braces in the second should left escaped
|
||||
|
||||
UP032_0.py:240:1: UP032 [*] Use f-string instead of `format` call
|
||||
|
|
||||
239 | # Both strings will be converted to an f-string and the curly braces in the second should left escaped
|
||||
240 | / (
|
||||
241 | | "{}"
|
||||
242 | | "{{{}}}"
|
||||
243 | | ).format(a, b)
|
||||
| |______________^ UP032
|
||||
244 |
|
||||
245 | ("{}" "{{{}}}").format(a, b)
|
||||
|
|
||||
= help: Convert to f-string
|
||||
|
||||
ℹ Safe fix
|
||||
238 238 |
|
||||
239 239 | # Both strings will be converted to an f-string and the curly braces in the second should left escaped
|
||||
240 240 | (
|
||||
241 |- "{}"
|
||||
242 |- "{{{}}}"
|
||||
243 |-).format(a, b)
|
||||
241 |+ f"{a}"
|
||||
242 |+ f"{{{b}}}"
|
||||
243 |+)
|
||||
244 244 |
|
||||
245 245 | ("{}" "{{{}}}").format(a, b)
|
||||
|
||||
UP032_0.py:245:1: UP032 [*] Use f-string instead of `format` call
|
||||
|
|
||||
243 | ).format(a, b)
|
||||
244 |
|
||||
245 | ("{}" "{{{}}}").format(a, b)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP032
|
||||
|
|
||||
= help: Convert to f-string
|
||||
|
||||
ℹ Safe fix
|
||||
242 242 | "{{{}}}"
|
||||
243 243 | ).format(a, b)
|
||||
244 244 |
|
||||
245 |-("{}" "{{{}}}").format(a, b)
|
||||
245 |+(f"{a}" f"{{{b}}}")
|
||||
|
||||
|
||||
|
||||
@@ -18,9 +18,11 @@ mod tests {
|
||||
#[test_case(Rule::RepeatedAppend, Path::new("FURB113.py"))]
|
||||
#[test_case(Rule::DeleteFullSlice, Path::new("FURB131.py"))]
|
||||
#[test_case(Rule::CheckAndRemoveFromSet, Path::new("FURB132.py"))]
|
||||
#[test_case(Rule::IfExprMinMax, Path::new("FURB136.py"))]
|
||||
#[test_case(Rule::ReimplementedStarmap, Path::new("FURB140.py"))]
|
||||
#[test_case(Rule::SliceCopy, Path::new("FURB145.py"))]
|
||||
#[test_case(Rule::UnnecessaryEnumerate, Path::new("FURB148.py"))]
|
||||
#[test_case(Rule::MathConstant, Path::new("FURB152.py"))]
|
||||
#[test_case(Rule::PrintEmptyString, Path::new("FURB105.py"))]
|
||||
#[test_case(Rule::ImplicitCwd, Path::new("FURB177.py"))]
|
||||
#[test_case(Rule::SingleItemMembershipTest, Path::new("FURB171.py"))]
|
||||
|
||||
178
crates/ruff_linter/src/rules/refurb/rules/if_expr_min_max.rs
Normal file
178
crates/ruff_linter/src/rules/refurb/rules/if_expr_min_max.rs
Normal file
@@ -0,0 +1,178 @@
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::comparable::ComparableExpr;
|
||||
use ruff_python_ast::{self as ast, CmpOp, Expr};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::fix::snippet::SourceCodeSnippet;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for `if` expressions that can be replaced with `min()` or `max()`
|
||||
/// calls.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// An `if` expression that selects the lesser or greater of two
|
||||
/// sub-expressions can be replaced with a `min()` or `max()` call
|
||||
/// respectively. When possible, prefer `min()` and `max()`, as they're more
|
||||
/// concise and readable than the equivalent `if` expression.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// highest_score = score1 if score1 > score2 else score2
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// highest_score = max(score2, score1)
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `min`](https://docs.python.org/3.11/library/functions.html#min)
|
||||
/// - [Python documentation: `max`](https://docs.python.org/3.11/library/functions.html#max)
|
||||
#[violation]
|
||||
pub struct IfExprMinMax {
|
||||
min_max: MinMax,
|
||||
expression: SourceCodeSnippet,
|
||||
replacement: SourceCodeSnippet,
|
||||
}
|
||||
|
||||
impl Violation for IfExprMinMax {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let Self {
|
||||
min_max,
|
||||
expression,
|
||||
replacement,
|
||||
} = self;
|
||||
|
||||
match (expression.full_display(), replacement.full_display()) {
|
||||
(_, None) => {
|
||||
format!("Replace `if` expression with `{min_max}` call")
|
||||
}
|
||||
(None, Some(replacement)) => {
|
||||
format!("Replace `if` expression with `{replacement}`")
|
||||
}
|
||||
(Some(expression), Some(replacement)) => {
|
||||
format!("Replace `{expression}` with `{replacement}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
let Self {
|
||||
replacement,
|
||||
min_max,
|
||||
..
|
||||
} = self;
|
||||
if let Some(replacement) = replacement.full_display() {
|
||||
Some(format!("Replace with `{replacement}`"))
|
||||
} else {
|
||||
Some(format!("Replace with `{min_max}` call"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// FURB136
|
||||
pub(crate) fn if_expr_min_max(checker: &mut Checker, if_exp: &ast::ExprIfExp) {
|
||||
let Expr::Compare(ast::ExprCompare {
|
||||
left,
|
||||
ops,
|
||||
comparators,
|
||||
..
|
||||
}) = if_exp.test.as_ref()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Ignore, e.g., `foo < bar < baz`.
|
||||
let [op] = ops.as_slice() else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Determine whether to use `min()` or `max()`, and whether to flip the
|
||||
// order of the arguments, which is relevant for breaking ties.
|
||||
let (mut min_max, mut flip_args) = match op {
|
||||
CmpOp::Gt => (MinMax::Max, true),
|
||||
CmpOp::GtE => (MinMax::Max, false),
|
||||
CmpOp::Lt => (MinMax::Min, true),
|
||||
CmpOp::LtE => (MinMax::Min, false),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let [right] = comparators.as_slice() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let body_cmp = ComparableExpr::from(if_exp.body.as_ref());
|
||||
let orelse_cmp = ComparableExpr::from(if_exp.orelse.as_ref());
|
||||
let left_cmp = ComparableExpr::from(left);
|
||||
let right_cmp = ComparableExpr::from(right);
|
||||
|
||||
if body_cmp == right_cmp && orelse_cmp == left_cmp {
|
||||
min_max = min_max.reverse();
|
||||
flip_args = !flip_args;
|
||||
} else if body_cmp != left_cmp || orelse_cmp != right_cmp {
|
||||
return;
|
||||
}
|
||||
|
||||
let (arg1, arg2) = if flip_args {
|
||||
(right, left.as_ref())
|
||||
} else {
|
||||
(left.as_ref(), right)
|
||||
};
|
||||
|
||||
let replacement = format!(
|
||||
"{min_max}({}, {})",
|
||||
checker.generator().expr(arg1),
|
||||
checker.generator().expr(arg2),
|
||||
);
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
IfExprMinMax {
|
||||
min_max,
|
||||
expression: SourceCodeSnippet::from_str(checker.locator().slice(if_exp)),
|
||||
replacement: SourceCodeSnippet::from_str(replacement.as_str()),
|
||||
},
|
||||
if_exp.range(),
|
||||
);
|
||||
|
||||
if checker.semantic().is_builtin(min_max.as_str()) {
|
||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||
replacement,
|
||||
if_exp.range(),
|
||||
)));
|
||||
}
|
||||
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
enum MinMax {
|
||||
Min,
|
||||
Max,
|
||||
}
|
||||
|
||||
impl MinMax {
|
||||
fn reverse(self) -> Self {
|
||||
match self {
|
||||
Self::Min => Self::Max,
|
||||
Self::Max => Self::Min,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Min => "min",
|
||||
Self::Max => "max",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MinMax {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(fmt, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
90
crates/ruff_linter/src/rules/refurb/rules/math_constant.rs
Normal file
90
crates/ruff_linter/src/rules/refurb/rules/math_constant.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
use anyhow::Result;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast, Number};
|
||||
use ruff_text_size::Ranged;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::importer::ImportRequest;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for literals that are similar to constants in `math` module.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Hard-coding mathematical constants like π increases code duplication,
|
||||
/// reduces readability, and may lead to a lack of precision.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```python
|
||||
/// A = 3.141592 * r**2
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// A = math.pi * r**2
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `math` constants](https://docs.python.org/3/library/math.html#constants)
|
||||
#[violation]
|
||||
pub struct MathConstant {
|
||||
literal: String,
|
||||
constant: &'static str,
|
||||
}
|
||||
|
||||
impl Violation for MathConstant {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let MathConstant { literal, constant } = self;
|
||||
format!("Replace `{literal}` with `math.{constant}`")
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
let MathConstant { constant, .. } = self;
|
||||
Some(format!("Use `math.{constant}`"))
|
||||
}
|
||||
}
|
||||
|
||||
/// FURB152
|
||||
pub(crate) fn math_constant(checker: &mut Checker, literal: &ast::ExprNumberLiteral) {
|
||||
let Number::Float(value) = literal.value else {
|
||||
return;
|
||||
};
|
||||
for (real_value, constant) in [
|
||||
(std::f64::consts::PI, "pi"),
|
||||
(std::f64::consts::E, "e"),
|
||||
(std::f64::consts::TAU, "tau"),
|
||||
] {
|
||||
if (value - real_value).abs() < 1e-2 {
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
MathConstant {
|
||||
literal: checker.locator().slice(literal).into(),
|
||||
constant,
|
||||
},
|
||||
literal.range(),
|
||||
);
|
||||
diagnostic.try_set_fix(|| convert_to_constant(literal, constant, checker));
|
||||
checker.diagnostics.push(diagnostic);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to_constant(
|
||||
literal: &ast::ExprNumberLiteral,
|
||||
constant: &'static str,
|
||||
checker: &Checker,
|
||||
) -> Result<Fix> {
|
||||
let (edit, binding) = checker.importer().get_or_import_symbol(
|
||||
&ImportRequest::import("math", constant),
|
||||
literal.start(),
|
||||
checker.semantic(),
|
||||
)?;
|
||||
Ok(Fix::safe_edits(
|
||||
Edit::range_replacement(binding, literal.range()),
|
||||
[edit],
|
||||
))
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
pub(crate) use check_and_remove_from_set::*;
|
||||
pub(crate) use delete_full_slice::*;
|
||||
pub(crate) use if_expr_min_max::*;
|
||||
pub(crate) use implicit_cwd::*;
|
||||
pub(crate) use isinstance_type_none::*;
|
||||
pub(crate) use math_constant::*;
|
||||
pub(crate) use print_empty_string::*;
|
||||
pub(crate) use read_whole_file::*;
|
||||
pub(crate) use reimplemented_starmap::*;
|
||||
@@ -13,8 +15,10 @@ pub(crate) use unnecessary_enumerate::*;
|
||||
|
||||
mod check_and_remove_from_set;
|
||||
mod delete_full_slice;
|
||||
mod if_expr_min_max;
|
||||
mod implicit_cwd;
|
||||
mod isinstance_type_none;
|
||||
mod math_constant;
|
||||
mod print_empty_string;
|
||||
mod read_whole_file;
|
||||
mod reimplemented_starmap;
|
||||
|
||||
@@ -6,7 +6,7 @@ use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::{self as ast, Expr, Stmt};
|
||||
use ruff_python_codegen::Generator;
|
||||
use ruff_python_semantic::analyze::typing::is_list;
|
||||
use ruff_python_semantic::{Binding, BindingId, DefinitionId, SemanticModel};
|
||||
use ruff_python_semantic::{Binding, BindingId, SemanticModel};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
@@ -183,8 +183,7 @@ fn match_consecutive_appends<'a>(
|
||||
let siblings: &[Stmt] = if semantic.at_top_level() {
|
||||
// If the statement is at the top level, we should go to the parent module.
|
||||
// Module is available in the definitions list.
|
||||
let module = semantic.definitions[DefinitionId::module()].as_module()?;
|
||||
module.python_ast
|
||||
semantic.definitions.python_ast()?
|
||||
} else {
|
||||
// Otherwise, go to the parent, and take its body as a sequence of siblings.
|
||||
semantic
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/refurb/mod.rs
|
||||
---
|
||||
FURB136.py:4:1: FURB136 [*] Replace `x if x > y else y` with `max(y, x)`
|
||||
|
|
||||
2 | y = 2
|
||||
3 |
|
||||
4 | x if x > y else y # FURB136
|
||||
| ^^^^^^^^^^^^^^^^^ FURB136
|
||||
5 |
|
||||
6 | x if x >= y else y # FURB136
|
||||
|
|
||||
= help: Replace with `max(y, x)`
|
||||
|
||||
ℹ Safe fix
|
||||
1 1 | x = 1
|
||||
2 2 | y = 2
|
||||
3 3 |
|
||||
4 |-x if x > y else y # FURB136
|
||||
4 |+max(y, x) # FURB136
|
||||
5 5 |
|
||||
6 6 | x if x >= y else y # FURB136
|
||||
7 7 |
|
||||
|
||||
FURB136.py:6:1: FURB136 [*] Replace `x if x >= y else y` with `max(x, y)`
|
||||
|
|
||||
4 | x if x > y else y # FURB136
|
||||
5 |
|
||||
6 | x if x >= y else y # FURB136
|
||||
| ^^^^^^^^^^^^^^^^^^ FURB136
|
||||
7 |
|
||||
8 | x if x < y else y # FURB136
|
||||
|
|
||||
= help: Replace with `max(x, y)`
|
||||
|
||||
ℹ Safe fix
|
||||
3 3 |
|
||||
4 4 | x if x > y else y # FURB136
|
||||
5 5 |
|
||||
6 |-x if x >= y else y # FURB136
|
||||
6 |+max(x, y) # FURB136
|
||||
7 7 |
|
||||
8 8 | x if x < y else y # FURB136
|
||||
9 9 |
|
||||
|
||||
FURB136.py:8:1: FURB136 [*] Replace `x if x < y else y` with `min(y, x)`
|
||||
|
|
||||
6 | x if x >= y else y # FURB136
|
||||
7 |
|
||||
8 | x if x < y else y # FURB136
|
||||
| ^^^^^^^^^^^^^^^^^ FURB136
|
||||
9 |
|
||||
10 | x if x <= y else y # FURB136
|
||||
|
|
||||
= help: Replace with `min(y, x)`
|
||||
|
||||
ℹ Safe fix
|
||||
5 5 |
|
||||
6 6 | x if x >= y else y # FURB136
|
||||
7 7 |
|
||||
8 |-x if x < y else y # FURB136
|
||||
8 |+min(y, x) # FURB136
|
||||
9 9 |
|
||||
10 10 | x if x <= y else y # FURB136
|
||||
11 11 |
|
||||
|
||||
FURB136.py:10:1: FURB136 [*] Replace `x if x <= y else y` with `min(x, y)`
|
||||
|
|
||||
8 | x if x < y else y # FURB136
|
||||
9 |
|
||||
10 | x if x <= y else y # FURB136
|
||||
| ^^^^^^^^^^^^^^^^^^ FURB136
|
||||
11 |
|
||||
12 | y if x > y else x # FURB136
|
||||
|
|
||||
= help: Replace with `min(x, y)`
|
||||
|
||||
ℹ Safe fix
|
||||
7 7 |
|
||||
8 8 | x if x < y else y # FURB136
|
||||
9 9 |
|
||||
10 |-x if x <= y else y # FURB136
|
||||
10 |+min(x, y) # FURB136
|
||||
11 11 |
|
||||
12 12 | y if x > y else x # FURB136
|
||||
13 13 |
|
||||
|
||||
FURB136.py:12:1: FURB136 [*] Replace `y if x > y else x` with `min(x, y)`
|
||||
|
|
||||
10 | x if x <= y else y # FURB136
|
||||
11 |
|
||||
12 | y if x > y else x # FURB136
|
||||
| ^^^^^^^^^^^^^^^^^ FURB136
|
||||
13 |
|
||||
14 | y if x >= y else x # FURB136
|
||||
|
|
||||
= help: Replace with `min(x, y)`
|
||||
|
||||
ℹ Safe fix
|
||||
9 9 |
|
||||
10 10 | x if x <= y else y # FURB136
|
||||
11 11 |
|
||||
12 |-y if x > y else x # FURB136
|
||||
12 |+min(x, y) # FURB136
|
||||
13 13 |
|
||||
14 14 | y if x >= y else x # FURB136
|
||||
15 15 |
|
||||
|
||||
FURB136.py:14:1: FURB136 [*] Replace `y if x >= y else x` with `min(y, x)`
|
||||
|
|
||||
12 | y if x > y else x # FURB136
|
||||
13 |
|
||||
14 | y if x >= y else x # FURB136
|
||||
| ^^^^^^^^^^^^^^^^^^ FURB136
|
||||
15 |
|
||||
16 | y if x < y else x # FURB136
|
||||
|
|
||||
= help: Replace with `min(y, x)`
|
||||
|
||||
ℹ Safe fix
|
||||
11 11 |
|
||||
12 12 | y if x > y else x # FURB136
|
||||
13 13 |
|
||||
14 |-y if x >= y else x # FURB136
|
||||
14 |+min(y, x) # FURB136
|
||||
15 15 |
|
||||
16 16 | y if x < y else x # FURB136
|
||||
17 17 |
|
||||
|
||||
FURB136.py:16:1: FURB136 [*] Replace `y if x < y else x` with `max(x, y)`
|
||||
|
|
||||
14 | y if x >= y else x # FURB136
|
||||
15 |
|
||||
16 | y if x < y else x # FURB136
|
||||
| ^^^^^^^^^^^^^^^^^ FURB136
|
||||
17 |
|
||||
18 | y if x <= y else x # FURB136
|
||||
|
|
||||
= help: Replace with `max(x, y)`
|
||||
|
||||
ℹ Safe fix
|
||||
13 13 |
|
||||
14 14 | y if x >= y else x # FURB136
|
||||
15 15 |
|
||||
16 |-y if x < y else x # FURB136
|
||||
16 |+max(x, y) # FURB136
|
||||
17 17 |
|
||||
18 18 | y if x <= y else x # FURB136
|
||||
19 19 |
|
||||
|
||||
FURB136.py:18:1: FURB136 [*] Replace `y if x <= y else x` with `max(y, x)`
|
||||
|
|
||||
16 | y if x < y else x # FURB136
|
||||
17 |
|
||||
18 | y if x <= y else x # FURB136
|
||||
| ^^^^^^^^^^^^^^^^^^ FURB136
|
||||
19 |
|
||||
20 | x + y if x > y else y # OK
|
||||
|
|
||||
= help: Replace with `max(y, x)`
|
||||
|
||||
ℹ Safe fix
|
||||
15 15 |
|
||||
16 16 | y if x < y else x # FURB136
|
||||
17 17 |
|
||||
18 |-y if x <= y else x # FURB136
|
||||
18 |+max(y, x) # FURB136
|
||||
19 19 |
|
||||
20 20 | x + y if x > y else y # OK
|
||||
21 21 |
|
||||
|
||||
FURB136.py:22:1: FURB136 [*] Replace `if` expression with `max(y, x)`
|
||||
|
|
||||
20 | x + y if x > y else y # OK
|
||||
21 |
|
||||
22 | / x if (
|
||||
23 | | x
|
||||
24 | | > y
|
||||
25 | | ) else y # FURB136
|
||||
| |________^ FURB136
|
||||
|
|
||||
= help: Replace with `max(y, x)`
|
||||
|
||||
ℹ Safe fix
|
||||
19 19 |
|
||||
20 20 | x + y if x > y else y # OK
|
||||
21 21 |
|
||||
22 |-x if (
|
||||
23 |- x
|
||||
24 |- > y
|
||||
25 |-) else y # FURB136
|
||||
22 |+max(y, x) # FURB136
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
source: crates/ruff_linter/src/rules/refurb/mod.rs
|
||||
---
|
||||
FURB152.py:3:5: FURB152 [*] Replace `3.14` with `math.pi`
|
||||
|
|
||||
1 | r = 3.1 # OK
|
||||
2 |
|
||||
3 | A = 3.14 * r ** 2 # FURB152
|
||||
| ^^^^ FURB152
|
||||
4 |
|
||||
5 | C = 6.28 * r # FURB152
|
||||
|
|
||||
= help: Use `math.pi`
|
||||
|
||||
ℹ Safe fix
|
||||
1 |+import math
|
||||
1 2 | r = 3.1 # OK
|
||||
2 3 |
|
||||
3 |-A = 3.14 * r ** 2 # FURB152
|
||||
4 |+A = math.pi * r ** 2 # FURB152
|
||||
4 5 |
|
||||
5 6 | C = 6.28 * r # FURB152
|
||||
6 7 |
|
||||
|
||||
FURB152.py:5:5: FURB152 [*] Replace `6.28` with `math.tau`
|
||||
|
|
||||
3 | A = 3.14 * r ** 2 # FURB152
|
||||
4 |
|
||||
5 | C = 6.28 * r # FURB152
|
||||
| ^^^^ FURB152
|
||||
6 |
|
||||
7 | e = 2.71 # FURB152
|
||||
|
|
||||
= help: Use `math.tau`
|
||||
|
||||
ℹ Safe fix
|
||||
1 |+import math
|
||||
1 2 | r = 3.1 # OK
|
||||
2 3 |
|
||||
3 4 | A = 3.14 * r ** 2 # FURB152
|
||||
4 5 |
|
||||
5 |-C = 6.28 * r # FURB152
|
||||
6 |+C = math.tau * r # FURB152
|
||||
6 7 |
|
||||
7 8 | e = 2.71 # FURB152
|
||||
|
||||
FURB152.py:7:5: FURB152 [*] Replace `2.71` with `math.e`
|
||||
|
|
||||
5 | C = 6.28 * r # FURB152
|
||||
6 |
|
||||
7 | e = 2.71 # FURB152
|
||||
| ^^^^ FURB152
|
||||
|
|
||||
= help: Use `math.e`
|
||||
|
||||
ℹ Safe fix
|
||||
1 |+import math
|
||||
1 2 | r = 3.1 # OK
|
||||
2 3 |
|
||||
3 4 | A = 3.14 * r ** 2 # FURB152
|
||||
4 5 |
|
||||
5 6 | C = 6.28 * r # FURB152
|
||||
6 7 |
|
||||
7 |-e = 2.71 # FURB152
|
||||
8 |+e = math.e # FURB152
|
||||
|
||||
|
||||
@@ -1045,7 +1045,7 @@ mod tests {
|
||||
#[test_case("match.py")]
|
||||
fn control_flow_graph(filename: &str) {
|
||||
let path = PathBuf::from_iter(["resources/test/fixtures/control-flow-graph", filename]);
|
||||
let source = fs::read_to_string(&path).expect("failed to read file");
|
||||
let source = fs::read_to_string(path).expect("failed to read file");
|
||||
let stmts = parse(&source, Mode::Module, filename)
|
||||
.unwrap_or_else(|err| panic!("failed to parse source: '{source}': {err}"))
|
||||
.expect_module()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user