UNPKG

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