Skip to content

Commit

Permalink
Fix: do not autofix octal escape sequence (fixes #10031) (#10240)
Browse files Browse the repository at this point in the history
* Fix: do not autofix octal escape sequence (fixes #10031)

* Chore: simplify regex and add tests (fixes #10031)

* Chore: add test for \0 escape (fixes #10031)

* Chore: one more \0 test (fixes #10031)

* Fix: autofix \0 literals (fixes #10031)
  • Loading branch information
malcolmsgroves authored and not-an-aardvark committed May 11, 2018
1 parent 514013c commit c343d86
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 3 deletions.
59 changes: 56 additions & 3 deletions lib/rules/prefer-template.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,45 @@ function getTopConcatBinaryExpression(node) {
return currentNode;
}

/**
* Determines whether a given node is a octal escape sequence
* @param {ASTNode} node A node to check
* @returns {boolean} `true` if the node is an octal escape sequence
*/
function isOctalEscapeSequence(node) {

// No need to check TemplateLiterals – would throw error with octal escape
const isStringLiteral = node.type === "Literal" && typeof node.value === "string";

if (!isStringLiteral) {
return false;
}

const match = node.raw.match(/^([^\\]|\\[^0-7])*\\([0-7]{1,3})/);

if (match) {

// \0 is actually not considered an octal
if (match[2] !== "0" || typeof match[3] !== "undefined") {
return true;
}
}
return false;
}

/**
* Checks whether or not a node contains a octal escape sequence
* @param {ASTNode} node A node to check
* @returns {boolean} `true` if the node contains an octal escape sequence
*/
function hasOctalEscapeSequence(node) {
if (isConcatenation(node)) {
return hasOctalEscapeSequence(node.left) || hasOctalEscapeSequence(node.right);
}

return isOctalEscapeSequence(node);
}

/**
* Checks whether or not a given binary expression has string literals.
* @param {ASTNode} node - A node to check.
Expand Down Expand Up @@ -193,6 +232,22 @@ module.exports = {
return `\`\${${textBeforeNode || ""}${sourceCode.getText(currentNode)}${textAfterNode || ""}}\``;
}

/**
* Returns a fixer object that converts a non-string binary expression to a template literal
* @param {SourceCodeFixer} fixer The fixer object
* @param {ASTNode} node A node that should be converted to a template literal
* @returns {Object} A fix for this binary expression
*/
function fixNonStringBinaryExpression(fixer, node) {
const topBinaryExpr = getTopConcatBinaryExpression(node.parent);

if (hasOctalEscapeSequence(topBinaryExpr)) {
return null;
}

return fixer.replaceText(topBinaryExpr, getTemplateLiteral(topBinaryExpr, null, null));
}

/**
* Reports if a given node is string concatenation with non string literals.
*
Expand All @@ -216,9 +271,7 @@ module.exports = {
context.report({
node: topBinaryExpr,
message: "Unexpected string concatenation.",
fix(fixer) {
return fixer.replaceText(topBinaryExpr, getTemplateLiteral(topBinaryExpr, null, null));
}
fix: fixer => fixNonStringBinaryExpression(fixer, node)
});
}
}
Expand Down
21 changes: 21 additions & 0 deletions tests/lib/rules/prefer-template.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
ruleTester.run("prefer-template", rule, {
valid: [
"'use strict';",
"var foo = 'foo' + '\\0';",
"var foo = 'bar';",
"var foo = 'bar' + 'baz';",
"var foo = foo + +'100';",
Expand Down Expand Up @@ -187,6 +188,26 @@ ruleTester.run("prefer-template", rule, {
code: "foo + 'handles unicode escapes correctly: \\x27'", // "\x27" === "'"
output: "`${foo }handles unicode escapes correctly: \\x27`",
errors
},
{
code: "foo + 'does not autofix octal escape sequence' + '\\033'",
output: null,
errors
},
{
code: "foo + '\\n other text \\033'",
output: null,
errors
},
{
code: "foo + '\\\\033'",
output: "`${foo }\\\\033`",
errors
},
{
code: "foo + '\\0'",
output: "`${foo }\\0`",
errors
}
]
});

0 comments on commit c343d86

Please sign in to comment.