UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

513 lines 19.6 kB
/** * Language-Specific Code Analyzer * Handles language-specific patterns and analysis for unused code detection */ import fs from 'fs-extra'; import * as path from 'path'; import { LanguageConfigManager } from './LanguageConfigManager.js'; export class LanguageSpecificAnalyzer { projectRoot; languageManager; fileMetadata; constructor(projectRoot, fileMetadata) { this.projectRoot = projectRoot; this.fileMetadata = fileMetadata; this.languageManager = new LanguageConfigManager(); } async analyzeByLanguage() { const results = []; // Group files by language const filesByLanguage = new Map(); for (const [file, metadata] of this.fileMetadata.entries()) { if (!filesByLanguage.has(metadata.language)) { filesByLanguage.set(metadata.language, []); } filesByLanguage.get(metadata.language).push(file); } // Analyze each language for (const [language, files] of filesByLanguage.entries()) { if (language === 'unknown') continue; const result = await this.analyzeLanguage(language, files); if (result) { results.push(result); } } return results; } async analyzeLanguage(language, files) { switch (language) { case 'javascript': return this.analyzeJavaScript(files); case 'python': return this.analyzePython(files); case 'java': return this.analyzeJava(files); case 'csharp': return this.analyzeCSharp(files); case 'go': return this.analyzeGo(files); case 'rust': return this.analyzeRust(files); case 'php': return this.analyzePHP(files); case 'ruby': return this.analyzeRuby(files); default: return null; } } async analyzeJavaScript(files) { const issues = []; const recommendations = []; // Check for console.log statements let consoleLogCount = 0; // Check for debugger statements let debuggerCount = 0; // Check for TODO/FIXME comments let todoCount = 0; // Check for unused React hooks let unusedHooksCount = 0; for (const file of files) { try { const content = await fs.readFile(path.join(this.projectRoot, file), 'utf-8'); // Count console.log const consoleLogs = content.match(/console\.(log|warn|error|debug)/g); if (consoleLogs) { consoleLogCount += consoleLogs.length; } // Count debugger statements const debuggers = content.match(/\bdebugger\b/g); if (debuggers) { debuggerCount += debuggers.length; } // Count TODOs const todos = content.match(/\/\/\s*(TODO|FIXME|HACK|XXX|BUG):/gi); if (todos) { todoCount += todos.length; } // Check for unused React hooks (simplified) if (content.includes('react')) { const hookDeclarations = content.match(/const\s+\[(\w+),\s*set\w+\]\s*=\s*useState/g); if (hookDeclarations) { hookDeclarations.forEach(decl => { const stateVar = decl.match(/const\s+\[(\w+),/)?.[1]; if (stateVar) { const usageCount = (content.match(new RegExp(`\\b${stateVar}\\b`, 'g')) || []).length; if (usageCount <= 1) { // Only in declaration unusedHooksCount++; } } }); } } } catch (error) { // Skip files that can't be read } } // Generate issues and recommendations if (consoleLogCount > 0) { issues.push(`Found ${consoleLogCount} console statements in production code`); recommendations.push('Remove or replace console statements with proper logging'); } if (debuggerCount > 0) { issues.push(`Found ${debuggerCount} debugger statements`); recommendations.push('Remove all debugger statements before production'); } if (todoCount > 0) { issues.push(`Found ${todoCount} TODO/FIXME comments`); recommendations.push('Address TODO items or create tickets for tracking'); } if (unusedHooksCount > 0) { issues.push(`Found ${unusedHooksCount} potentially unused React hooks`); recommendations.push('Remove unused React state variables and hooks'); } return { language: 'JavaScript/TypeScript', totalFiles: files.length, issues, recommendations, patterns: { 'console-statements': consoleLogCount, 'debugger-statements': debuggerCount, 'todo-comments': todoCount, 'unused-hooks': unusedHooksCount } }; } async analyzePython(files) { const issues = []; const recommendations = []; let printStatements = 0; let pdbImports = 0; let todoCount = 0; let unusedImports = 0; for (const file of files) { try { const content = await fs.readFile(path.join(this.projectRoot, file), 'utf-8'); // Count print statements const prints = content.match(/\bprint\s*\(/g); if (prints) { printStatements += prints.length; } // Check for pdb imports if (content.includes('import pdb') || content.includes('pdb.set_trace')) { pdbImports++; } // Count TODOs const todos = content.match(/#\s*(TODO|FIXME|HACK|XXX|BUG):/gi); if (todos) { todoCount += todos.length; } // Simple unused import detection const imports = content.match(/^(?:from\s+\S+\s+)?import\s+([^#\n]+)/gm); if (imports) { imports.forEach(imp => { const importedNames = imp.replace(/.*import\s+/, '').split(',').map(n => n.trim()); importedNames.forEach(name => { const simpleName = name.split(' as ')[0].trim(); const usageCount = (content.match(new RegExp(`\\b${simpleName}\\b`, 'g')) || []).length; if (usageCount <= 1) { unusedImports++; } }); }); } } catch (error) { // Skip files that can't be read } } if (printStatements > 0) { issues.push(`Found ${printStatements} print statements`); recommendations.push('Replace print statements with proper logging (use logging module)'); } if (pdbImports > 0) { issues.push(`Found ${pdbImports} files with pdb debugger imports`); recommendations.push('Remove pdb imports and set_trace calls'); } if (unusedImports > 0) { issues.push(`Found ${unusedImports} potentially unused imports`); recommendations.push('Remove unused imports (consider using autoflake or isort)'); } return { language: 'Python', totalFiles: files.length, issues, recommendations, patterns: { 'print-statements': printStatements, 'pdb-debugger': pdbImports, 'todo-comments': todoCount, 'unused-imports': unusedImports } }; } async analyzeJava(files) { const issues = []; const recommendations = []; let systemOutPrint = 0; let printStackTrace = 0; let todoCount = 0; let emptyTryCatch = 0; for (const file of files) { try { const content = await fs.readFile(path.join(this.projectRoot, file), 'utf-8'); // Count System.out.print const sysOuts = content.match(/System\.out\.(print|println)/g); if (sysOuts) { systemOutPrint += sysOuts.length; } // Count printStackTrace const stackTraces = content.match(/\.printStackTrace\(\)/g); if (stackTraces) { printStackTrace += stackTraces.length; } // Count TODOs const todos = content.match(/\/\/\s*(TODO|FIXME|HACK|XXX|BUG):/gi); if (todos) { todoCount += todos.length; } // Check for empty catch blocks const emptyCatch = content.match(/catch\s*\([^)]+\)\s*{\s*}/g); if (emptyCatch) { emptyTryCatch += emptyCatch.length; } } catch (error) { // Skip files that can't be read } } if (systemOutPrint > 0) { issues.push(`Found ${systemOutPrint} System.out.print statements`); recommendations.push('Use proper logging framework (SLF4J, Log4j2)'); } if (printStackTrace > 0) { issues.push(`Found ${printStackTrace} printStackTrace calls`); recommendations.push('Use logger.error() with proper exception handling'); } if (emptyTryCatch > 0) { issues.push(`Found ${emptyTryCatch} empty catch blocks`); recommendations.push('Handle exceptions properly or at least log them'); } return { language: 'Java', totalFiles: files.length, issues, recommendations, patterns: { 'system-out-print': systemOutPrint, 'stack-trace-print': printStackTrace, 'todo-comments': todoCount, 'empty-catch-blocks': emptyTryCatch } }; } async analyzeCSharp(files) { const issues = []; const recommendations = []; let consoleWriteLine = 0; let todoCount = 0; let catchException = 0; for (const file of files) { try { const content = await fs.readFile(path.join(this.projectRoot, file), 'utf-8'); // Count Console.WriteLine const consoles = content.match(/Console\.(Write|WriteLine)/g); if (consoles) { consoleWriteLine += consoles.length; } // Count TODOs const todos = content.match(/\/\/\s*(TODO|FIXME|HACK|XXX|BUG):/gi); if (todos) { todoCount += todos.length; } // Check for catch(Exception) const genericCatch = content.match(/catch\s*\(\s*Exception\s+/g); if (genericCatch) { catchException += genericCatch.length; } } catch (error) { // Skip files that can't be read } } if (consoleWriteLine > 0) { issues.push(`Found ${consoleWriteLine} Console.Write statements`); recommendations.push('Use ILogger interface for logging'); } if (catchException > 0) { issues.push(`Found ${catchException} generic catch(Exception) blocks`); recommendations.push('Catch specific exceptions when possible'); } return { language: 'C#', totalFiles: files.length, issues, recommendations, patterns: { 'console-write': consoleWriteLine, 'todo-comments': todoCount, 'generic-catch': catchException } }; } async analyzeGo(files) { const issues = []; const recommendations = []; let fmtPrintln = 0; let todoCount = 0; let ignoredErrors = 0; for (const file of files) { try { const content = await fs.readFile(path.join(this.projectRoot, file), 'utf-8'); // Count fmt.Print statements const fmtPrints = content.match(/fmt\.(Print|Println|Printf)/g); if (fmtPrints) { fmtPrintln += fmtPrints.length; } // Count TODOs const todos = content.match(/\/\/\s*(TODO|FIXME|HACK|XXX|BUG):/gi); if (todos) { todoCount += todos.length; } // Check for ignored errors const ignored = content.match(/\s_\s*,\s*err\s*:=/g); if (ignored) { ignoredErrors += ignored.length; } } catch (error) { // Skip files that can't be read } } if (fmtPrintln > 0) { issues.push(`Found ${fmtPrintln} fmt.Print statements`); recommendations.push('Use structured logging (logrus, zap, zerolog)'); } if (ignoredErrors > 0) { issues.push(`Found ${ignoredErrors} ignored errors`); recommendations.push('Handle all errors explicitly'); } return { language: 'Go', totalFiles: files.length, issues, recommendations, patterns: { 'fmt-print': fmtPrintln, 'todo-comments': todoCount, 'ignored-errors': ignoredErrors } }; } async analyzeRust(files) { const issues = []; const recommendations = []; let printlnMacro = 0; let todoCount = 0; let unwrapCalls = 0; for (const file of files) { try { const content = await fs.readFile(path.join(this.projectRoot, file), 'utf-8'); // Count println! macros const printlns = content.match(/println!/g); if (printlns) { printlnMacro += printlns.length; } // Count TODOs const todos = content.match(/\/\/\s*(TODO|FIXME|HACK|XXX|BUG):/gi); if (todos) { todoCount += todos.length; } // Count unwrap() calls const unwraps = content.match(/\.unwrap\(\)/g); if (unwraps) { unwrapCalls += unwraps.length; } } catch (error) { // Skip files that can't be read } } if (printlnMacro > 0) { issues.push(`Found ${printlnMacro} println! macros`); recommendations.push('Use proper logging crate (log, env_logger, tracing)'); } if (unwrapCalls > 0) { issues.push(`Found ${unwrapCalls} unwrap() calls`); recommendations.push('Handle Results and Options properly with ? operator or match'); } return { language: 'Rust', totalFiles: files.length, issues, recommendations, patterns: { 'println-macro': printlnMacro, 'todo-comments': todoCount, 'unwrap-calls': unwrapCalls } }; } async analyzePHP(files) { const issues = []; const recommendations = []; let echoStatements = 0; let varDump = 0; let todoCount = 0; for (const file of files) { try { const content = await fs.readFile(path.join(this.projectRoot, file), 'utf-8'); // Count echo statements const echos = content.match(/\becho\s+/g); if (echos) { echoStatements += echos.length; } // Count var_dump const dumps = content.match(/\bvar_dump\s*\(/g); if (dumps) { varDump += dumps.length; } // Count TODOs const todos = content.match(/\/\/\s*(TODO|FIXME|HACK|XXX|BUG):/gi); if (todos) { todoCount += todos.length; } } catch (error) { // Skip files that can't be read } } if (echoStatements > 0) { issues.push(`Found ${echoStatements} echo statements`); recommendations.push('Use proper templating or structured output'); } if (varDump > 0) { issues.push(`Found ${varDump} var_dump calls`); recommendations.push('Remove debugging var_dump calls'); } return { language: 'PHP', totalFiles: files.length, issues, recommendations, patterns: { 'echo-statements': echoStatements, 'var-dump': varDump, 'todo-comments': todoCount } }; } async analyzeRuby(files) { const issues = []; const recommendations = []; let putsStatements = 0; let pStatements = 0; let todoCount = 0; for (const file of files) { try { const content = await fs.readFile(path.join(this.projectRoot, file), 'utf-8'); // Count puts statements const puts = content.match(/\bputs\s+/g); if (puts) { putsStatements += puts.length; } // Count p statements const ps = content.match(/\bp\s+/g); if (ps) { pStatements += ps.length; } // Count TODOs const todos = content.match(/#\s*(TODO|FIXME|HACK|XXX|BUG):/gi); if (todos) { todoCount += todos.length; } } catch (error) { // Skip files that can't be read } } if (putsStatements > 0) { issues.push(`Found ${putsStatements} puts statements`); recommendations.push('Use proper logging (Logger class)'); } if (pStatements > 0) { issues.push(`Found ${pStatements} p debug statements`); recommendations.push('Remove debug p statements'); } return { language: 'Ruby', totalFiles: files.length, issues, recommendations, patterns: { 'puts-statements': putsStatements, 'p-debug': pStatements, 'todo-comments': todoCount } }; } } //# sourceMappingURL=LanguageSpecificAnalyzer.js.map