mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-18 11:41:21 +00:00
[refurb] Detect empty f-strings (FURB105) (#21348)
## Summary
Fixes FURB105 (`print-empty-string`) to detect empty f-strings in
addition to regular empty strings. Previously, the rule only flagged
`print("")` but missed `print(f"")`. This fix ensures both cases are
detected and can be automatically fixed.
Fixes #21346
## Problem Analysis
The FURB105 rule checks for unnecessary empty strings passed to
`print()` calls. The `is_empty_string` helper function was only checking
for `Expr::StringLiteral` with empty values, but did not handle
`Expr::FString` (f-strings). As a result, `print(f"")` was not being
flagged as a violation, even though it's semantically equivalent to
`print("")` and should be simplified to `print()`.
The issue occurred because the function used a `matches!` macro that
only checked for string literals:
```rust
fn is_empty_string(expr: &Expr) -> bool {
matches!(
expr,
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) if value.is_empty()
)
}
```
## Approach
1. **Import the helper function**: Added `is_empty_f_string` to the
imports from `ruff_python_ast::helpers`, which already provides logic to
detect empty f-strings.
2. **Update `is_empty_string` function**: Changed the implementation
from a `matches!` macro to a `match` expression that handles both string
literals and f-strings:
```rust
fn is_empty_string(expr: &Expr) -> bool {
match expr {
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) =>
value.is_empty(),
Expr::FString(f_string) => is_empty_f_string(f_string),
_ => false,
}
}
```
The fix leverages the existing `is_empty_f_string` helper function which
properly handles the complexity of f-strings, including nested f-strings
and interpolated expressions. This ensures the detection is accurate and
consistent with how empty strings are detected elsewhere in the
codebase.
This commit is contained in:
parent
1d188476b6
commit
e4dc406a3d
3 changed files with 78 additions and 16 deletions
|
|
@ -19,6 +19,9 @@ print("", *args, sep="")
|
|||
print("", **kwargs)
|
||||
print(sep="\t")
|
||||
print(sep=print(1))
|
||||
print(f"")
|
||||
print(f"", sep=",")
|
||||
print(f"", end="bar")
|
||||
|
||||
# OK.
|
||||
|
||||
|
|
@ -33,3 +36,4 @@ print("foo", "", sep=",")
|
|||
print("foo", "", "bar", "", sep=",")
|
||||
print("", "", **kwargs)
|
||||
print(*args, sep=",")
|
||||
print(f"foo")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||
use ruff_python_ast::helpers::contains_effect;
|
||||
use ruff_python_ast::helpers::{contains_effect, is_empty_f_string};
|
||||
use ruff_python_ast::{self as ast, Expr};
|
||||
use ruff_python_codegen::Generator;
|
||||
use ruff_python_semantic::SemanticModel;
|
||||
|
|
@ -194,13 +194,11 @@ pub(crate) fn print_empty_string(checker: &Checker, call: &ast::ExprCall) {
|
|||
|
||||
/// Check if an expression is a constant empty string.
|
||||
fn is_empty_string(expr: &Expr) -> bool {
|
||||
matches!(
|
||||
expr,
|
||||
Expr::StringLiteral(ast::ExprStringLiteral {
|
||||
value,
|
||||
..
|
||||
}) if value.is_empty()
|
||||
)
|
||||
match expr {
|
||||
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value.is_empty(),
|
||||
Expr::FString(f_string) => is_empty_f_string(f_string),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@ help: Remove empty string
|
|||
19 + print(**kwargs)
|
||||
20 | print(sep="\t")
|
||||
21 | print(sep=print(1))
|
||||
22 |
|
||||
22 | print(f"")
|
||||
|
||||
FURB105 [*] Unnecessary separator passed to `print`
|
||||
--> FURB105.py:20:1
|
||||
|
|
@ -327,6 +327,7 @@ FURB105 [*] Unnecessary separator passed to `print`
|
|||
20 | print(sep="\t")
|
||||
| ^^^^^^^^^^^^^^^
|
||||
21 | print(sep=print(1))
|
||||
22 | print(f"")
|
||||
|
|
||||
help: Remove separator
|
||||
17 | print("", *args)
|
||||
|
|
@ -335,8 +336,8 @@ help: Remove separator
|
|||
- print(sep="\t")
|
||||
20 + print()
|
||||
21 | print(sep=print(1))
|
||||
22 |
|
||||
23 | # OK.
|
||||
22 | print(f"")
|
||||
23 | print(f"", sep=",")
|
||||
|
||||
FURB105 [*] Unnecessary separator passed to `print`
|
||||
--> FURB105.py:21:1
|
||||
|
|
@ -345,8 +346,8 @@ FURB105 [*] Unnecessary separator passed to `print`
|
|||
20 | print(sep="\t")
|
||||
21 | print(sep=print(1))
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
22 |
|
||||
23 | # OK.
|
||||
22 | print(f"")
|
||||
23 | print(f"", sep=",")
|
||||
|
|
||||
help: Remove separator
|
||||
18 | print("", *args, sep="")
|
||||
|
|
@ -354,7 +355,66 @@ help: Remove separator
|
|||
20 | print(sep="\t")
|
||||
- print(sep=print(1))
|
||||
21 + print()
|
||||
22 |
|
||||
23 | # OK.
|
||||
24 |
|
||||
22 | print(f"")
|
||||
23 | print(f"", sep=",")
|
||||
24 | print(f"", end="bar")
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
FURB105 [*] Unnecessary empty string passed to `print`
|
||||
--> FURB105.py:22:1
|
||||
|
|
||||
20 | print(sep="\t")
|
||||
21 | print(sep=print(1))
|
||||
22 | print(f"")
|
||||
| ^^^^^^^^^^
|
||||
23 | print(f"", sep=",")
|
||||
24 | print(f"", end="bar")
|
||||
|
|
||||
help: Remove empty string
|
||||
19 | print("", **kwargs)
|
||||
20 | print(sep="\t")
|
||||
21 | print(sep=print(1))
|
||||
- print(f"")
|
||||
22 + print()
|
||||
23 | print(f"", sep=",")
|
||||
24 | print(f"", end="bar")
|
||||
25 |
|
||||
|
||||
FURB105 [*] Unnecessary empty string and separator passed to `print`
|
||||
--> FURB105.py:23:1
|
||||
|
|
||||
21 | print(sep=print(1))
|
||||
22 | print(f"")
|
||||
23 | print(f"", sep=",")
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
24 | print(f"", end="bar")
|
||||
|
|
||||
help: Remove empty string and separator
|
||||
20 | print(sep="\t")
|
||||
21 | print(sep=print(1))
|
||||
22 | print(f"")
|
||||
- print(f"", sep=",")
|
||||
23 + print()
|
||||
24 | print(f"", end="bar")
|
||||
25 |
|
||||
26 | # OK.
|
||||
|
||||
FURB105 [*] Unnecessary empty string passed to `print`
|
||||
--> FURB105.py:24:1
|
||||
|
|
||||
22 | print(f"")
|
||||
23 | print(f"", sep=",")
|
||||
24 | print(f"", end="bar")
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
25 |
|
||||
26 | # OK.
|
||||
|
|
||||
help: Remove empty string
|
||||
21 | print(sep=print(1))
|
||||
22 | print(f"")
|
||||
23 | print(f"", sep=",")
|
||||
- print(f"", end="bar")
|
||||
24 + print(end="bar")
|
||||
25 |
|
||||
26 | # OK.
|
||||
27 |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue