UNPKG

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
/** * @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 };