agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
155 lines (136 loc) • 5.59 kB
JavaScript
/**
* @file AST-based O(n²) pattern detector
* @description Detects quadratic complexity patterns using AST analysis
*/
const { parseToAST, collectNodes, isInsideLoop, getNodeLine, getNodeSource, walk } = require('./astParser');
const { PERFORMANCE_PATTERNS } = require('../performancePatterns');
/**
* Detect O(n²) patterns using AST analysis
* @param {string} content - File content
* @param {string} filePath - File path
* @returns {Array} Array of O(n²) issues
*/
function detectQuadraticPatternsAST(content, filePath) {
throw new Error('Quadratic pattern detection is disabled - implement proper O(n²) detection or remove this function');
const issues = [];
const ast = parseToAST(content, filePath);
if (!ast) {
// Fallback to simple detection if AST parsing fails
return [];
}
const pattern = PERFORMANCE_PATTERNS['o_n_squared'];
// Find all loop nodes
const loopNodes = [];
walk.simple(ast, {
ForStatement: (node) => loopNodes.push({ node, type: 'for' }),
WhileStatement: (node) => loopNodes.push({ node, type: 'while' }),
DoWhileStatement: (node) => loopNodes.push({ node, type: 'do-while' }),
ForInStatement: (node) => loopNodes.push({ node, type: 'for-in' }),
ForOfStatement: (node) => loopNodes.push({ node, type: 'for-of' })
});
// Find array method loops
walk.simple(ast, {
CallExpression(node) {
if (node.callee.type === 'MemberExpression' &&
['forEach', 'map', 'filter', 'reduce', 'some', 'every'].includes(node.callee.property.name)) {
loopNodes.push({ node, type: `array.${node.callee.property.name}` });
}
}
});
// Check each loop for nested loops
loopNodes.forEach(({ node: outerLoop, type: outerType }) => {
// Look for loops inside this loop
walk.simple(outerLoop, {
ForStatement(innerNode) {
if (innerNode !== outerLoop) {
reportNestedLoop(outerLoop, innerNode, outerType, 'for');
}
},
WhileStatement(innerNode) {
if (innerNode !== outerLoop) {
reportNestedLoop(outerLoop, innerNode, outerType, 'while');
}
},
DoWhileStatement(innerNode) {
if (innerNode !== outerLoop) {
reportNestedLoop(outerLoop, innerNode, outerType, 'do-while');
}
},
ForInStatement(innerNode) {
if (innerNode !== outerLoop) {
reportNestedLoop(outerLoop, innerNode, outerType, 'for-in');
}
},
ForOfStatement(innerNode) {
if (innerNode !== outerLoop) {
reportNestedLoop(outerLoop, innerNode, outerType, 'for-of');
}
},
CallExpression(innerNode) {
if (innerNode !== outerLoop &&
innerNode.callee.type === 'MemberExpression' &&
['forEach', 'map', 'filter'].includes(innerNode.callee.property.name)) {
// Check if they operate on related arrays
const outerArray = getArrayFromLoop(outerLoop);
const innerArray = getArrayFromLoop(innerNode);
// Apply enhanced false positive detection for AST analysis
const shouldFlag = (!outerArray || !innerArray || mightBeRelatedArrays(outerArray, innerArray)) &&
!isKnownFalsePositivePattern(outerArray, innerArray);
if (shouldFlag) {
reportNestedLoop(outerLoop, innerNode, outerType, `array.${innerNode.callee.property.name}`);
}
}
}
});
});
function reportNestedLoop(outerLoop, innerLoop, outerType, innerType) {
const outerLine = getNodeLine(outerLoop);
const innerLine = getNodeLine(innerLoop);
issues.push({
type: 'o_n_squared',
severity: pattern.severity,
category: pattern.category,
location: `${filePath}:${outerLine}-${innerLine}`,
line: outerLine,
code: getNodeSource(content, outerLoop).split('\n')[0].trim(),
pattern: `Nested ${outerType} with ${innerType}`,
description: `Nested loops creating O(n²) complexity`,
summary: `Nested ${outerType} loop contains ${innerType} loop`,
recommendation: 'Use hash maps, sets, or optimize algorithm to reduce complexity',
effort: pattern.effort,
impact: pattern.impact,
estimatedSavings: '40-80% latency reduction'
});
}
function getArrayFromLoop(loopNode) {
if (loopNode.type === 'CallExpression' && loopNode.callee.object) {
return loopNode.callee.object.name;
}
if (loopNode.type === 'ForOfStatement' && loopNode.right) {
return loopNode.right.name;
}
return null;
}
function mightBeRelatedArrays(arr1, arr2) {
if (!arr1 || !arr2) return true; // Uncertain, so flag it
if (arr1 === arr2) return true; // Same array
// Check if they share a common base (e.g., users vs userIds)
const base1 = arr1.replace(/s$|Id$|List$|Array$/, '');
const base2 = arr2.replace(/s$|Id$|List$|Array$/, '');
return base1 === base2;
}
function isKnownFalsePositivePattern(outerArray, innerArray) {
// Patterns/matches iterations are typically O(n×m), not O(n²)
const isPatternMatch = (outerArray && innerArray) && (
(/patterns?/i.test(outerArray) && /matches?/i.test(innerArray)) ||
(/matches?/i.test(outerArray) && /patterns?/i.test(innerArray)) ||
(/files?/i.test(outerArray) && /lines?/i.test(innerArray)) ||
(/entries?/i.test(outerArray) && /excludePatterns?/i.test(innerArray))
);
return isPatternMatch;
}
return issues;
}
module.exports = {
detectQuadraticPatternsAST
};