From 0bcba4fba2f751861cbd21ee3983e1ee2f8a53c1 Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 30 Oct 2023 01:51:04 +0000 Subject: [PATCH] fix(linter) Fix panic on no useless escape (#1092) Closes #1041 --- .../src/rules/eslint/no_useless_escape.rs | 24 ++++++++++++------- .../src/snapshots/no_useless_escape.snap | 12 ++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs b/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs index 278441f33..4eedc4654 100644 --- a/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs +++ b/crates/oxc_linter/src/rules/eslint/no_useless_escape.rs @@ -19,6 +19,7 @@ pub struct NoUselessEscape; declare_oxc_lint!( /// ### What it does /// + /// Disallow unnecessary escape characters /// /// ### Why is this bad? /// @@ -135,15 +136,16 @@ fn check_string(string: &str) -> Vec { let mut offsets = vec![]; let quote_string = string.chars().next(); let mut in_escape = false; - let mut offset = 0; - for c in string[1..].chars() { - offset += c.len_utf8(); + let mut byte_offset = 0; + + for c in string.chars() { + byte_offset += c.len_utf8(); if in_escape { in_escape = false; match c { c if c.is_ascii_digit() || quote_string == Some(c) => { /* noop */ } c if !VALID_STRING_ESCAPES.contains(c) => { - offsets.push(offset); + offsets.push(byte_offset - c.len_utf8()); } _ => {} } @@ -162,27 +164,29 @@ fn check_template(string: &str) -> Vec { let mut offsets = vec![]; let mut in_escape = false; let mut prev_char = '`'; - let mut offset = 0; + let mut byte_offset = 1; let mut chars = string.chars().peekable(); + while let Some(c) = chars.next() { - offset += c.len_utf8(); + byte_offset += c.len_utf8(); + if in_escape { in_escape = false; match c { c if c.is_ascii_digit() || c == '`' => { /* noop */ } '{' => { if prev_char != '$' { - offsets.push(offset); + offsets.push(byte_offset - c.len_utf8()); } } '$' => { if chars.peek().is_some_and(|c| *c != '{') { - offsets.push(offset); + offsets.push(byte_offset - c.len_utf8()); } } c if !VALID_STRING_ESCAPES.contains(c) => { - offsets.push(offset); + offsets.push(byte_offset - c.len_utf8()); } _ => {} } @@ -356,6 +360,8 @@ fn test() { "`template literal with mixed linebreaks in line continuations \\\n\\\r\\\r\n\\and useless escape`", "`\\a```", r"var foo = /\(([^\)\(]+)\)$|\(([^\)\)]+)\)$/;", + r#"var stringLiteralWithNextLine = "line 1\…line 2";"#, + r"var stringLiteralWithNextLine = `line 1\…line 2`;", ]; Tester::new_without_config(NoUselessEscape::NAME, pass, fail).test_and_snapshot(); diff --git a/crates/oxc_linter/src/snapshots/no_useless_escape.snap b/crates/oxc_linter/src/snapshots/no_useless_escape.snap index ecb235b59..0c27346c5 100644 --- a/crates/oxc_linter/src/snapshots/no_useless_escape.snap +++ b/crates/oxc_linter/src/snapshots/no_useless_escape.snap @@ -374,4 +374,16 @@ expression: no_useless_escape · ── ╰──── + ⚠ eslint(no-useless-escape): Unnecessary escape character '\u{85}' + ╭─[no_useless_escape.tsx:1:1] + 1 │ var stringLiteralWithNextLine = "line 1\…line 2"; + · ─ + ╰──── + + ⚠ eslint(no-useless-escape): Unnecessary escape character '\u{85}' + ╭─[no_useless_escape.tsx:1:1] + 1 │ var stringLiteralWithNextLine = `line 1\…line 2`; + · ─ + ╰──── +