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
JavaScript
;
// 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