agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
170 lines (156 loc) • 6.13 kB
JavaScript
/**
* @file Dead code detection for cleanup optimization
* @description Single responsibility: Identify unused variables, functions, and unreachable code patterns
*
* This module implements comprehensive dead code detection using two-pass analysis to identify
* unused declarations, unreachable code after return statements, commented-out code blocks,
* and empty function definitions. It uses static analysis to track declaration-usage relationships
* while avoiding false positives from dynamic code patterns.
*
* Design rationale:
* - Two-pass analysis separates declaration discovery from usage tracking
* - Pattern-based detection handles multiple dead code scenarios
* - Conservative approach minimizes false positives in dynamic JavaScript
* - Line-based analysis maintains performance for large files
*/
/**
* Comprehensive dead code detection using multi-pattern static analysis
*
* Technical function: Two-pass analysis to identify unused declarations and unreachable code
*
* Implementation rationale:
* - Two-pass algorithm separates concerns: first collect declarations, then track usage
* - Map-based declaration storage enables O(1) lookup during usage analysis
* - Set-based usage tracking avoids duplicate counting of identifier references
* - Multiple dead code pattern detection covers comprehensive cleanup scenarios
*
* Dead code detection strategy:
* - Pass 1: Collect all variable and function declarations with line numbers
* - Pass 2: Scan for usage of declared identifiers (excluding declaration lines)
* - Pattern matching: Identify unreachable code, commented code, empty functions
* - Conservative analysis: Avoid false positives from dynamic code patterns
*
* Algorithm complexity considerations:
* - O(n) time complexity where n is number of lines
* - O(d + u) space complexity where d=declarations, u=unique usages
* - Regex-based pattern matching optimized for common JavaScript constructs
* - Line-by-line processing maintains memory efficiency for large files
*
* JavaScript-specific challenges:
* - Dynamic property access: obj[variableName] usage detection
* - Eval and dynamic code: Variables used in string contexts
* - Closures and hoisting: Complex scope-based usage patterns
* - Module exports: Variables used only for export purposes
*
* False positive mitigation:
* - Exclude declaration lines from usage scanning to avoid self-references
* - Conservative identifier matching prevents partial word matches
* - Skip analysis of potentially dynamic contexts (eval, with statements)
* - Manual verification recommended for critical refactoring decisions
*
* @param {string} content - JavaScript file content to analyze for dead code patterns
* @returns {Array<Object>} Array of dead code issues with type, line, and description
* @example
* const issues = findDeadCode(`
* const unused = 42;
* function helper() { return 1; }
* function main() { return 2; }
* main();
* `);
* // Returns: [{ name: 'unused', type: 'unused_variable', line: 1, description: '...' }]
*/
function findDeadCode(content) {
const issues = [];
const lines = content.split('\n');
// Find declared variables and functions
const declarations = new Map();
const usages = new Set();
// First pass: collect declarations
lines.forEach((line, index) => {
const trimmed = line.trim();
// Variable declarations
const varMatch = trimmed.match(/(?:const|let|var)\s+(\w+)/);
if (varMatch) {
declarations.set(varMatch[1], {
name: varMatch[1],
type: 'variable',
line: index + 1,
declaration: trimmed
});
}
// Function declarations
const funcMatch = trimmed.match(/function\s+(\w+)/);
if (funcMatch) {
declarations.set(funcMatch[1], {
name: funcMatch[1],
type: 'function',
line: index + 1,
declaration: trimmed
});
}
});
// Second pass: find usages (exclude declaration lines)
lines.forEach((line, index) => {
// Skip declaration lines to avoid marking as used
const trimmed = line.trim();
if (trimmed.match(/(?:const|let|var)\s+\w+/) || trimmed.match(/function\s+\w+/)) {
return;
}
// Find identifiers in usage context
line.replace(/\b(\w+)\b/g, (match, identifier) => {
if (declarations.has(identifier)) {
usages.add(identifier);
}
return match;
});
});
// Find unused declarations
declarations.forEach((declaration, name) => {
if (!usages.has(name)) {
issues.push({
name: name,
type: 'unused_' + declaration.type,
line: declaration.line,
description: `Unused ${declaration.type}: ${name}`
});
}
});
// Additional dead code patterns
lines.forEach((line, index) => {
const trimmed = line.trim();
// Check for unreachable code after return
if (trimmed.startsWith('return') && index < lines.length - 1) {
const nextLine = lines[index + 1];
if (nextLine && nextLine.trim() && !nextLine.trim().startsWith('}') && !nextLine.trim().startsWith('//')) {
issues.push({
type: 'unreachable_code',
line: index + 2,
description: 'Code after return statement is unreachable'
});
}
}
// Check for commented out code blocks
if (trimmed.startsWith('//') && trimmed.length > 10) {
const commentContent = trimmed.substring(2).trim();
if (commentContent.includes('function') || commentContent.includes('const') || commentContent.includes('=')) {
issues.push({
type: 'commented_code',
line: index + 1,
description: 'Commented out code should be removed'
});
}
}
// Check for empty functions
if (trimmed.includes('function') && line.includes('{}')) {
issues.push({
type: 'empty_function',
line: index + 1,
description: 'Empty function should be implemented or removed'
});
}
});
return issues;
}
module.exports = {
findDeadCode
};