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