mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 06:41:23 +00:00
[ruff
] Handle empty t-strings in unnecessary-empty-iterable-within-deque-call
(RUF037
) (#20045)
Adds a method to `TStringValue` to detect whether the t-string is empty _as an iterable_. Note the subtlety here that, unlike f-strings, an empty t-string is still truthy (i.e. `bool(t"")==True`). Closes #19951
This commit is contained in:
parent
0e9d77e43a
commit
0b6ce1c788
4 changed files with 66 additions and 1 deletions
|
@ -102,3 +102,8 @@ deque("abc") # OK
|
||||||
deque(b"abc") # OK
|
deque(b"abc") # OK
|
||||||
deque(f"" "a") # OK
|
deque(f"" "a") # OK
|
||||||
deque(f"{x}" "") # OK
|
deque(f"{x}" "") # OK
|
||||||
|
|
||||||
|
# https://github.com/astral-sh/ruff/issues/19951
|
||||||
|
deque(t"")
|
||||||
|
deque(t"" t"")
|
||||||
|
deque(t"{""}") # OK
|
||||||
|
|
|
@ -103,6 +103,7 @@ pub(crate) fn unnecessary_literal_within_deque_call(checker: &Checker, deque: &a
|
||||||
Expr::StringLiteral(string) => string.value.is_empty(),
|
Expr::StringLiteral(string) => string.value.is_empty(),
|
||||||
Expr::BytesLiteral(bytes) => bytes.value.is_empty(),
|
Expr::BytesLiteral(bytes) => bytes.value.is_empty(),
|
||||||
Expr::FString(fstring) => fstring.value.is_empty_literal(),
|
Expr::FString(fstring) => fstring.value.is_empty_literal(),
|
||||||
|
Expr::TString(tstring) => tstring.value.is_empty_iterable(),
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
if !is_empty_literal {
|
if !is_empty_literal {
|
||||||
|
|
|
@ -383,3 +383,42 @@ help: Replace with `deque()`
|
||||||
101 101 | deque("abc") # OK
|
101 101 | deque("abc") # OK
|
||||||
102 102 | deque(b"abc") # OK
|
102 102 | deque(b"abc") # OK
|
||||||
103 103 | deque(f"" "a") # OK
|
103 103 | deque(f"" "a") # OK
|
||||||
|
|
||||||
|
RUF037 [*] Unnecessary empty iterable within a deque call
|
||||||
|
--> RUF037.py:107:1
|
||||||
|
|
|
||||||
|
106 | # https://github.com/astral-sh/ruff/issues/19951
|
||||||
|
107 | deque(t"")
|
||||||
|
| ^^^^^^^^^^
|
||||||
|
108 | deque(t"" t"")
|
||||||
|
109 | deque(t"{""}") # OK
|
||||||
|
|
|
||||||
|
help: Replace with `deque()`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
104 104 | deque(f"{x}" "") # OK
|
||||||
|
105 105 |
|
||||||
|
106 106 | # https://github.com/astral-sh/ruff/issues/19951
|
||||||
|
107 |-deque(t"")
|
||||||
|
107 |+deque()
|
||||||
|
108 108 | deque(t"" t"")
|
||||||
|
109 109 | deque(t"{""}") # OK
|
||||||
|
|
||||||
|
RUF037 [*] Unnecessary empty iterable within a deque call
|
||||||
|
--> RUF037.py:108:1
|
||||||
|
|
|
||||||
|
106 | # https://github.com/astral-sh/ruff/issues/19951
|
||||||
|
107 | deque(t"")
|
||||||
|
108 | deque(t"" t"")
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
109 | deque(t"{""}") # OK
|
||||||
|
|
|
||||||
|
help: Replace with `deque()`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
105 105 |
|
||||||
|
106 106 | # https://github.com/astral-sh/ruff/issues/19951
|
||||||
|
107 107 | deque(t"")
|
||||||
|
108 |-deque(t"" t"")
|
||||||
|
108 |+deque()
|
||||||
|
109 109 | deque(t"{""}") # OK
|
||||||
|
|
|
@ -515,7 +515,7 @@ impl FStringValue {
|
||||||
|
|
||||||
/// Returns `true` if the node represents an empty f-string literal.
|
/// Returns `true` if the node represents an empty f-string literal.
|
||||||
///
|
///
|
||||||
/// Noteh that a [`FStringValue`] node will always have >= 1 [`FStringPart`]s inside it.
|
/// Note that a [`FStringValue`] node will always have >= 1 [`FStringPart`]s inside it.
|
||||||
/// This method checks whether the value of the concatenated parts is equal to the empty
|
/// This method checks whether the value of the concatenated parts is equal to the empty
|
||||||
/// f-string, not whether the f-string has 0 parts inside it.
|
/// f-string, not whether the f-string has 0 parts inside it.
|
||||||
pub fn is_empty_literal(&self) -> bool {
|
pub fn is_empty_literal(&self) -> bool {
|
||||||
|
@ -681,6 +681,22 @@ impl TStringValue {
|
||||||
pub fn elements(&self) -> impl Iterator<Item = &InterpolatedStringElement> {
|
pub fn elements(&self) -> impl Iterator<Item = &InterpolatedStringElement> {
|
||||||
self.iter().flat_map(|tstring| tstring.elements.iter())
|
self.iter().flat_map(|tstring| tstring.elements.iter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the node represents an empty t-string in the
|
||||||
|
/// sense that `__iter__` returns an empty iterable.
|
||||||
|
///
|
||||||
|
/// Beware that empty t-strings are still truthy, i.e. `bool(t"") == True`.
|
||||||
|
///
|
||||||
|
/// Note that a [`TStringValue`] node will always contain at least one
|
||||||
|
/// [`TString`] node. This method checks whether each of the constituent
|
||||||
|
/// t-strings (in an implicitly concatenated t-string) are empty
|
||||||
|
/// in the above sense.
|
||||||
|
pub fn is_empty_iterable(&self) -> bool {
|
||||||
|
match &self.inner {
|
||||||
|
TStringValueInner::Single(tstring) => tstring.is_empty(),
|
||||||
|
TStringValueInner::Concatenated(tstrings) => tstrings.iter().all(TString::is_empty),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a TStringValue {
|
impl<'a> IntoIterator for &'a TStringValue {
|
||||||
|
@ -1182,6 +1198,10 @@ impl TString {
|
||||||
pub fn quote_style(&self) -> Quote {
|
pub fn quote_style(&self) -> Quote {
|
||||||
self.flags.quote_style()
|
self.flags.quote_style()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.elements.is_empty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TString> for Expr {
|
impl From<TString> for Expr {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue