mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-20 04:29:47 +00:00
[flake8-logging-format] Avoid dropping implicitly concatenated pieces in the G004 fix (#20793)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / ty completion evaluation (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks instrumented (ruff) (push) Blocked by required conditions
CI / benchmarks instrumented (ty) (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / ty completion evaluation (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks instrumented (ruff) (push) Blocked by required conditions
CI / benchmarks instrumented (ty) (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
## Summary The original autofix for G004 was quietly dropping everything but the f-string components of any implicit concatenation sequence; this addresses that. Side note: It looks like `f_strings` is a bit risky to use (since it implicitly skips non-f-string parts); use iter and include implicitly concatenated pieces. We should consider if it's worth having (convenience vs. bit risky). ## Test Plan ``` cargo test -p ruff_linter ``` Backtest (run new testcases against previous implementation): ``` git checkout HEAD^ crates/ruff_linter/src/rules/flake8_logging_format/rules/logging_call.rs cargot test -p ruff_linter ```
This commit is contained in:
parent
8248193ed9
commit
66885e4bce
5 changed files with 139 additions and 27 deletions
8
crates/ruff_linter/resources/test/fixtures/flake8_logging_format/G004_implicit_concat.py
vendored
Normal file
8
crates/ruff_linter/resources/test/fixtures/flake8_logging_format/G004_implicit_concat.py
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import logging
|
||||
|
||||
variablename = "value"
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.info(f"a" f"b {variablename}")
|
||||
log.info("a " f"b {variablename}")
|
||||
log.info("prefix " f"middle {variablename}" f" suffix")
|
||||
|
|
@ -23,6 +23,7 @@ mod tests {
|
|||
#[test_case(Path::new("G003.py"))]
|
||||
#[test_case(Path::new("G004.py"))]
|
||||
#[test_case(Path::new("G004_arg_order.py"))]
|
||||
#[test_case(Path::new("G004_implicit_concat.py"))]
|
||||
#[test_case(Path::new("G010.py"))]
|
||||
#[test_case(Path::new("G101_1.py"))]
|
||||
#[test_case(Path::new("G101_2.py"))]
|
||||
|
|
@ -52,6 +53,7 @@ mod tests {
|
|||
|
||||
#[test_case(Rule::LoggingFString, Path::new("G004.py"))]
|
||||
#[test_case(Rule::LoggingFString, Path::new("G004_arg_order.py"))]
|
||||
#[test_case(Rule::LoggingFString, Path::new("G004_implicit_concat.py"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
"preview__{}_{}",
|
||||
|
|
|
|||
|
|
@ -42,38 +42,52 @@ fn logging_f_string(
|
|||
// Default to double quotes if we can't determine it.
|
||||
let quote_str = f_string
|
||||
.value
|
||||
.f_strings()
|
||||
.iter()
|
||||
.map(|part| match part {
|
||||
ast::FStringPart::Literal(literal) => literal.flags.quote_str(),
|
||||
ast::FStringPart::FString(f) => f.flags.quote_str(),
|
||||
})
|
||||
.next()
|
||||
.map(|f| f.flags.quote_str())
|
||||
.unwrap_or("\"");
|
||||
|
||||
for f in f_string.value.f_strings() {
|
||||
for element in &f.elements {
|
||||
match element {
|
||||
InterpolatedStringElement::Literal(lit) => {
|
||||
// If the literal text contains a '%' placeholder, bail out: mixing
|
||||
// f-string interpolation with '%' placeholders is ambiguous for our
|
||||
// automatic conversion, so don't offer a fix for this case.
|
||||
if lit.value.as_ref().contains('%') {
|
||||
return;
|
||||
}
|
||||
format_string.push_str(lit.value.as_ref());
|
||||
for part in &f_string.value {
|
||||
match part {
|
||||
ast::FStringPart::Literal(literal) => {
|
||||
let literal_text = literal.as_str();
|
||||
if literal_text.contains('%') {
|
||||
return;
|
||||
}
|
||||
InterpolatedStringElement::Interpolation(interpolated) => {
|
||||
if interpolated.format_spec.is_some()
|
||||
|| !matches!(
|
||||
interpolated.conversion,
|
||||
ruff_python_ast::ConversionFlag::None
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
match interpolated.expression.as_ref() {
|
||||
Expr::Name(name) => {
|
||||
format_string.push_str("%s");
|
||||
args.push(name.id.as_str());
|
||||
format_string.push_str(literal_text);
|
||||
}
|
||||
ast::FStringPart::FString(f) => {
|
||||
for element in &f.elements {
|
||||
match element {
|
||||
InterpolatedStringElement::Literal(lit) => {
|
||||
// If the literal text contains a '%' placeholder, bail out: mixing
|
||||
// f-string interpolation with '%' placeholders is ambiguous for our
|
||||
// automatic conversion, so don't offer a fix for this case.
|
||||
if lit.value.as_ref().contains('%') {
|
||||
return;
|
||||
}
|
||||
format_string.push_str(lit.value.as_ref());
|
||||
}
|
||||
InterpolatedStringElement::Interpolation(interpolated) => {
|
||||
if interpolated.format_spec.is_some()
|
||||
|| !matches!(
|
||||
interpolated.conversion,
|
||||
ruff_python_ast::ConversionFlag::None
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
match interpolated.expression.as_ref() {
|
||||
Expr::Name(name) => {
|
||||
format_string.push_str("%s");
|
||||
args.push(name.id.as_str());
|
||||
}
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_logging_format/mod.rs
|
||||
assertion_line: 50
|
||||
---
|
||||
G004 Logging statement uses f-string
|
||||
--> G004_implicit_concat.py:6:10
|
||||
|
|
||||
5 | log = logging.getLogger(__name__)
|
||||
6 | log.info(f"a" f"b {variablename}")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
7 | log.info("a " f"b {variablename}")
|
||||
8 | log.info("prefix " f"middle {variablename}" f" suffix")
|
||||
|
|
||||
help: Convert to lazy `%` formatting
|
||||
|
||||
G004 Logging statement uses f-string
|
||||
--> G004_implicit_concat.py:7:10
|
||||
|
|
||||
5 | log = logging.getLogger(__name__)
|
||||
6 | log.info(f"a" f"b {variablename}")
|
||||
7 | log.info("a " f"b {variablename}")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
8 | log.info("prefix " f"middle {variablename}" f" suffix")
|
||||
|
|
||||
help: Convert to lazy `%` formatting
|
||||
|
||||
G004 Logging statement uses f-string
|
||||
--> G004_implicit_concat.py:8:10
|
||||
|
|
||||
6 | log.info(f"a" f"b {variablename}")
|
||||
7 | log.info("a " f"b {variablename}")
|
||||
8 | log.info("prefix " f"middle {variablename}" f" suffix")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: Convert to lazy `%` formatting
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/flake8_logging_format/mod.rs
|
||||
assertion_line: 71
|
||||
---
|
||||
G004 [*] Logging statement uses f-string
|
||||
--> G004_implicit_concat.py:6:10
|
||||
|
|
||||
5 | log = logging.getLogger(__name__)
|
||||
6 | log.info(f"a" f"b {variablename}")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
7 | log.info("a " f"b {variablename}")
|
||||
8 | log.info("prefix " f"middle {variablename}" f" suffix")
|
||||
|
|
||||
help: Convert to lazy `%` formatting
|
||||
3 | variablename = "value"
|
||||
4 |
|
||||
5 | log = logging.getLogger(__name__)
|
||||
- log.info(f"a" f"b {variablename}")
|
||||
6 + log.info("ab %s", variablename)
|
||||
7 | log.info("a " f"b {variablename}")
|
||||
8 | log.info("prefix " f"middle {variablename}" f" suffix")
|
||||
|
||||
G004 [*] Logging statement uses f-string
|
||||
--> G004_implicit_concat.py:7:10
|
||||
|
|
||||
5 | log = logging.getLogger(__name__)
|
||||
6 | log.info(f"a" f"b {variablename}")
|
||||
7 | log.info("a " f"b {variablename}")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
8 | log.info("prefix " f"middle {variablename}" f" suffix")
|
||||
|
|
||||
help: Convert to lazy `%` formatting
|
||||
4 |
|
||||
5 | log = logging.getLogger(__name__)
|
||||
6 | log.info(f"a" f"b {variablename}")
|
||||
- log.info("a " f"b {variablename}")
|
||||
7 + log.info("a b %s", variablename)
|
||||
8 | log.info("prefix " f"middle {variablename}" f" suffix")
|
||||
|
||||
G004 [*] Logging statement uses f-string
|
||||
--> G004_implicit_concat.py:8:10
|
||||
|
|
||||
6 | log.info(f"a" f"b {variablename}")
|
||||
7 | log.info("a " f"b {variablename}")
|
||||
8 | log.info("prefix " f"middle {variablename}" f" suffix")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: Convert to lazy `%` formatting
|
||||
5 | log = logging.getLogger(__name__)
|
||||
6 | log.info(f"a" f"b {variablename}")
|
||||
7 | log.info("a " f"b {variablename}")
|
||||
- log.info("prefix " f"middle {variablename}" f" suffix")
|
||||
8 + log.info("prefix middle %s suffix", variablename)
|
||||
Loading…
Add table
Add a link
Reference in a new issue