eslint-plugin-regexp
Advanced tools
Comparing version
@@ -8,2 +8,89 @@ "use strict"; | ||
const replacements_utils_1 = require("../utils/replacements-utils"); | ||
class CapturingData { | ||
constructor(node, flags) { | ||
this.unused = new Set(); | ||
this.unusedNames = new Map(); | ||
this.indexToCapturingGroup = new Map(); | ||
this.countOfCapturingGroup = 0; | ||
this.state = { | ||
used: false, | ||
track: true, | ||
}; | ||
this.node = node; | ||
this.flags = flags; | ||
} | ||
getUnused() { | ||
const unusedCapturingGroups = new Set(this.unused); | ||
const unusedNames = new Set(); | ||
for (const cgNodes of this.unusedNames.values()) { | ||
for (const cgNode of cgNodes) { | ||
if (!unusedCapturingGroups.has(cgNode)) { | ||
unusedNames.add(cgNode); | ||
} | ||
} | ||
} | ||
return { | ||
unusedCapturingGroups, | ||
unusedNames, | ||
}; | ||
} | ||
usedIndex(ref) { | ||
const cgNode = this.indexToCapturingGroup.get(ref); | ||
if (cgNode) { | ||
this.unused.delete(cgNode); | ||
} | ||
} | ||
usedName(ref) { | ||
const cgNodes = this.unusedNames.get(ref); | ||
if (cgNodes) { | ||
this.unusedNames.delete(ref); | ||
for (const cgNode of cgNodes) { | ||
this.unused.delete(cgNode); | ||
} | ||
} | ||
} | ||
usedAllNames() { | ||
for (const cgNodes of this.unusedNames.values()) { | ||
for (const cgNode of cgNodes) { | ||
this.unused.delete(cgNode); | ||
} | ||
} | ||
this.unusedNames.clear(); | ||
} | ||
isAllUsed() { | ||
return !this.unused.size && !this.unusedNames.size; | ||
} | ||
markAsUsed() { | ||
this.state.used = true; | ||
} | ||
markAsCannotTrack() { | ||
this.state.track = false; | ||
} | ||
isNeedReport() { | ||
return this.state.used && this.state.track && !this.isAllUsed(); | ||
} | ||
visitor() { | ||
return { | ||
onCapturingGroupEnter: (cgNode) => { | ||
this.countOfCapturingGroup++; | ||
if (!cgNode.references.length) { | ||
this.unused.add(cgNode); | ||
this.indexToCapturingGroup.set(this.countOfCapturingGroup, cgNode); | ||
} | ||
else if (cgNode.references.some((ref) => typeof ref.ref === "string")) { | ||
return; | ||
} | ||
if (cgNode.name) { | ||
const array = this.unusedNames.get(cgNode.name); | ||
if (array) { | ||
array.push(cgNode); | ||
} | ||
else { | ||
this.unusedNames.set(cgNode.name, [cgNode]); | ||
} | ||
} | ||
}, | ||
}; | ||
} | ||
} | ||
function getProperty(node) { | ||
@@ -161,23 +248,21 @@ if (node.type === "MemberExpression") { | ||
} | ||
function report(cgNode, data, messageId) { | ||
context.report({ | ||
node: data.node, | ||
loc: utils_1.getRegexpLocation(sourceCode, data.node, cgNode), | ||
messageId: messageId !== null && messageId !== void 0 ? messageId : (cgNode.name | ||
? "unusedNamedCapturingGroup" | ||
: "unusedCapturingGroup"), | ||
data: cgNode.name ? { name: cgNode.name } : {}, | ||
}); | ||
} | ||
function reportUnused(capturingData, unusedCapturingGroups, unusedNames) { | ||
function reportUnused(capturingData) { | ||
const { unusedCapturingGroups, unusedNames, } = capturingData.getUnused(); | ||
for (const cgNode of unusedCapturingGroups) { | ||
report(cgNode, capturingData, null); | ||
context.report({ | ||
node: capturingData.node, | ||
loc: utils_1.getRegexpLocation(sourceCode, capturingData.node, cgNode), | ||
messageId: cgNode.name | ||
? "unusedNamedCapturingGroup" | ||
: "unusedCapturingGroup", | ||
data: cgNode.name ? { name: cgNode.name } : {}, | ||
}); | ||
} | ||
for (const cgNodes of unusedNames.values()) { | ||
for (const cgNode of cgNodes) { | ||
if (unusedCapturingGroups.has(cgNode)) { | ||
continue; | ||
} | ||
report(cgNode, capturingData, "unusedName"); | ||
} | ||
for (const cgNode of unusedNames) { | ||
context.report({ | ||
node: capturingData.node, | ||
loc: utils_1.getRegexpLocation(sourceCode, capturingData.node, cgNode), | ||
messageId: "unusedName", | ||
data: { name: cgNode.name }, | ||
}); | ||
} | ||
@@ -187,3 +272,3 @@ } | ||
const capturingData = getCapturingData(node.arguments[0]); | ||
if (capturingData == null || !capturingData.unused.size) { | ||
if (capturingData == null || capturingData.isAllUsed()) { | ||
return; | ||
@@ -195,7 +280,6 @@ } | ||
if (capturingData.flags.includes("g")) { | ||
for (const cgNode of capturingData.unused) { | ||
report(cgNode, capturingData, null); | ||
} | ||
capturingData.markAsUsed(); | ||
} | ||
else { | ||
capturingData.markAsUsed(); | ||
verifyForExecResult(node, capturingData); | ||
@@ -206,3 +290,3 @@ } | ||
const capturingData = getCapturingData(node.arguments[0]); | ||
if (capturingData == null || !capturingData.unused.size) { | ||
if (capturingData == null || capturingData.isAllUsed()) { | ||
return; | ||
@@ -213,9 +297,7 @@ } | ||
} | ||
for (const cgNode of capturingData.unused) { | ||
report(cgNode, capturingData, null); | ||
} | ||
capturingData.markAsUsed(); | ||
} | ||
function verifyForTest(node) { | ||
const capturingData = getCapturingData(node.callee.object); | ||
if (capturingData == null || !capturingData.unused.size) { | ||
if (capturingData == null || capturingData.isAllUsed()) { | ||
return; | ||
@@ -226,9 +308,7 @@ } | ||
} | ||
for (const cgNode of capturingData.unused) { | ||
report(cgNode, capturingData, null); | ||
} | ||
capturingData.markAsUsed(); | ||
} | ||
function verifyForReplace(node) { | ||
const capturingData = getCapturingData(node.arguments[0]); | ||
if (capturingData == null || !capturingData.unused.size) { | ||
if (capturingData == null || capturingData.isAllUsed()) { | ||
return; | ||
@@ -242,2 +322,3 @@ } | ||
replacementNode.type === "ArrowFunctionExpression") { | ||
capturingData.markAsUsed(); | ||
verifyForReplaceFunction(replacementNode, capturingData); | ||
@@ -248,4 +329,6 @@ } | ||
if (!evaluated || typeof evaluated.value !== "string") { | ||
capturingData.markAsCannotTrack(); | ||
return; | ||
} | ||
capturingData.markAsUsed(); | ||
verifyForReplacePattern(evaluated.value, capturingData); | ||
@@ -255,31 +338,18 @@ } | ||
function verifyForReplacePattern(replacementPattern, capturingData) { | ||
const unusedCapturingGroups = new Set(capturingData.unused); | ||
const unusedNames = new Map(capturingData.unusedNames); | ||
for (const replacement of replacements_utils_1.parseReplacementsForString(replacementPattern)) { | ||
if (replacement.type === "ReferenceElement") { | ||
if (typeof replacement.ref === "number") { | ||
const cgNode = capturingData.unusedIndexes.get(replacement.ref); | ||
if (cgNode) { | ||
unusedCapturingGroups.delete(cgNode); | ||
} | ||
capturingData.usedIndex(replacement.ref); | ||
} | ||
else { | ||
const cgNodes = unusedNames.get(replacement.ref); | ||
if (cgNodes) { | ||
unusedNames.delete(replacement.ref); | ||
for (const cgNode of cgNodes) { | ||
unusedCapturingGroups.delete(cgNode); | ||
} | ||
} | ||
capturingData.usedName(replacement.ref); | ||
} | ||
} | ||
} | ||
reportUnused(capturingData, unusedCapturingGroups, unusedNames); | ||
} | ||
function verifyForReplaceFunction(replacementNode, capturingData) { | ||
const unusedCapturingGroups = new Set(capturingData.unused); | ||
const unusedNames = new Map(capturingData.unusedNames); | ||
for (let index = 0; index < replacementNode.params.length; index++) { | ||
const arg = replacementNode.params[index]; | ||
if (arg.type === "RestElement") { | ||
capturingData.markAsCannotTrack(); | ||
return; | ||
@@ -290,22 +360,13 @@ } | ||
} | ||
else if (index <= capturingData.count) { | ||
const cgNode = capturingData.unusedIndexes.get(index); | ||
if (cgNode) { | ||
unusedCapturingGroups.delete(cgNode); | ||
} | ||
else if (index <= capturingData.countOfCapturingGroup) { | ||
capturingData.usedIndex(index); | ||
} | ||
else if (capturingData.count + 3 <= index) { | ||
for (const cgNodes of unusedNames.values()) { | ||
for (const cgNode of cgNodes) { | ||
unusedCapturingGroups.delete(cgNode); | ||
} | ||
} | ||
unusedNames.clear(); | ||
else if (capturingData.countOfCapturingGroup + 3 <= index) { | ||
capturingData.usedAllNames(); | ||
} | ||
} | ||
reportUnused(capturingData, unusedCapturingGroups, unusedNames); | ||
} | ||
function verifyForExec(node) { | ||
const capturingData = getCapturingData(node.callee.object); | ||
if (capturingData == null || !capturingData.unused.size) { | ||
if (capturingData == null || capturingData.isAllUsed()) { | ||
return; | ||
@@ -316,2 +377,3 @@ } | ||
} | ||
capturingData.markAsUsed(); | ||
verifyForExecResult(node, capturingData); | ||
@@ -322,6 +384,5 @@ } | ||
if (refs == null) { | ||
capturingData.markAsCannotTrack(); | ||
return; | ||
} | ||
const unusedCapturingGroups = new Set(capturingData.unused); | ||
const unusedNames = new Map(capturingData.unusedNames); | ||
for (const ref of refs) { | ||
@@ -331,18 +392,7 @@ if (ref.ref === "groups") { | ||
if (sub == null) { | ||
for (const cgNodes of unusedNames.values()) { | ||
for (const cgNode of cgNodes) { | ||
unusedCapturingGroups.delete(cgNode); | ||
} | ||
} | ||
unusedNames.clear(); | ||
capturingData.usedAllNames(); | ||
} | ||
else { | ||
for (const namedRef of sub) { | ||
const cgNodes = unusedNames.get(namedRef.ref); | ||
if (cgNodes) { | ||
unusedNames.delete(namedRef.ref); | ||
for (const cgNode of cgNodes) { | ||
unusedCapturingGroups.delete(cgNode); | ||
} | ||
} | ||
capturingData.usedName(namedRef.ref); | ||
} | ||
@@ -352,13 +402,9 @@ } | ||
else { | ||
const cgNode = capturingData.unusedIndexes.get(Number(ref.ref)); | ||
if (cgNode) { | ||
unusedCapturingGroups.delete(cgNode); | ||
} | ||
capturingData.usedIndex(Number(ref.ref)); | ||
} | ||
} | ||
reportUnused(capturingData, unusedCapturingGroups, unusedNames); | ||
} | ||
function verifyForMatchAll(node) { | ||
const capturingData = getCapturingData(node.arguments[0]); | ||
if (capturingData == null || !capturingData.unused.size) { | ||
if (capturingData == null || capturingData.isAllUsed()) { | ||
return; | ||
@@ -369,8 +415,8 @@ } | ||
} | ||
capturingData.markAsUsed(); | ||
const refs = extractUsedReferencesForIteration(node); | ||
if (refs == null) { | ||
capturingData.markAsCannotTrack(); | ||
return; | ||
} | ||
const unusedCapturingGroups = new Set(capturingData.unused); | ||
const unusedNames = new Map(capturingData.unusedNames); | ||
for (const ref of refs) { | ||
@@ -380,18 +426,7 @@ if (ref.ref === "groups") { | ||
if (sub == null) { | ||
for (const cgNodes of unusedNames.values()) { | ||
for (const cgNode of cgNodes) { | ||
unusedCapturingGroups.delete(cgNode); | ||
} | ||
} | ||
unusedNames.clear(); | ||
capturingData.usedAllNames(); | ||
} | ||
else { | ||
for (const namedRef of sub) { | ||
const cgNodes = unusedNames.get(namedRef.ref); | ||
if (cgNodes) { | ||
unusedNames.delete(namedRef.ref); | ||
for (const cgNode of cgNodes) { | ||
unusedCapturingGroups.delete(cgNode); | ||
} | ||
} | ||
capturingData.usedName(namedRef.ref); | ||
} | ||
@@ -401,9 +436,5 @@ } | ||
else { | ||
const cgNode = capturingData.unusedIndexes.get(Number(ref.ref)); | ||
if (cgNode) { | ||
unusedCapturingGroups.delete(cgNode); | ||
} | ||
capturingData.usedIndex(Number(ref.ref)); | ||
} | ||
} | ||
reportUnused(capturingData, unusedCapturingGroups, unusedNames); | ||
function extractUsedReferencesForIteration(expr) { | ||
@@ -457,36 +488,15 @@ const parent = expr.parent; | ||
function createVisitor(node, _pattern, flags, regexpNode) { | ||
const capturingData = { | ||
unused: new Set(), | ||
unusedIndexes: new Map(), | ||
unusedNames: new Map(), | ||
count: 0, | ||
flags, | ||
node, | ||
}; | ||
const capturingData = new CapturingData(node, flags); | ||
capturingDataMap.set(regexpNode, capturingData); | ||
return { | ||
onCapturingGroupEnter(cgNode) { | ||
capturingData.count++; | ||
if (!cgNode.references.length) { | ||
capturingData.unused.add(cgNode); | ||
capturingData.unusedIndexes.set(capturingData.count, cgNode); | ||
} | ||
else if (cgNode.references.some((ref) => typeof ref.ref === "string")) { | ||
return; | ||
} | ||
if (cgNode.name) { | ||
const array = capturingData.unusedNames.get(cgNode.name); | ||
if (array) { | ||
array.push(cgNode); | ||
} | ||
else { | ||
capturingData.unusedNames.set(cgNode.name, [cgNode]); | ||
} | ||
} | ||
}, | ||
}; | ||
return capturingData.visitor(); | ||
} | ||
return Object.assign(Object.assign({}, utils_1.defineRegexpVisitor(context, { | ||
createVisitor, | ||
})), { "CallExpression:exit"(node) { | ||
})), { "Program:exit"() { | ||
for (const capturingData of capturingDataMap.values()) { | ||
if (capturingData.isNeedReport()) { | ||
reportUnused(capturingData); | ||
} | ||
} | ||
}, "CallExpression:exit"(node) { | ||
if (!ast_utils_1.isKnownMethodCall(node, { | ||
@@ -493,0 +503,0 @@ match: 1, |
{ | ||
"name": "eslint-plugin-regexp", | ||
"version": "0.6.1", | ||
"version": "0.6.2", | ||
"description": "ESLint plugin for finding RegExp mistakes and RegExp style guide violations.", | ||
@@ -49,3 +49,3 @@ "main": "dist/index.js", | ||
"devDependencies": { | ||
"@ota-meshi/eslint-plugin": "^0.4.0", | ||
"@ota-meshi/eslint-plugin": "^0.5.0", | ||
"@types/eslint": "^7.2.0", | ||
@@ -68,3 +68,3 @@ "@types/eslint-scope": "^3.7.0", | ||
"eslint-plugin-prettier": "^3.3.1", | ||
"eslint-plugin-regexp": "^0.5.0", | ||
"eslint-plugin-regexp": "^0.6.0", | ||
"eslint-plugin-vue": "^7.5.0", | ||
@@ -71,0 +71,0 @@ "eslint-plugin-yml": "^0.9.0", |
7026
0.14%284913
-0.4%