UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

355 lines 15.4 kB
/** * Dead Code Analyzer * Detects unreachable code, impossible conditions, and commented code blocks */ import fs from 'fs-extra'; import * as path from 'path'; import { LanguageConfigManager } from './LanguageConfigManager.js'; export class DeadCodeAnalyzer { projectRoot; languageManager; fileMetadata; constructor(projectRoot, fileMetadata) { this.projectRoot = projectRoot; this.fileMetadata = fileMetadata; this.languageManager = new LanguageConfigManager(); } async findDeadCodeBlocks() { const deadCodeBlocks = []; // Analyze each file for dead code for (const [file, metadata] of this.fileMetadata.entries()) { const filePath = path.join(this.projectRoot, file); try { const content = await fs.readFile(filePath, 'utf-8'); const blocks = await this.analyzeDeadCodeInFile(content, file, metadata.language); deadCodeBlocks.push(...blocks); } catch (error) { // Skip files that can't be read } } return deadCodeBlocks.sort((a, b) => b.size - a.size); } async analyzeDeadCodeInFile(content, file, language) { const blocks = []; const lines = content.split('\n'); // Detect unreachable code after return statements blocks.push(...this.detectUnreachableCode(lines, file)); // Detect impossible conditions blocks.push(...this.detectImpossibleConditions(lines, file)); // Detect unused variables within functions blocks.push(...this.detectUnusedVariables(lines, file, language)); // Detect large commented code blocks blocks.push(...this.detectCommentedCodeBlocks(lines, file, language)); // Detect duplicate code blocks blocks.push(...this.detectDuplicateCode(lines, file)); return blocks; } detectUnreachableCode(lines, file) { const blocks = []; let inFunction = false; let functionBraceCount = 0; let hasReturn = false; let unreachableStartLine = -1; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); // Track function boundaries if (line.includes('function') || line.includes('=>')) { inFunction = true; hasReturn = false; functionBraceCount = 0; } if (inFunction) { functionBraceCount += (line.match(/{/g) || []).length; functionBraceCount -= (line.match(/}/g) || []).length; // Check for return statement if (line.startsWith('return') || line.includes('throw')) { hasReturn = true; unreachableStartLine = i + 2; // Next line after return } // If we found a return and there's more code before function ends if (hasReturn && unreachableStartLine === i + 1 && functionBraceCount > 0) { // Look for unreachable code let unreachableEndLine = i; let codeFound = false; for (let j = i; j < lines.length && functionBraceCount > 0; j++) { const checkLine = lines[j].trim(); if (checkLine && !checkLine.startsWith('//') && !checkLine.startsWith('*')) { codeFound = true; unreachableEndLine = j + 1; } functionBraceCount += (checkLine.match(/{/g) || []).length; functionBraceCount -= (checkLine.match(/}/g) || []).length; } if (codeFound) { blocks.push({ file, startLine: unreachableStartLine, endLine: unreachableEndLine, type: 'unreachable', reason: 'Code after return statement', size: unreachableEndLine - unreachableStartLine + 1, confidence: 0.95 }); } } if (functionBraceCount === 0) { inFunction = false; hasReturn = false; } } } return blocks; } detectImpossibleConditions(lines, file) { const blocks = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; // Pattern 1: if (false) or if (true) const constantCondition = line.match(/if\s*\(\s*(true|false)\s*\)/); if (constantCondition) { const isAlwaysFalse = constantCondition[1] === 'false'; // Find the block size let blockEnd = i + 1; let braceCount = 0; let started = false; for (let j = i; j < lines.length && j < i + 100; j++) { const checkLine = lines[j]; if (checkLine.includes('{')) { started = true; braceCount += (checkLine.match(/{/g) || []).length; } if (started) { braceCount -= (checkLine.match(/}/g) || []).length; if (braceCount === 0) { blockEnd = j + 1; break; } } } blocks.push({ file, startLine: i + 1, endLine: blockEnd, type: 'impossible_condition', reason: isAlwaysFalse ? 'Condition is always false' : 'Condition is always true (consider removing if statement)', size: blockEnd - i, confidence: 1.0 }); } // Pattern 2: Contradictory conditions like if (x && !x) const contradiction = line.match(/if\s*\(\s*(\w+)\s*&&\s*!\1\s*\)/); if (contradiction) { blocks.push({ file, startLine: i + 1, endLine: i + 1, type: 'impossible_condition', reason: 'Contradictory condition - always false', size: 1, confidence: 1.0 }); } // Pattern 3: Duplicate conditions if (x) { ... } else if (x) if (line.includes('else if')) { const currentCondition = line.match(/else\s+if\s*\(([^)]+)\)/); if (currentCondition) { // Look back for the previous condition for (let j = i - 1; j >= 0 && j > i - 20; j--) { const prevLine = lines[j]; const prevCondition = prevLine.match(/if\s*\(([^)]+)\)/); if (prevCondition && prevCondition[1].trim() === currentCondition[1].trim()) { blocks.push({ file, startLine: i + 1, endLine: i + 1, type: 'impossible_condition', reason: 'Duplicate condition in else-if chain', size: 1, confidence: 0.95 }); break; } } } } } return blocks; } detectUnusedVariables(lines, file, language) { const blocks = []; if (!['javascript', 'typescript'].includes(language)) { return blocks; // Only implemented for JS/TS for now } // Simple unused variable detection within function scopes let inFunction = false; let functionStart = -1; let functionBraceCount = 0; let functionVariables = new Map(); for (let i = 0; i < lines.length; i++) { const line = lines[i]; // Detect function start if (line.includes('function') || line.includes('=>')) { inFunction = true; functionStart = i; functionBraceCount = 0; functionVariables.clear(); } if (inFunction) { functionBraceCount += (line.match(/{/g) || []).length; functionBraceCount -= (line.match(/}/g) || []).length; // Extract variable declarations const varDeclarations = line.match(/(?:const|let|var)\s+(\w+)/g); if (varDeclarations) { varDeclarations.forEach(decl => { const varName = decl.replace(/(?:const|let|var)\s+/, ''); if (!functionVariables.has(varName)) { functionVariables.set(varName, i + 1); } }); } // Check for variable usage (exclude the declaration line) functionVariables.forEach((declLine, varName) => { if (i !== declLine - 1) { const regex = new RegExp(`\\b${varName}\\b`); if (regex.test(line)) { functionVariables.delete(varName); } } }); // End of function if (functionBraceCount === 0 && functionStart !== i) { // Report unused variables functionVariables.forEach((lineNum, varName) => { blocks.push({ file, startLine: lineNum, endLine: lineNum, type: 'unused_variable', reason: `Variable '${varName}' is declared but never used`, size: 1, confidence: 0.8 }); }); inFunction = false; functionVariables.clear(); } } } return blocks; } detectCommentedCodeBlocks(lines, file, language) { const blocks = []; // Look for large blocks of commented code let commentBlockStart = -1; let consecutiveCommentLines = 0; let commentedCodeLines = 0; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); // Check if line is a comment const isComment = line.startsWith('//') || line.startsWith('/*') || line.startsWith('*') || (language === 'python' && line.startsWith('#')); if (isComment) { if (commentBlockStart === -1) { commentBlockStart = i + 1; } consecutiveCommentLines++; // Check if the comment contains code patterns const commentContent = line.replace(/^\/\/\s*|^\/\*\s*|\*\s*|^#\s*/, ''); if (this.looksLikeCode(commentContent)) { commentedCodeLines++; } } else { // End of comment block if (consecutiveCommentLines >= 5 && commentedCodeLines >= 3) { blocks.push({ file, startLine: commentBlockStart, endLine: i, type: 'commented_code', reason: 'Large block of commented code detected', size: consecutiveCommentLines, confidence: commentedCodeLines / consecutiveCommentLines }); } commentBlockStart = -1; consecutiveCommentLines = 0; commentedCodeLines = 0; } } // Handle comment block at end of file if (consecutiveCommentLines >= 5 && commentedCodeLines >= 3) { blocks.push({ file, startLine: commentBlockStart, endLine: lines.length, type: 'commented_code', reason: 'Large block of commented code detected', size: consecutiveCommentLines, confidence: commentedCodeLines / consecutiveCommentLines }); } return blocks; } detectDuplicateCode(lines, file) { const blocks = []; const minBlockSize = 5; // Minimum lines for duplicate detection // Create hashes for blocks of code const codeBlocks = new Map(); for (let i = 0; i < lines.length - minBlockSize; i++) { // Skip empty lines and comments if (!lines[i].trim() || lines[i].trim().startsWith('//')) continue; // Create a hash for the next minBlockSize lines const blockLines = []; let validLines = 0; for (let j = i; j < lines.length && validLines < minBlockSize; j++) { const line = lines[j].trim(); if (line && !line.startsWith('//')) { blockLines.push(line); validLines++; } } if (blockLines.length === minBlockSize) { const blockHash = blockLines.join('\n'); if (!codeBlocks.has(blockHash)) { codeBlocks.set(blockHash, []); } codeBlocks.get(blockHash).push({ start: i + 1, end: i + minBlockSize }); } } // Find duplicates codeBlocks.forEach((locations, hash) => { if (locations.length > 1) { // Report all but the first occurrence as duplicates for (let i = 1; i < locations.length; i++) { blocks.push({ file, startLine: locations[i].start, endLine: locations[i].end, type: 'duplicate_code', reason: `Duplicate code block (first occurrence at line ${locations[0].start})`, size: locations[i].end - locations[i].start + 1, confidence: 0.9 }); } } }); return blocks; } looksLikeCode(line) { // Common code patterns const codePatterns = [ /^\s*(?:function|class|interface|const|let|var|if|else|for|while|return|import|export)\b/, /^\s*[\w.]+\s*\(/, // Function calls /^\s*[\w.]+\s*=/, // Assignments /[{};]/, // Code delimiters /^\s*\}/, // Closing braces /^\s*\[/, // Array literals /^\s*</ // JSX/HTML ]; return codePatterns.some(pattern => pattern.test(line)); } } //# sourceMappingURL=DeadCodeAnalyzer.js.map