Use ExprFString for StringLike::FString variant (#10311)
## Summary
This PR updates the `StringLike::FString` variant to use `ExprFString`
instead of `FStringLiteralElement`.
For context, the reason it used `FStringLiteralElement` is that the node
is actually the string part of an f-string ("foo" in `f"foo{x}"`). But,
this is inconsistent with other variants where the captured value is the
_entire_ string.
This is also problematic w.r.t. implicitly concatenated strings. Any
rules which work with `StringLike::FString` doesn't account for the
string part in an implicitly concatenated f-strings. For example, we
don't flag confusable character in the first part of `"𝐁ad" f"𝐁ad
string"`, but only the second part
(https://play.ruff.rs/16071c4c-a1dd-4920-b56f-e2ce2f69c843).
### Update `PYI053`
_This is included in this PR because otherwise it requires a temporary
workaround to be compatible with the old logic._
This PR also updates the `PYI053` (`string-or-bytes-too-long`) rule for
f-string to consider _all_ the visible characters in a f-string,
including the ones which are implicitly concatenated. This is consistent
with implicitly concatenated strings and bytes.
For example,
```python
def foo(
# We count all the characters here
arg1: str = '51 character ' 'stringgggggggggggggggggggggggggggggggg',
# But not here because of the `{x}` replacement field which _breaks_ them up into two chunks
arg2: str = f'51 character {x} stringgggggggggggggggggggggggggggggggggggggggggggg',
) -> None: ...
```
This PR fixes it to consider all _visible_ characters inside an f-string
which includes expressions as well.
fixes: #10310
fixes: #10307
## Test Plan
Add new test cases and update the snapshots.
## Review
To facilitate the review process, the change have been split into two
commits: one which has the code change while the other has the test
cases and updated snapshots.
This commit is contained in:
@@ -399,35 +399,35 @@ impl LiteralExpressionRef<'_> {
|
||||
/// f-strings.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum StringLike<'a> {
|
||||
StringLiteral(&'a ast::ExprStringLiteral),
|
||||
BytesLiteral(&'a ast::ExprBytesLiteral),
|
||||
FStringLiteral(&'a ast::FStringLiteralElement),
|
||||
String(&'a ast::ExprStringLiteral),
|
||||
Bytes(&'a ast::ExprBytesLiteral),
|
||||
FString(&'a ast::ExprFString),
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ast::ExprStringLiteral> for StringLike<'a> {
|
||||
fn from(value: &'a ast::ExprStringLiteral) -> Self {
|
||||
StringLike::StringLiteral(value)
|
||||
StringLike::String(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ast::ExprBytesLiteral> for StringLike<'a> {
|
||||
fn from(value: &'a ast::ExprBytesLiteral) -> Self {
|
||||
StringLike::BytesLiteral(value)
|
||||
StringLike::Bytes(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ast::FStringLiteralElement> for StringLike<'a> {
|
||||
fn from(value: &'a ast::FStringLiteralElement) -> Self {
|
||||
StringLike::FStringLiteral(value)
|
||||
impl<'a> From<&'a ast::ExprFString> for StringLike<'a> {
|
||||
fn from(value: &'a ast::ExprFString) -> Self {
|
||||
StringLike::FString(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ranged for StringLike<'_> {
|
||||
fn range(&self) -> TextRange {
|
||||
match self {
|
||||
StringLike::StringLiteral(literal) => literal.range(),
|
||||
StringLike::BytesLiteral(literal) => literal.range(),
|
||||
StringLike::FStringLiteral(literal) => literal.range(),
|
||||
StringLike::String(literal) => literal.range(),
|
||||
StringLike::Bytes(literal) => literal.range(),
|
||||
StringLike::FString(literal) => literal.range(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user