diff --git a/crates/ruff/resources/test/fixtures/pylint/invalid_characters.py b/crates/ruff/resources/test/fixtures/pylint/invalid_characters.py index ecc681b112..6233ed9353 100644 Binary files a/crates/ruff/resources/test/fixtures/pylint/invalid_characters.py and b/crates/ruff/resources/test/fixtures/pylint/invalid_characters.py differ diff --git a/crates/ruff/src/rules/pylint/rules/invalid_string_characters.rs b/crates/ruff/src/rules/pylint/rules/invalid_string_characters.rs index 8eac9f2621..3b05fedb1a 100644 --- a/crates/ruff/src/rules/pylint/rules/invalid_string_characters.rs +++ b/crates/ruff/src/rules/pylint/rules/invalid_string_characters.rs @@ -184,16 +184,20 @@ pub fn invalid_string_characters( let text = locator.slice(Range::new(start, end)); for (row, line) in UniversalNewlineIterator::from(text).enumerate() { - for (column, match_) in line.match_indices(&['\x08', '\x1A', '\x1B', '\0', '\u{200b}']) { - let (replacement, rule): (&str, DiagnosticKind) = match match_.chars().next().unwrap() { + let mut char_offset = 0; + for char in line.chars() { + let (replacement, rule): (&str, DiagnosticKind) = match char { '\x08' => ("\\b", InvalidCharacterBackspace.into()), '\x1A' => ("\\x1A", InvalidCharacterSub.into()), '\x1B' => ("\\x1B", InvalidCharacterEsc.into()), '\0' => ("\\0", InvalidCharacterNul.into()), '\u{200b}' => ("\\u200b", InvalidCharacterZeroWidthSpace.into()), - _ => unreachable!(), + _ => { + char_offset += 1; + continue; + } }; - let location = helpers::to_absolute(Location::new(row + 1, column), start); + let location = helpers::to_absolute(Location::new(row + 1, char_offset), start); let end_location = Location::new(location.row(), location.column() + 1); let mut diagnostic = Diagnostic::new(rule, Range::new(location, end_location)); if autofix { @@ -204,6 +208,7 @@ pub fn invalid_string_characters( )); } diagnostics.push(diagnostic); + char_offset += 1; } } diff --git a/crates/ruff/src/rules/pylint/snapshots/ruff__rules__pylint__tests__PLE2515_invalid_characters.py.snap b/crates/ruff/src/rules/pylint/snapshots/ruff__rules__pylint__tests__PLE2515_invalid_characters.py.snap index 4bc2993940..272ae53441 100644 --- a/crates/ruff/src/rules/pylint/snapshots/ruff__rules__pylint__tests__PLE2515_invalid_characters.py.snap +++ b/crates/ruff/src/rules/pylint/snapshots/ruff__rules__pylint__tests__PLE2515_invalid_characters.py.snap @@ -23,4 +23,67 @@ expression: diagnostics row: 34 column: 13 parent: ~ +- kind: + name: InvalidCharacterZeroWidthSpace + body: "Invalid unescaped character zero-width-space, use \"\\u200B\" instead" + suggestion: Replace with escape sequence + fixable: true + location: + row: 38 + column: 35 + end_location: + row: 38 + column: 36 + fix: + edits: + - content: "\\u200b" + location: + row: 38 + column: 35 + end_location: + row: 38 + column: 36 + parent: ~ +- kind: + name: InvalidCharacterZeroWidthSpace + body: "Invalid unescaped character zero-width-space, use \"\\u200B\" instead" + suggestion: Replace with escape sequence + fixable: true + location: + row: 39 + column: 59 + end_location: + row: 39 + column: 60 + fix: + edits: + - content: "\\u200b" + location: + row: 39 + column: 59 + end_location: + row: 39 + column: 60 + parent: ~ +- kind: + name: InvalidCharacterZeroWidthSpace + body: "Invalid unescaped character zero-width-space, use \"\\u200B\" instead" + suggestion: Replace with escape sequence + fixable: true + location: + row: 39 + column: 60 + end_location: + row: 39 + column: 61 + fix: + edits: + - content: "\\u200b" + location: + row: 39 + column: 60 + end_location: + row: 39 + column: 61 + parent: ~