Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/ruff/RUF037.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,8 @@ def f():
deque(b"abc") # OK
deque(f"" "a") # OK
deque(f"{x}" "") # OK

# https://github.com/astral-sh/ruff/issues/19951
deque(t"")
deque(t"" t"")
deque(t"{""}") # OK
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub(crate) fn unnecessary_literal_within_deque_call(checker: &Checker, deque: &a
Expr::StringLiteral(string) => string.value.is_empty(),
Expr::BytesLiteral(bytes) => bytes.value.is_empty(),
Expr::FString(fstring) => fstring.value.is_empty_literal(),
Expr::TString(tstring) => tstring.value.is_empty_iterable(),
_ => false,
};
if !is_empty_literal {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,3 +383,42 @@ help: Replace with `deque()`
101 101 | deque("abc") # OK
102 102 | deque(b"abc") # 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
22 changes: 21 additions & 1 deletion crates/ruff_python_ast/src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ impl FStringValue {

/// 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
/// f-string, not whether the f-string has 0 parts inside it.
pub fn is_empty_literal(&self) -> bool {
Expand Down Expand Up @@ -681,6 +681,22 @@ impl TStringValue {
pub fn elements(&self) -> impl Iterator<Item = &InterpolatedStringElement> {
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 {
Expand Down Expand Up @@ -1182,6 +1198,10 @@ impl TString {
pub fn quote_style(&self) -> Quote {
self.flags.quote_style()
}

pub fn is_empty(&self) -> bool {
self.elements.is_empty()
}
}

impl From<TString> for Expr {
Expand Down
Loading