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