Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

F541: Invalid replacement with string concatenation #4827

Closed
Tracked by #4972
addisoncrump opened this issue Jun 3, 2023 · 3 comments · Fixed by #5319
Closed
Tracked by #4972

F541: Invalid replacement with string concatenation #4827

addisoncrump opened this issue Jun 3, 2023 · 3 comments · Fixed by #5319
Assignees
Labels
bug Something isn't working

Comments

@addisoncrump
Copy link
Contributor

Python allows for string concatenation by placing two strings one after another:

>>> "5" "4"
'54'

It also permits a string followed by a f-string:

>>> "5" f"4"
'54'

Even those immediately concatenated:

>>> "5"f"4"
'54'

However, there is a niche corner case which causes Ruff to generate invalid syntax: if the first string is empty, followed by an f-string, it will incorrectly generate a triple-quote:

>>> ""f""
''
>>> ""f"4"
'4'
>>> """4"
... 
  File "<stdin>", line 1
    """4"
    ^
SyntaxError: unterminated triple-quoted string literal (detected at line 1)

The reproducing example for this is:

""f"4"

Which Ruff corrects to:

"""4"

Which is invalid syntax. It's worth noting that I don't think any linter currently supports this!

Discovered by #4822.

@zanieb
Copy link
Member

zanieb commented Jun 3, 2023

Should we automatically fix this case as ""f"4""4"?

Since this wasn't apparent to me at first, note that F541 is for f-strings with missing placeholders so if the second string has placeholders i.e. ""f"{x}" this bug does not apply. Perhaps it's a general bug in string concatenation cases though?

@addisoncrump
Copy link
Contributor Author

Yes, I suspect this is an issue with how strings are formatted -- not just for this case. However, this particular scenario is just very likely for a fuzzer to generate, where other string issues may not be so trivial to trigger.

@charliermarsh
Copy link
Member

We ran into something like this before, in the rule that transforms from strings to f-strings (rules/pyupgrade/rules/f_strings.rs):

// 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.
let existing = checker.locator.slice(TextRange::up_to(expr.start()));
if existing
    .chars()
    .last()
    .map_or(false, |char| char.is_ascii_alphabetic())
{
    contents.insert(0, ' ');
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants