From b3789cd9e96c26c173d0104ed717202a5851c67f Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sun, 31 Dec 2023 11:43:35 -0400 Subject: [PATCH] Fix continuation detection following multi-line strings (#9332) ## Summary The logic that detects continuations assumed that tokens themselves cannot span multiple lines. However, strings _can_ -- even single-quoted strings. Closes https://github.com/astral-sh/ruff/issues/9323. --- .../test/fixtures/pycodestyle/W293.py | 16 +++++ .../ruff_linter/src/rules/pycodestyle/mod.rs | 1 + ...les__pycodestyle__tests__W293_W293.py.snap | 63 +++++++++++++++++++ crates/ruff_python_index/src/indexer.rs | 13 +++- 4 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 crates/ruff_linter/resources/test/fixtures/pycodestyle/W293.py create mode 100644 crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__W293_W293.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/W293.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/W293.py new file mode 100644 index 0000000000000..3f0996a451dca --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/W293.py @@ -0,0 +1,16 @@ +# See: https://github.com/astral-sh/ruff/issues/9323 +class Chassis(RobotModuleTemplate): + """底盘信息推送控制 + + """\ + + +"""""" \ + \ + + + +"abc\ + " \ + \ + diff --git a/crates/ruff_linter/src/rules/pycodestyle/mod.rs b/crates/ruff_linter/src/rules/pycodestyle/mod.rs index 4c97f01c713e5..8475fde905e67 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/mod.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/mod.rs @@ -29,6 +29,7 @@ mod tests { #[test_case(Rule::LambdaAssignment, Path::new("E731.py"))] #[test_case(Rule::BareExcept, Path::new("E722.py"))] #[test_case(Rule::BlankLineWithWhitespace, Path::new("W29.py"))] + #[test_case(Rule::BlankLineWithWhitespace, Path::new("W293.py"))] #[test_case(Rule::InvalidEscapeSequence, Path::new("W605_0.py"))] #[test_case(Rule::InvalidEscapeSequence, Path::new("W605_1.py"))] #[test_case(Rule::LineTooLong, Path::new("E501.py"))] diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__W293_W293.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__W293_W293.py.snap new file mode 100644 index 0000000000000..5c949970f3119 --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__W293_W293.py.snap @@ -0,0 +1,63 @@ +--- +source: crates/ruff_linter/src/rules/pycodestyle/mod.rs +--- +W293.py:4:1: W293 [*] Blank line contains whitespace + | +2 | class Chassis(RobotModuleTemplate): +3 | """底盘信息推送控制 +4 | + | ^^^^ W293 +5 | """\ + | + = help: Remove whitespace from blank line + +ℹ Safe fix +1 1 | # See: https://github.com/astral-sh/ruff/issues/9323 +2 2 | class Chassis(RobotModuleTemplate): +3 3 | """底盘信息推送控制 +4 |- + 4 |+ +5 5 | """\ +6 6 | +7 7 | + +W293.py:10:1: W293 [*] Blank line contains whitespace + | + 8 | """""" \ + 9 | \ +10 | + | ^^^^ W293 + | + = help: Remove whitespace from blank line + +ℹ Safe fix +5 5 | """\ +6 6 | +7 7 | +8 |-"""""" \ +9 |- \ +10 |- + 8 |+"""""" +11 9 | +12 10 | +13 11 | "abc\ + +W293.py:16:1: W293 [*] Blank line contains whitespace + | +14 | " \ +15 | \ +16 | + | ^^^^ W293 + | + = help: Remove whitespace from blank line + +ℹ Safe fix +11 11 | +12 12 | +13 13 | "abc\ +14 |- " \ +15 |- \ +16 |- + 14 |+ " + + diff --git a/crates/ruff_python_index/src/indexer.rs b/crates/ruff_python_index/src/indexer.rs index 8e9e20919670c..27af11356b3dd 100644 --- a/crates/ruff_python_index/src/indexer.rs +++ b/crates/ruff_python_index/src/indexer.rs @@ -62,13 +62,22 @@ impl Indexer { comment_ranges_builder.visit_token(tok, *range); fstring_ranges_builder.visit_token(tok, *range); - if matches!(tok, Tok::Newline | Tok::NonLogicalNewline) { - line_start = range.end(); + match tok { + Tok::Newline | Tok::NonLogicalNewline => { + line_start = range.end(); + } + Tok::String { .. } => { + // If the previous token was a string, find the start of the line that contains + // the closing delimiter, since the token itself can span multiple lines. + line_start = locator.line_start(range.end()); + } + _ => {} } prev_token = Some(tok); prev_end = range.end(); } + Self { comment_ranges: comment_ranges_builder.finish(), continuation_lines,