agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
125 lines (112 loc) • 4.46 kB
JavaScript
/**
* @file Memory leak detector
* @description Detects potential memory leaks
*/
/**
* Detects potential memory leaks
* @param {string} content - File content
* @param {string} filePath - Path to the file
* @returns {Array} Array of memory leak issues
*/
function detectMemoryLeaks(content, filePath) {
const issues = [];
const lines = content.split('\n');
const { PERFORMANCE_PATTERNS } = require('./performancePatterns');
// Check for module-level collections
lines.forEach((line, index) => {
// Skip if line is indented (likely inside a function/class)
if (line.startsWith(' ') || line.startsWith('\t')) return;
// Match module-level Map/Set declarations
const collectionMatch = line.match(/^(const|let|var)\s+(\w+)\s*=\s*new\s+(Map|Set)\s*\(\)/);
if (collectionMatch) {
const [, , varName, collectionType] = collectionMatch;
// Skip WeakMap/WeakSet as they have automatic garbage collection
if (line.includes('Weak')) return;
const patternInfo = PERFORMANCE_PATTERNS['memory_leak'];
issues.push({
type: 'memory_leak',
severity: patternInfo.severity,
category: patternInfo.category,
location: `${filePath}:${index + 1}`,
line: index + 1,
code: line.trim(),
issue: 'module-level collection without bounds',
description: `Module-level ${collectionType} "${varName}" can grow unbounded`,
summary: 'Module-level collection can cause memory leak',
recommendation: 'Use WeakMap/WeakSet, implement size limits, or clear periodically',
effort: patternInfo.effort,
impact: patternInfo.impact,
estimatedSavings: 'prevents OOM crashes and memory growth'
});
}
});
lines.forEach((line, index) => {
const lineNumber = index + 1;
// Check for common memory leak patterns
const leakPatterns = [
{
pattern: /setInterval\s*\([^)]*\)(?!.*clearInterval)/i,
issue: 'uncleaned setInterval',
recommendation: 'Store interval ID and call clearInterval in cleanup'
},
{
pattern: /setTimeout\s*\([^)]*\)(?!.*clearTimeout)/i,
issue: 'potentially uncleaned setTimeout',
recommendation: 'Consider clearTimeout if component/function can unmount early'
},
{
pattern: /addEventListener\s*\([^)]*\)(?!.*removeEventListener)/i,
issue: 'uncleaned event listener',
recommendation: 'Add removeEventListener in cleanup/unmount'
},
{
pattern: /global\.\w+\s*=\s*new\s+(Map|Set)\s*\(\)/i,
issue: 'global collection without cleanup',
recommendation: 'Clear global collections when no longer needed'
}
];
leakPatterns.forEach(({ pattern, issue, recommendation }) => {
if (pattern.test(line)) {
const patternInfo = PERFORMANCE_PATTERNS['memory_leak'];
issues.push({
type: 'memory_leak',
severity: patternInfo.severity,
category: patternInfo.category,
location: `${filePath}:${lineNumber}`,
line: lineNumber,
code: line.trim(),
issue: issue,
description: `Potential memory leak: ${issue}`,
summary: `Potential memory leak: ${issue}`,
recommendation: recommendation,
effort: patternInfo.effort,
impact: patternInfo.impact,
estimatedSavings: 'prevents OOM crashes and memory growth'
});
}
});
// Check for closures holding large objects
if (line.includes('function') && line.includes('return') && line.length > 200) {
const patternInfo = PERFORMANCE_PATTERNS['memory_leak'];
issues.push({
type: 'memory_leak',
severity: 'MEDIUM',
category: patternInfo.category,
location: `${filePath}:${lineNumber}`,
line: lineNumber,
code: line.substring(0, 100) + '...',
issue: 'large closure',
description: 'Large closure may retain references — potential memory leak',
summary: 'Large closure may retain references — potential memory leak',
recommendation: 'Review closure scope, nullify references when done',
effort: patternInfo.effort,
impact: patternInfo.impact,
estimatedSavings: 'reduces memory footprint'
});
}
});
return issues;
}
module.exports = {
detectMemoryLeaks
};