UNPKG

ripbug-ai-detector

Version:

🔥 RipBug AI Bug Detector - Built by an AI that rips its own bugs. Destroy AI-generated bugs before you commit.

179 lines • 7.68 kB
"use strict"; // Basic Cross-File Analyzer - Simple and working // No fancy features, just detect breaking changes across files Object.defineProperty(exports, "__esModule", { value: true }); exports.BasicCrossFileAnalyzer = void 0; const ast_parser_enhanced_1 = require("./ast-parser-enhanced"); const file_utils_1 = require("../utils/file-utils"); class BasicCrossFileAnalyzer { parser; constructor() { this.parser = new ast_parser_enhanced_1.EnhancedASTParser({ enableTreeSitter: true, fallbackToRegex: true, debugMode: false }); } // Just find function calls across files, compare signatures, report mismatches async detectBreakingChanges(functions, allFiles) { const issues = []; for (const func of functions) { // Skip non-exported functions (they can't be called from other files) if (!func.isExported) continue; // Find calls to this function in other files const callSites = await this.findCallsInOtherFiles(func, allFiles); for (const callSite of callSites) { // Compare what the function expects vs what the call provides const mismatch = this.compareSignatures(func, callSite); if (mismatch) { issues.push(this.createIssue(func, callSite, mismatch)); } } } return issues; } // Find calls to a function in other files async findCallsInOtherFiles(func, allFiles) { const callSites = []; for (const file of allFiles) { // Skip the file where the function is defined if (file === func.file) continue; try { const fileInfo = await file_utils_1.FileUtils.getFileInfo(file); if (!fileInfo.isJavaScript) continue; // Extract function calls from this file const calls = this.parser.extractFunctionCalls(fileInfo.content, file); // Find calls to our function for (const call of calls) { if (this.isCallToFunction(call.name, func.name)) { callSites.push({ file: call.file, line: call.line, column: call.column, context: call.context, argumentCount: call.arguments ? call.arguments.length : 0 }); } } } catch (error) { // Skip files that can't be read continue; } } return callSites; } // Check if a call name matches a function name isCallToFunction(callName, functionName) { // Direct match if (callName === functionName) return true; // Method call (obj.functionName) if (callName.endsWith(`.${functionName}`)) return true; return false; } // Compare function signature with call site compareSignatures(func, callSite) { // Count required parameters (non-optional, no default value) const requiredParams = func.parameters.filter(p => !p.optional && !p.defaultValue); const providedArgs = callSite.argumentCount; // Missing required parameters = breaking change if (providedArgs < requiredParams.length) { const missing = requiredParams.length - providedArgs; return { type: 'missing-parameters', message: `Function call missing ${missing} required parameter(s): ${func.name}()`, severity: 'error' }; } // Too many parameters = potential issue but not breaking if (providedArgs > func.parameters.length) { const extra = providedArgs - func.parameters.length; return { type: 'extra-parameters', message: `Function call has ${extra} extra parameter(s): ${func.name}()`, severity: 'warning' }; } // No mismatch return null; } // Create an issue from a signature mismatch createIssue(func, callSite, mismatch) { const requiredParams = func.parameters.filter(p => !p.optional && !p.defaultValue); return { id: `cross-file-${func.name}-${callSite.line}-${Date.now()}`, type: 'function-signature-change', severity: mismatch.severity, message: mismatch.message, file: func.file, line: func.line, column: func.column, details: { functionName: func.name, oldSignature: `${func.name}(${this.getSimpleSignature(callSite.argumentCount)})`, newSignature: `${func.name}(${this.getParameterSignature(func.parameters)})`, affectedFiles: [{ path: callSite.file, line: callSite.line, column: callSite.column, context: callSite.context, suggestion: this.generateSuggestion(func, callSite, mismatch) }], context: `Breaking call in ${callSite.file}:${callSite.line}`, breakingChangeType: mismatch.type }, suggestions: [ this.generateSuggestion(func, callSite, mismatch), 'Check if function signature was changed without updating callers', 'Verify this is an AI-generated change that broke existing code' ], confidence: 0.95 // High confidence for clear signature mismatches }; } // Generate a simple signature representation getSimpleSignature(argCount) { if (argCount === 0) return ''; return Array(argCount).fill('arg').map((_, i) => `arg${i + 1}`).join(', '); } // Generate parameter signature getParameterSignature(parameters) { return parameters.map(p => { const optional = p.optional || p.defaultValue ? '?' : ''; return `${p.name}${optional}: ${p.type || 'any'}`; }).join(', '); } // Generate specific suggestion for fixing the call generateSuggestion(func, callSite, mismatch) { if (mismatch.type === 'missing-parameters') { const requiredParams = func.parameters.filter(p => !p.optional && !p.defaultValue); const missing = requiredParams.length - callSite.argumentCount; const missingParams = requiredParams.slice(callSite.argumentCount); const suggestions = missingParams.map(p => { if (p.type === 'string') return "''"; if (p.type === 'number') return '0'; if (p.type === 'boolean') return 'false'; if (p.type && p.type.includes('{}')) return '{}'; if (p.type && p.type.includes('[]')) return '[]'; return 'undefined'; }); return `Add missing parameter(s): ${suggestions.join(', ')}`; } if (mismatch.type === 'extra-parameters') { return `Remove extra parameters from the function call`; } return 'Update call to match function signature'; } } exports.BasicCrossFileAnalyzer = BasicCrossFileAnalyzer; //# sourceMappingURL=basic-cross-file-analyzer.js.map