Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b79b6fb

Browse files
authored
fix: Fix suggestion message in no-useless-escape (#17339)
* fix: Fix suggestion message in `no-useless-escape` * Fixes * Fix for nodes with null `directive` * Empty commit * Remove fallback for `isDirective` * Remove duplicate tests
1 parent 84d243b commit b79b6fb

File tree

5 files changed

+102
-44
lines changed

5 files changed

+102
-44
lines changed

lib/rules/no-useless-escape.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ module.exports = {
9494
messages: {
9595
unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
9696
removeEscape: "Remove the `\\`. This maintains the current functionality.",
97+
removeEscapeDoNotKeepSemantics: "Remove the `\\` if it was inserted by mistake.",
9798
escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character."
9899
},
99100

@@ -125,7 +126,10 @@ module.exports = {
125126
data: { character },
126127
suggest: [
127128
{
128-
messageId: "removeEscape",
129+
130+
// Removing unnecessary `\` characters in a directive is not guaranteed to maintain functionality.
131+
messageId: astUtils.isDirective(node.parent)
132+
? "removeEscapeDoNotKeepSemantics" : "removeEscape",
129133
fix(fixer) {
130134
return fixer.removeRange(range);
131135
}

lib/rules/padding-line-between-statements.js

Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -130,42 +130,6 @@ function isBlockLikeStatement(sourceCode, node) {
130130
);
131131
}
132132

133-
/**
134-
* Check whether the given node is a directive or not.
135-
* @param {ASTNode} node The node to check.
136-
* @param {SourceCode} sourceCode The source code object to get tokens.
137-
* @returns {boolean} `true` if the node is a directive.
138-
*/
139-
function isDirective(node, sourceCode) {
140-
return (
141-
astUtils.isTopLevelExpressionStatement(node) &&
142-
node.expression.type === "Literal" &&
143-
typeof node.expression.value === "string" &&
144-
!astUtils.isParenthesised(sourceCode, node.expression)
145-
);
146-
}
147-
148-
/**
149-
* Check whether the given node is a part of directive prologue or not.
150-
* @param {ASTNode} node The node to check.
151-
* @param {SourceCode} sourceCode The source code object to get tokens.
152-
* @returns {boolean} `true` if the node is a part of directive prologue.
153-
*/
154-
function isDirectivePrologue(node, sourceCode) {
155-
if (isDirective(node, sourceCode)) {
156-
for (const sibling of node.parent.body) {
157-
if (sibling === node) {
158-
break;
159-
}
160-
if (!isDirective(sibling, sourceCode)) {
161-
return false;
162-
}
163-
}
164-
return true;
165-
}
166-
return false;
167-
}
168-
169133
/**
170134
* Gets the actual last token.
171135
*
@@ -359,12 +323,10 @@ const StatementTypes = {
359323
CJS_IMPORT.test(sourceCode.getText(node.declarations[0].init))
360324
},
361325
directive: {
362-
test: isDirectivePrologue
326+
test: astUtils.isDirective
363327
},
364328
expression: {
365-
test: (node, sourceCode) =>
366-
node.type === "ExpressionStatement" &&
367-
!isDirectivePrologue(node, sourceCode)
329+
test: node => node.type === "ExpressionStatement" && !astUtils.isDirective(node)
368330
},
369331
iife: {
370332
test: isIIFEStatement
@@ -375,10 +337,10 @@ const StatementTypes = {
375337
isBlockLikeStatement(sourceCode, node)
376338
},
377339
"multiline-expression": {
378-
test: (node, sourceCode) =>
340+
test: node =>
379341
node.loc.start.line !== node.loc.end.line &&
380342
node.type === "ExpressionStatement" &&
381-
!isDirectivePrologue(node, sourceCode)
343+
!astUtils.isDirective(node)
382344
},
383345

384346
"multiline-const": newMultilineKeywordTester("const"),

lib/rules/utils/ast-utils.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,15 @@ function isTopLevelExpressionStatement(node) {
10061006

10071007
}
10081008

1009+
/**
1010+
* Check whether the given node is a part of a directive prologue or not.
1011+
* @param {ASTNode} node The node to check.
1012+
* @returns {boolean} `true` if the node is a part of directive prologue.
1013+
*/
1014+
function isDirective(node) {
1015+
return node.type === "ExpressionStatement" && typeof node.directive === "string";
1016+
}
1017+
10091018
//------------------------------------------------------------------------------
10101019
// Public Interface
10111020
//------------------------------------------------------------------------------
@@ -2158,5 +2167,6 @@ module.exports = {
21582167
getSwitchCaseColonToken,
21592168
getModuleExportName,
21602169
isConstant,
2161-
isTopLevelExpressionStatement
2170+
isTopLevelExpressionStatement,
2171+
isDirective
21622172
};

tests/lib/rules/no-useless-escape.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,6 +1066,43 @@ ruleTester.run("no-useless-escape", rule, {
10661066
output: "`\\\\a```"
10671067
}]
10681068
}]
1069+
},
1070+
1071+
// https://github.com/eslint/eslint/issues/16988
1072+
{
1073+
code: String.raw`"use\ strict";`,
1074+
errors: [{
1075+
line: 1,
1076+
column: 5,
1077+
endColumn: 6,
1078+
message: "Unnecessary escape character: \\ .",
1079+
type: "Literal",
1080+
suggestions: [{
1081+
messageId: "removeEscapeDoNotKeepSemantics",
1082+
output: String.raw`"use strict";`
1083+
}, {
1084+
messageId: "escapeBackslash",
1085+
output: String.raw`"use\\ strict";`
1086+
}]
1087+
}]
1088+
},
1089+
{
1090+
code: String.raw`({ foo() { "foo"; "bar"; "ba\z" } })`,
1091+
parserOptions: { ecmaVersion: 6 },
1092+
errors: [{
1093+
line: 1,
1094+
column: 29,
1095+
endColumn: 30,
1096+
message: "Unnecessary escape character: \\z.",
1097+
type: "Literal",
1098+
suggestions: [{
1099+
messageId: "removeEscapeDoNotKeepSemantics",
1100+
output: String.raw`({ foo() { "foo"; "bar"; "baz" } })`
1101+
}, {
1102+
messageId: "escapeBackslash",
1103+
output: String.raw`({ foo() { "foo"; "bar"; "ba\\z" } })`
1104+
}]
1105+
}]
10691106
}
10701107
]
10711108
});

