diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index d18bf0547..c0c53860c 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -76,6 +76,7 @@ mod eslint { pub mod no_irregular_whitespace; pub mod no_iterator; pub mod no_loss_of_precision; + pub mod no_multi_str; pub mod no_new; pub mod no_new_native_nonconstructor; pub mod no_new_wrappers; @@ -449,6 +450,7 @@ oxc_macros::declare_all_lint_rules! { eslint::no_caller, eslint::no_case_declarations, eslint::no_class_assign, + eslint::no_multi_str, eslint::require_await, eslint::no_compare_neg_zero, eslint::no_cond_assign, diff --git a/crates/oxc_linter/src/rules/eslint/no_multi_str.rs b/crates/oxc_linter/src/rules/eslint/no_multi_str.rs new file mode 100644 index 000000000..ba501bc8b --- /dev/null +++ b/crates/oxc_linter/src/rules/eslint/no_multi_str.rs @@ -0,0 +1,78 @@ +use oxc_ast::AstKind; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{context::LintContext, rule::Rule, AstNode}; + +fn no_multi_str_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("eslint(no-multi-str): Unexpected multi string.").with_label(span0) +} + +#[derive(Debug, Default, Clone)] +pub struct NoMultiStr; + +declare_oxc_lint!( + /// ### What it does + /// + /// Disallow multiline strings. + /// + /// ### Why is this bad? + /// + /// Some consider this to be a bad practice as it was an undocumented feature of JavaScript + /// that was only formalized later. + /// + /// ### Example + /// ```javascript + /// var x = "Line 1 \ + /// Line 2"; + /// ``` + NoMultiStr, + style, +); + +impl Rule for NoMultiStr { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + if let AstKind::StringLiteral(literal) = node.kind() { + let source = literal.span.source_text(ctx.source_text()); + // https://github.com/eslint/eslint/blob/9e6d6405c3ee774c2e716a3453ede9696ced1be7/lib/shared/ast-utils.js#L12 + let position = + source.find(|ch| matches!(ch, '\r' | '\n' | '\u{2028}' | '\u{2029}')).unwrap_or(0); + if position != 0 { + // We found the "newline" character but want to highlight the '\', so go back one + // character. + let multi_span_start = + literal.span.start + u32::try_from(position).unwrap_or_default() - 1; + ctx.diagnostic(no_multi_str_diagnostic(Span::new( + multi_span_start, + multi_span_start + 1, + ))); + } + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + "var a = 'Line 1 Line 2';", + "var a =
+

Wat

+
;", // { "ecmaVersion": 6, "parserOptions": { "ecmaFeatures": { "jsx": true } } } + ]; + + let fail = vec![ + "var x = 'Line 1 \\ + Line 2'", + "test('Line 1 \\ + Line 2');", + "'foo\\\rbar';", + "'foo\\
bar';", + "'foo\\
ar';", + "'\\
still fails';", + ]; + + Tester::new(NoMultiStr::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/no_multi_str.snap b/crates/oxc_linter/src/snapshots/no_multi_str.snap new file mode 100644 index 000000000..c0a85a48f --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_multi_str.snap @@ -0,0 +1,40 @@ +--- +source: crates/oxc_linter/src/tester.rs +--- + ⚠ eslint(no-multi-str): Unexpected multi string. + ╭─[no_multi_str.tsx:1:17] + 1 │ var x = 'Line 1 \ + · ─ + 2 │ Line 2' + ╰──── + + ⚠ eslint(no-multi-str): Unexpected multi string. + ╭─[no_multi_str.tsx:1:14] + 1 │ test('Line 1 \ + · ─ + 2 │ Line 2'); + ╰──── + + ⚠ eslint(no-multi-str): Unexpected multi string. + ╭─[no_multi_str.tsx:1:5] + 1 │ 'foo\ bar'; + · ─ + ╰──── + + ⚠ eslint(no-multi-str): Unexpected multi string. + ╭─[no_multi_str.tsx:1:5] + 1 │ 'foo\
bar'; + · ─ + ╰──── + + ⚠ eslint(no-multi-str): Unexpected multi string. + ╭─[no_multi_str.tsx:1:5] + 1 │ 'foo\
ar'; + · ─ + ╰──── + + ⚠ eslint(no-multi-str): Unexpected multi string. + ╭─[no_multi_str.tsx:1:2] + 1 │ '\
still fails'; + · ─ + ╰────