mirror of https://github.com/lukechilds/node.git
Browse Source
PR-URL: https://github.com/nodejs/node/pull/11834 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Teddy Katz <teddy.katz@gmail.com> Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>v6
committed by
Franziska Hinkelmann
35 changed files with 214 additions and 47 deletions
@ -0,0 +1,157 @@ |
|||||
|
/** |
||||
|
* @fileoverview Look for unescaped "literal" dots in regular expressions |
||||
|
* @author Brian White |
||||
|
*/ |
||||
|
'use strict'; |
||||
|
|
||||
|
const path = require('path'); |
||||
|
const utilsPath = path.join(__dirname, '..', 'eslint', 'lib', 'ast-utils.js'); |
||||
|
const astUtils = require(utilsPath); |
||||
|
const getLocationFromRangeIndex = astUtils.getLocationFromRangeIndex; |
||||
|
const getRangeIndexFromLocation = astUtils.getRangeIndexFromLocation; |
||||
|
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
// Rule Definition
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
|
||||
|
module.exports = function(context) { |
||||
|
const sourceCode = context.getSourceCode(); |
||||
|
const regexpStack = []; |
||||
|
var regexpBuffer = []; |
||||
|
var inRegExp = false; |
||||
|
|
||||
|
var getLocFromIndex; |
||||
|
if (typeof sourceCode.getLocFromIndex === 'function') { |
||||
|
getLocFromIndex = function(index) { |
||||
|
return sourceCode.getLocFromIndex(index); |
||||
|
}; |
||||
|
} else { |
||||
|
getLocFromIndex = function(index) { |
||||
|
return getLocationFromRangeIndex(sourceCode, index); |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
var getIndexFromLoc; |
||||
|
if (typeof sourceCode.getIndexFromLoc === 'function') { |
||||
|
getIndexFromLoc = function(loc) { |
||||
|
return sourceCode.getIndexFromLoc(loc); |
||||
|
}; |
||||
|
} else { |
||||
|
getIndexFromLoc = function(loc) { |
||||
|
return getRangeIndexFromLocation(sourceCode, loc); |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
function report(node, startOffset) { |
||||
|
context.report({ |
||||
|
node, |
||||
|
loc: getLocFromIndex(getIndexFromLoc(node.loc.start) + startOffset), |
||||
|
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 |
||||
|
}; |
||||
|
}; |
Loading…
Reference in new issue