0
0
mirror of https://github.com/nodejs/node.git synced 2024-12-01 16:10:02 +01:00
nodejs/tools/eslint-rules/no-unescaped-regexp-dot.js
Rich Trott 3c31bfff65 benchmark,lib,test,tools: use consistent quotes
In preparation for a linting rule, use consistent quotation for
properties in objects.

PR-URL: https://github.com/nodejs/node/pull/19156
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Teddy Katz <teddy.katz@gmail.com>
Reviewed-By: Vse Mozhet Byt <vsemozhetbyt@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Reviewed-By: Roman Reiss <me@silverwind.io>
2018-03-07 19:06:44 -08:00

131 lines
3.6 KiB
JavaScript

/**
* @fileoverview Look for unescaped "literal" dots in regular expressions
* @author Brian White
*/
'use strict';
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = function(context) {
const sourceCode = context.getSourceCode();
const regexpStack = [];
var regexpBuffer = [];
var inRegExp = false;
function report(node, startOffset) {
const indexOfDot = sourceCode.getIndexFromLoc(node.loc.start) + startOffset;
context.report({
node,
loc: sourceCode.getLocFromIndex(indexOfDot),
message: 'Unescaped dot character in regular expression'
});
}
const allowedModifiers = ['+', '*', '?', '{'];
function checkRegExp(nodes) {
var escaping = false;
var inCharClass = false;
for (var n = 0; n < nodes.length; ++n) {
const pair = nodes[n];
const node = pair[0];
const str = pair[1];
for (var i = 0; i < str.length; ++i) {
switch (str[i]) {
case '[':
if (!escaping)
inCharClass = true;
else
escaping = false;
break;
case ']':
if (!escaping) {
if (inCharClass)
inCharClass = false;
} else {
escaping = false;
}
break;
case '\\':
escaping = !escaping;
break;
case '.':
if (!escaping) {
if (!inCharClass &&
((i + 1) === str.length ||
allowedModifiers.indexOf(str[i + 1]) === -1)) {
report(node, i);
}
} else {
escaping = false;
}
break;
default:
if (escaping)
escaping = false;
}
}
}
}
function checkRegExpStart(node) {
if (node.callee && node.callee.name === 'RegExp') {
if (inRegExp) {
regexpStack.push(regexpBuffer);
regexpBuffer = [];
}
inRegExp = true;
}
}
function checkRegExpEnd(node) {
if (node.callee && node.callee.name === 'RegExp') {
checkRegExp(regexpBuffer);
if (regexpStack.length) {
regexpBuffer = regexpStack.pop();
} else {
inRegExp = false;
regexpBuffer = [];
}
}
}
function checkLiteral(node) {
const isTemplate = (node.type === 'TemplateLiteral' && node.quasis &&
node.quasis.length);
if (inRegExp &&
(isTemplate || (typeof node.value === 'string' && node.value.length))) {
var p = node.parent;
while (p && p.type === 'BinaryExpression') {
p = p.parent;
}
if (p && (p.type === 'NewExpression' || p.type === 'CallExpression') &&
p.callee && p.callee.type === 'Identifier' &&
p.callee.name === 'RegExp') {
if (isTemplate) {
const quasis = node.quasis;
for (var i = 0; i < quasis.length; ++i) {
const el = quasis[i];
if (el.type === 'TemplateElement' && el.value && el.value.cooked)
regexpBuffer.push([el, el.value.cooked]);
}
} else {
regexpBuffer.push([node, node.value]);
}
}
} else if (node.regex) {
checkRegExp([[node, node.regex.pattern]]);
}
}
return {
'TemplateLiteral': checkLiteral,
'Literal': checkLiteral,
'CallExpression': checkRegExpStart,
'NewExpression': checkRegExpStart,
'CallExpression:exit': checkRegExpEnd,
'NewExpression:exit': checkRegExpEnd
};
};