Skip to content

Commit

Permalink
Trim trailing empty strings when converting to f-strings (#8712)
Browse files Browse the repository at this point in the history
## Summary

When converting from a `.format` call to an f-string, we can trim any
trailing empty tokens.

Closes #8683.
  • Loading branch information
charliermarsh authored Nov 16, 2023
1 parent a591725 commit 2424188
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 1 deletion.
19 changes: 19 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pyupgrade/UP032_0.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,22 @@ async def c():
# The fixed string will exceed the line length, but it's still smaller than the
# existing line length, so it's fine.
"<Customer: {}, {}, {}, {}, {}>".format(self.internal_ids, self.external_ids, self.properties, self.tags, self.others)

# When fixing, trim the trailing empty string.
raise ValueError("Conflicting configuration dicts: {!r} {!r}"
"".format(new_dict, d))

# When fixing, trim the trailing empty string.
raise ValueError("Conflicting configuration dicts: {!r} {!r}"
.format(new_dict, d))

raise ValueError(
"Conflicting configuration dicts: {!r} {!r}"
"".format(new_dict, d)
)

raise ValueError(
"Conflicting configuration dicts: {!r} {!r}"
"".format(new_dict, d)

)
16 changes: 15 additions & 1 deletion crates/ruff_linter/src/rules/pyupgrade/rules/f_strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,21 @@ pub(crate) fn f_strings(
contents.push_str(&fstring);
prev_end = range.end();
}
contents.push_str(checker.locator().slice(TextRange::new(prev_end, end)));

// If the remainder is non-empty, add it to the contents.
let rest = checker.locator().slice(TextRange::new(prev_end, end));
if !lexer::lex_starts_at(rest, Mode::Expression, prev_end)
.flatten()
.all(|(token, _)| match token {
Tok::Comment(_) | Tok::Newline | Tok::NonLogicalNewline | Tok::Indent | Tok::Dedent => {
true
}
Tok::String { value, .. } => value.is_empty(),
_ => false,
})
{
contents.push_str(rest);
}

// If necessary, add a space between any leading keyword (`return`, `yield`, `assert`, etc.)
// and the string. For example, `return"foo"` is valid, but `returnf"foo"` is not.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,8 @@ UP032_0.py:209:1: UP032 [*] Use f-string instead of `format` call
208 | # existing line length, so it's fine.
209 | "<Customer: {}, {}, {}, {}, {}>".format(self.internal_ids, self.external_ids, self.properties, self.tags, self.others)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP032
210 |
211 | # When fixing, trim the trailing empty string.
|
= help: Convert to f-string

Expand All @@ -971,5 +973,98 @@ UP032_0.py:209:1: UP032 [*] Use f-string instead of `format` call
208 208 | # existing line length, so it's fine.
209 |-"<Customer: {}, {}, {}, {}, {}>".format(self.internal_ids, self.external_ids, self.properties, self.tags, self.others)
209 |+f"<Customer: {self.internal_ids}, {self.external_ids}, {self.properties}, {self.tags}, {self.others}>"
210 210 |
211 211 | # When fixing, trim the trailing empty string.
212 212 | raise ValueError("Conflicting configuration dicts: {!r} {!r}"

UP032_0.py:212:18: UP032 [*] Use f-string instead of `format` call
|
211 | # When fixing, trim the trailing empty string.
212 | raise ValueError("Conflicting configuration dicts: {!r} {!r}"
| __________________^
213 | | "".format(new_dict, d))
| |_______________________________________^ UP032
214 |
215 | # When fixing, trim the trailing empty string.
|
= help: Convert to f-string

Safe fix
209 209 | "<Customer: {}, {}, {}, {}, {}>".format(self.internal_ids, self.external_ids, self.properties, self.tags, self.others)
210 210 |
211 211 | # When fixing, trim the trailing empty string.
212 |-raise ValueError("Conflicting configuration dicts: {!r} {!r}"
213 |- "".format(new_dict, d))
212 |+raise ValueError(f"Conflicting configuration dicts: {new_dict!r} {d!r}")
214 213 |
215 214 | # When fixing, trim the trailing empty string.
216 215 | raise ValueError("Conflicting configuration dicts: {!r} {!r}"

UP032_0.py:216:18: UP032 [*] Use f-string instead of `format` call
|
215 | # When fixing, trim the trailing empty string.
216 | raise ValueError("Conflicting configuration dicts: {!r} {!r}"
| __________________^
217 | | .format(new_dict, d))
| |_____________________________________^ UP032
218 |
219 | raise ValueError(
|
= help: Convert to f-string

Safe fix
213 213 | "".format(new_dict, d))
214 214 |
215 215 | # When fixing, trim the trailing empty string.
216 |-raise ValueError("Conflicting configuration dicts: {!r} {!r}"
217 |- .format(new_dict, d))
216 |+raise ValueError(f"Conflicting configuration dicts: {new_dict!r} {d!r}")
218 217 |
219 218 | raise ValueError(
220 219 | "Conflicting configuration dicts: {!r} {!r}"

UP032_0.py:220:5: UP032 [*] Use f-string instead of `format` call
|
219 | raise ValueError(
220 | "Conflicting configuration dicts: {!r} {!r}"
| _____^
221 | | "".format(new_dict, d)
| |__________________________^ UP032
222 | )
|
= help: Convert to f-string

Safe fix
217 217 | .format(new_dict, d))
218 218 |
219 219 | raise ValueError(
220 |- "Conflicting configuration dicts: {!r} {!r}"
221 |- "".format(new_dict, d)
220 |+ f"Conflicting configuration dicts: {new_dict!r} {d!r}"
222 221 | )
223 222 |
224 223 | raise ValueError(

UP032_0.py:225:5: UP032 [*] Use f-string instead of `format` call
|
224 | raise ValueError(
225 | "Conflicting configuration dicts: {!r} {!r}"
| _____^
226 | | "".format(new_dict, d)
| |__________________________^ UP032
227 |
228 | )
|
= help: Convert to f-string

Safe fix
222 222 | )
223 223 |
224 224 | raise ValueError(
225 |- "Conflicting configuration dicts: {!r} {!r}"
226 |- "".format(new_dict, d)
225 |+ f"Conflicting configuration dicts: {new_dict!r} {d!r}"
227 226 |
228 227 | )


0 comments on commit 2424188

Please sign in to comment.