diff --git a/src/flake8/processor.py b/src/flake8/processor.py index 21a25e0f..29c5929c 100644 --- a/src/flake8/processor.py +++ b/src/flake8/processor.py @@ -203,9 +203,17 @@ def build_logical_line_tokens(self) -> _Logical: # noqa: C901 if token_type == tokenize.STRING: text = mutate_string(text) elif token_type == FSTRING_MIDDLE: # pragma: >=3.12 cover - text = "x" * len(text) + # A curly brace in an FSTRING_MIDDLE token must be an escaped + # curly brace. Both 'text' and 'end' will account for the + # escaped version of the token (i.e. a single brace) rather + # than the raw double brace version, so we must counteract this + fstring_offset = 0 + if "{" in text or "}" in text: + fstring_offset = text.count("{") + text.count("}") + text = "x" * (len(text) + fstring_offset) + end = (end[0], end[1] + fstring_offset) if previous_row: - (start_row, start_column) = start + start_row, start_column = start if previous_row != start_row: row_index = previous_row - 1 column_index = previous_column - 1 @@ -219,7 +227,7 @@ def build_logical_line_tokens(self) -> _Logical: # noqa: C901 logical.append(text) length += len(text) mapping.append((length, end)) - (previous_row, previous_column) = end + previous_row, previous_column = end return comments, logical, mapping def build_ast(self) -> ast.AST: diff --git a/tests/integration/test_plugins.py b/tests/integration/test_plugins.py index d4c22b0b..360f7559 100644 --- a/tests/integration/test_plugins.py +++ b/tests/integration/test_plugins.py @@ -248,7 +248,7 @@ def test_logical_line_plugin(tmpdir, capsys): cfg.write(cfg_s) src = """\ -f'hello world' +f'{{"{hello}": "{world}"}}' """ t_py = tmpdir.join("t.py") t_py.write_binary(src.encode()) @@ -257,7 +257,7 @@ def test_logical_line_plugin(tmpdir, capsys): assert main(("t.py", "--config", str(cfg))) == 1 expected = """\ -t.py:1:1: T001 "f'xxxxxxxxxxx'" +t.py:1:1: T001 "f'xxx{hello}xxxx{world}xxx'" """ out, err = capsys.readouterr() assert out == expected