mira-consciousness
Version:
Memory & Intelligence Retention Archive - Preserving The Spark
355 lines • 15.4 kB
JavaScript
/**
* 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