tests/lib/rules/utils/ast-utils.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,4 +1875,49 @@ describe("ast-utils", () => {
18751875
});
18761876
});
18771877
});
1878+
1879+
describe("isDirective", () => {
1880+
const expectedResults = [
1881+
{ code: '"use strict";', expectedRetVal: true },
1882+
{ code: '"use strict"; "use asm";', nodeText: '"use asm";', expectedRetVal: true },
1883+
{ code: 'const a = () => { "foo"; }', nodeText: '"foo";', expectedRetVal: true },
1884+
{ code: '"";', expectedRetVal: true },
1885+
{ code: '{ "foo"; }', nodeText: '"foo";', expectedRetVal: false },
1886+
{ code: "foo();", expectedRetVal: false },
1887+
{ code: '"foo" + "bar";', expectedRetVal: false },
1888+
{ code: "12345;", expectedRetVal: false },
1889+
{ code: "`foo`;", expectedRetVal: false },
1890+
{ code: "('foo');", expectedRetVal: false },
1891+
{ code: 'foo(); "use strict";', nodeText: '"use strict";', expectedRetVal: false }
1892+
];
1893+
1894+
expectedResults.forEach(({ code, nodeText = code, expectedRetVal }) => {
1895+
it(`should return ${expectedRetVal} for \`${nodeText}\` in \`${code}\``, () => {
1896+
linter.defineRule("checker", {
1897+
create: mustCall(({ sourceCode }) => {
1898+
const assertForNode = mustCall(
1899+
node => assert.strictEqual(astUtils.isDirective(node), expectedRetVal)
1900+
);
1901+
1902+
return ({
1903+
ExpressionStatement(node) {
1904+
if (sourceCode.getText(node) === nodeText) {
1905+
assertForNode(node);
1906+
1907+
if (!expectedRetVal) {
1908+
1909+
// The flow parser sets `directive` to null on non-directive ExpressionStatement nodes.
1910+
node.directive = null;
1911+
assertForNode(node);
1912+
}
1913+
}
1914+
}
1915+
});
1916+
})
1917+
});
1918+
1919+
linter.verify(code, { rules: { checker: "error" }, parserOptions: { ecmaVersion: 2022 } });
1920+
});
1921+
});
1922+
});
18781923
});

0 commit comments

Comments
 (0)