mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(linter/eslint/no-implicit-coercion): preserve template coercion whitespace (#25470)
## Summary - only treat one-expression template literals as shorthand coercion when both cooked quasis are empty - preserve leading, trailing, and escaped whitespace when disallowTemplateShorthand is enabled - add regression coverage for meaningful whitespace and empty line continuations Fixes #25454
This commit is contained in:
1 parent
2e35238d65
commit
b746e00fd1
2 files changed
+29
-31
No files matched your search
@@ -315,25 +315,20 @@ impl Rule for NoImplicitCoercion {
|
||||
let first_quasi = &template.quasis[0];
|
||||
let last_quasi = &template.quasis[1];
|
||||
|
||||
// Check if both quasi parts contain no meaningful content.
|
||||
// We use the cooked value to handle escape sequences properly.
|
||||
// ESLint considers templates like `\n${foo}` as coercion because
|
||||
// escape sequences that become whitespace don't add meaningful content.
|
||||
// We also allow backslash characters in the cooked value because they
|
||||
// typically come from line continuations (e.g., `\\\n${foo}`) which
|
||||
// ESLint also treats as implicit coercion.
|
||||
let first_is_empty_or_whitespace = first_quasi
|
||||
// Only an empty cooked value is shorthand coercion. Whitespace is part of the
|
||||
// resulting string and must not be discarded by the fixer.
|
||||
let first_is_empty = first_quasi
|
||||
.value
|
||||
.cooked
|
||||
.as_ref()
|
||||
.is_some_and(|s| s.chars().all(|c| c.is_whitespace() || c == '\\'));
|
||||
let last_is_empty_or_whitespace = last_quasi
|
||||
.is_some_and(|s| s.is_empty());
|
||||
let last_is_empty = last_quasi
|
||||
.value
|
||||
.cooked
|
||||
.as_ref()
|
||||
.is_some_and(|s| s.chars().all(|c| c.is_whitespace() || c == '\\'));
|
||||
.is_some_and(|s| s.is_empty());
|
||||
|
||||
if first_is_empty_or_whitespace && last_is_empty_or_whitespace {
|
||||
if first_is_empty && last_is_empty {
|
||||
let expr = &template.expressions[0];
|
||||
// Don't warn if the expression is already a string literal or String() call
|
||||
if !is_string_literal_or_string_call(expr) {
|
||||
@@ -554,6 +549,20 @@ fn test() {
|
||||
("foo += `${bar}`", None),
|
||||
("`a${foo}`", Some(serde_json::json!([{ "disallowTemplateShorthand": true }]))),
|
||||
("`${foo}b`", Some(serde_json::json!([{ "disallowTemplateShorthand": true }]))),
|
||||
("` ${foo}`", Some(serde_json::json!([{ "disallowTemplateShorthand": true }]))),
|
||||
("`${foo} `", Some(serde_json::json!([{ "disallowTemplateShorthand": true }]))),
|
||||
(r"`\n${foo}`", Some(serde_json::json!([{ "disallowTemplateShorthand": true }]))),
|
||||
(r"`${foo}\t`", Some(serde_json::json!([{ "disallowTemplateShorthand": true }]))),
|
||||
(
|
||||
r"`\\
|
||||
${foo}`",
|
||||
Some(serde_json::json!([{ "disallowTemplateShorthand": true }])),
|
||||
),
|
||||
(
|
||||
r"`${foo}\\
|
||||
`",
|
||||
Some(serde_json::json!([{ "disallowTemplateShorthand": true }])),
|
||||
),
|
||||
("`${foo}${bar}`", Some(serde_json::json!([{ "disallowTemplateShorthand": true }]))),
|
||||
("tag`${foo}`", Some(serde_json::json!([{ "disallowTemplateShorthand": true }]))),
|
||||
("`${foo}`", None),
|
||||
@@ -597,16 +606,7 @@ fn test() {
|
||||
(r#"foo.bar+"""#, None),
|
||||
("foo.bar+``", None),
|
||||
("`${foo}`", Some(serde_json::json!([{ "disallowTemplateShorthand": true }]))),
|
||||
(
|
||||
r"`\\
|
||||
${foo}`",
|
||||
Some(serde_json::json!([{ "disallowTemplateShorthand": true }])),
|
||||
),
|
||||
(
|
||||
r"`${foo}\\
|
||||
`",
|
||||
Some(serde_json::json!([{ "disallowTemplateShorthand": true }])),
|
||||
),
|
||||
("`\\\n${foo}`", Some(serde_json::json!([{ "disallowTemplateShorthand": true }]))),
|
||||
(r#"foo += """#, None),
|
||||
("foo += ``", None),
|
||||
("var a = !!foo", Some(serde_json::json!([{ "boolean": true, "allow": ["~"] }]))),
|
||||
@@ -637,6 +637,11 @@ fn test() {
|
||||
"var a = Boolean(foo)",
|
||||
Some(serde_json::json!([{ "boolean": true, "allow": ["~"] }])),
|
||||
),
|
||||
(
|
||||
"`${foo}`",
|
||||
"String(foo)",
|
||||
Some(serde_json::json!([{ "disallowTemplateShorthand": true }])),
|
||||
),
|
||||
];
|
||||
Tester::new(NoImplicitCoercion::NAME, NoImplicitCoercion::PLUGIN, pass, fail)
|
||||
.expect_fix(fix)
|
||||
|
||||
@@ -172,15 +172,8 @@ source: crates/oxc_linter/src/tester.rs
|
||||
|
||||
⚠ eslint(no-implicit-coercion): Unexpected implicit coercion to string
|
||||
╭─[no_implicit_coercion.tsx:1:1]
|
||||
1 │ ╭─▶ `\\
|
||||
2 │ ╰─▶ ${foo}`
|
||||
╰────
|
||||
help: Use `String(value)` instead
|
||||
|
||||
⚠ eslint(no-implicit-coercion): Unexpected implicit coercion to string
|
||||
╭─[no_implicit_coercion.tsx:1:1]
|
||||
1 │ ╭─▶ `${foo}\\
|
||||
2 │ ╰─▶ `
|
||||
1 │ ╭─▶ `\
|
||||
2 │ ╰─▶ ${foo}`
|
||||
╰────
|
||||
help: Use `String(value)` instead
|
||||
|
||||
|
||||
Reference in new issue
Block a user