Skip to content

Commit 30ebf92

Browse files
mdjermanovickaicataldo
authored andcommitted
Fix: prefer-template autofix produces syntax error with octal escapes (#12085)
1 parent 13c3988 commit 30ebf92

File tree

4 files changed

+105
-10
lines changed

4 files changed

+105
-10
lines changed

lib/rules/prefer-template.js

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,7 @@ function isOctalEscapeSequence(node) {
5252
return false;
5353
}
5454

55-
const match = node.raw.match(/^([^\\]|\\[^0-7])*\\([0-7]{1,3})/u);
56-
57-
if (match) {
58-
59-
// \0 is actually not considered an octal
60-
if (match[2] !== "0" || typeof match[3] !== "undefined") {
61-
return true;
62-
}
63-
}
64-
return false;
55+
return astUtils.hasOctalEscapeSequence(node.raw);
6556
}
6657

6758
/**

lib/rules/utils/ast-utils.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
3838
const STATEMENT_LIST_PARENTS = new Set(["Program", "BlockStatement", "SwitchCase"]);
3939

4040
const DECIMAL_INTEGER_PATTERN = /^(0|[1-9]\d*)$/u;
41+
const OCTAL_ESCAPE_PATTERN = /^(?:[^\\]|\\[^0-7]|\\0(?![0-9]))*\\(?:[1-7]|0[0-9])/u;
4142

4243
/**
4344
* Checks reference if is non initializer and writable.
@@ -1373,5 +1374,20 @@ module.exports = {
13731374
"/*".length +
13741375
(match ? match.index + 1 : 0)
13751376
);
1377+
},
1378+
1379+
/**
1380+
* Determines whether the given raw string contains an octal escape sequence.
1381+
*
1382+
* "\1", "\2" ... "\7"
1383+
* "\00", "\01" ... "\09"
1384+
*
1385+
* "\0", when not followed by a digit, is not an octal escape sequence.
1386+
*
1387+
* @param {string} rawString A string in its raw representation.
1388+
* @returns {boolean} `true` if the string contains at least one octal escape sequence.
1389+
*/
1390+
hasOctalEscapeSequence(rawString) {
1391+
return OCTAL_ESCAPE_PATTERN.test(rawString);
13761392
}
13771393
};

tests/lib/rules/prefer-template.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,16 @@ ruleTester.run("prefer-template", rule, {
199199
output: null,
200200
errors
201201
},
202+
{
203+
code: "foo + '\\0\\1'",
204+
output: null,
205+
errors
206+
},
207+
{
208+
code: "foo + '\\08'",
209+
output: null,
210+
errors
211+
},
202212
{
203213
code: "foo + '\\\\033'",
204214
output: "`${foo }\\\\033`",

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

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,4 +1250,82 @@ describe("ast-utils", () => {
12501250
assert.strictEqual(astUtils.equalTokens(ast.body[0], ast.body[1], sourceCode), false);
12511251
});
12521252
});
1253+
1254+
describe("hasOctalEscapeSequence", () => {
1255+
1256+
/* eslint-disable quote-props */
1257+
const expectedResults = {
1258+
"\\1": true,
1259+
"\\2": true,
1260+
"\\7": true,
1261+
"\\00": true,
1262+
"\\01": true,
1263+
"\\02": true,
1264+
"\\07": true,
1265+
"\\08": true,
1266+
"\\09": true,
1267+
"\\10": true,
1268+
"\\12": true,
1269+
" \\1": true,
1270+
"\\1 ": true,
1271+
"a\\1": true,
1272+
"\\1a": true,
1273+
"a\\1a": true,
1274+
" \\01": true,
1275+
"\\01 ": true,
1276+
"a\\01": true,
1277+
"\\01a": true,
1278+
"a\\01a": true,
1279+
"a\\08a": true,
1280+
"\\0\\1": true,
1281+
"\\0\\01": true,
1282+
"\\0\\08": true,
1283+
"\\n\\1": true,
1284+
"\\n\\01": true,
1285+
"\\n\\08": true,
1286+
"\\\\\\1": true,
1287+
"\\\\\\01": true,
1288+
"\\\\\\08": true,
1289+
1290+
"\\0": false,
1291+
"\\8": false,
1292+
"\\9": false,
1293+
" \\0": false,
1294+
"\\0 ": false,
1295+
"a\\0": false,
1296+
"\\0a": false,
1297+
"a\\8a": false,
1298+
"\\0\\8": false,
1299+
"\\8\\0": false,
1300+
"\\80": false,
1301+
"\\81": false,
1302+
"\\\\": false,
1303+
"\\\\0": false,
1304+
"\\\\01": false,
1305+
"\\\\08": false,
1306+
"\\\\1": false,
1307+
"\\\\12": false,
1308+
"\\\\\\0": false,
1309+
"\\\\\\8": false,
1310+
"\\0\\\\": false,
1311+
"0": false,
1312+
"1": false,
1313+
"8": false,
1314+
"01": false,
1315+
"08": false,
1316+
"80": false,
1317+
"12": false,
1318+
"\\a": false,
1319+
"\\n": false
1320+
};
1321+
/* eslint-enable quote-props */
1322+
1323+
Object.keys(expectedResults).forEach(key => {
1324+
it(`should return ${expectedResults[key]} for ${key}`, () => {
1325+
const ast = espree.parse(`"${key}"`);
1326+
1327+
assert.strictEqual(astUtils.hasOctalEscapeSequence(ast.body[0].expression.raw), expectedResults[key]);
1328+
});
1329+
});
1330+
});
12531331
});

0 commit comments

Comments
 (0)