UNPKG

agentsqripts

Version:

Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems

177 lines (156 loc) 7.66 kB
/** * @file Main quadratic pattern detection * @description Single responsibility: Orchestrate O(n²) pattern detection */ const extractIterationVariable = require('./extractIterationVariable'); const isSinglePassOperation = require('./isSinglePassOperation'); const isPatternsMatchesIteration = require('./isPatternsMatchesIteration'); const isIteratingDifferentDatasets = require('./isIteratingDifferentDatasets'); const couldBeRelatedData = require('./couldBeRelatedData'); const checkSequentialNotNested = require('./checkSequentialNotNested'); /** * Detects O(n²) algorithmic patterns * NOTE: While some detections may be false positives (like O(n×m) patterns), * they are valuable for LLM code inspection to identify potential optimization * opportunities. Nested loops operating on related data can often be optimized * using hash maps or more efficient algorithms for 40-80% performance gains. * @param {string} content - File content * @param {string} filePath - Path to the file * @returns {Array} Array of O(n²) performance issues */ function detectQuadraticPatterns(content, filePath) { const issues = []; const lines = content.split('\n'); const { PERFORMANCE_PATTERNS } = require('../performancePatterns'); // Use a more sophisticated approach to detect true O(n²) patterns const braceStack = []; let currentIndent = 0; for (let index = 0; index < lines.length; index++) { const line = lines[index]; const lineNumber = index + 1; const trimmedLine = line.trim(); const indentLevel = line.length - line.trimStart().length; // Track brace depth more accurately const openBraces = (line.match(/{/g) || []).length; const closeBraces = (line.match(/}/g) || []).length; currentIndent += openBraces - closeBraces; // Only detect true nested iterations with proper scope analysis const iterationPatterns = [ /for\s*\([^)]*\)/, // Matches all for loops including traditional /while\s*\([^)]*\)/, /\.forEach\s*\(/, /\.map\s*\(/, /\.filter\s*\(/ // Removed flatMap - it's O(n), not O(n²) ]; // Exclude single-pass operations that are commonly O(n), not O(n²) const singlePassPatterns = [ /\.reduce\s*\(/, // reduce is O(n) /\.find\s*\(/, // find is O(n), stops early /\.some\s*\(/, // some is O(n), stops early /\.every\s*\(/ // every is O(n), stops early ]; const isIteration = iterationPatterns.some(pattern => pattern.test(trimmedLine)); const isSinglePass = singlePassPatterns.some(pattern => pattern.test(trimmedLine)); if (isIteration && !isSinglePass) { // Skip if outer loop is small/fixed size if (/for\s*\([^;]+;\s*\w+\s*<\s*\d+\s*;/.test(trimmedLine)) { const limit = trimmedLine.match(/<\s*(\d+)/)?.[1]; if (limit && parseInt(limit, 10) < 100) continue; // Small loops aren't O(n²) problems } // Look for another iteration within reasonable scope (next 20 lines) for (let j = index + 1; j < Math.min(lines.length, index + 20); j++) { const nextLine = lines[j]; const nextTrimmed = nextLine.trim(); const nextIndent = nextLine.length - nextLine.trimStart().length; // Must be indented deeper (actually nested) if (nextIndent <= indentLevel) continue; const hasNestedIteration = iterationPatterns.some(pattern => pattern.test(nextTrimmed)); if (hasNestedIteration) { // Check if it's actually operating on related data (true O(n²)) const outerVar = extractIterationVariable(trimmedLine); const innerVar = extractIterationVariable(nextTrimmed); // Skip false positives: single reduce operations are O(n), not O(n²) if (isSinglePassOperation(nextTrimmed)) { continue; } // Traditional nested for loops should always be flagged const isTraditionalNested = /for\s*\([^;]+;\s*\w+\s*</.test(trimmedLine) && /for\s*\([^;]+;\s*\w+\s*</.test(nextTrimmed); if (isTraditionalNested) { const pattern = PERFORMANCE_PATTERNS['quadratic_time'] || { severity: 'HIGH', category: 'Algorithm', effort: 3, impact: '90%+ reduction from O(n²) to O(n)' }; issues.push({ type: 'o_n_squared', severity: pattern.severity, category: pattern.category, location: `${filePath}:${lineNumber}-${j + 1}`, line: j + 1, outerLoop: lineNumber, innerLoop: j + 1, code: nextTrimmed, description: `Traditional nested loops creating O(n²) complexity`, summary: 'Nested for loops creating O(n²) complexity', recommendation: 'Use hash maps, sets, or optimize algorithm to reduce complexity', effort: pattern.effort, impact: pattern.impact, estimatedSavings: '50-90% latency reduction for large datasets' }); continue; } // Enhanced false positive detection with improved accuracy const isFalsePositive = ( // Different array operations (O(n×m), not O(n²)) - check for different data sources outerVar && innerVar && outerVar !== innerVar && !couldBeRelatedData(outerVar, innerVar, lines, index, j) || // Iterating over constants (not O(n²)) /\b(CONSTANTS?|PERMISSIONS?|ROLES?|TYPES?|CONFIG|patterns?|matches?)\b/i.test(nextTrimmed) || // Different iteration variables suggest different datasets isIteratingDifferentDatasets(trimmedLine, nextTrimmed) || // Single element lookup patterns nextTrimmed.includes('.map(x => x.') || nextTrimmed.includes('= patterns.') || nextTrimmed.includes('patterns[') || // Check for sequential operations (not nested) checkSequentialNotNested(lines, index, j) || // Patterns → matches iteration (O(n×m), not O(n²)) isPatternsMatchesIteration(trimmedLine, nextTrimmed) ); if (!isFalsePositive) { const pattern = PERFORMANCE_PATTERNS['quadratic_time'] || { severity: 'HIGH', category: 'Algorithm', effort: 3, impact: '90%+ reduction from O(n²) to O(n)' }; issues.push({ type: 'o_n_squared', severity: pattern.severity, category: pattern.category, location: `${filePath}:${lineNumber}-${j + 1}`, line: j + 1, outerLoop: lineNumber, innerLoop: j + 1, code: nextTrimmed, description: `Nested loops creating O(n²) time complexity. Outer: ${outerVar || 'array'}, Inner: ${innerVar || 'array'}`, summary: 'Nested loops creating O(n²) complexity', recommendation: 'Use hash maps, sets, or optimize algorithm to reduce complexity', effort: pattern.effort, impact: pattern.impact, estimatedSavings: '50-90% latency reduction for large datasets' }); } // Skip past the detected pattern to avoid duplicates index = j; break; } } } } return issues; } module.exports = detectQuadraticPatterns;