UNPKG

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