UNPKG

@aaswe/codebase-ai

Version:

AI-Assisted Software Engineering (AASWE) - Rich codebase context for IDE LLMs

579 lines 22 kB
"use strict"; /** * Go AST Analyzer * Implements AST analysis for Go files using regex-based parsing */ Object.defineProperty(exports, "__esModule", { value: true }); exports.GoAnalyzer = void 0; const BaseAnalyzer_1 = require("./BaseAnalyzer"); class GoAnalyzer extends BaseAnalyzer_1.BaseAnalyzer { constructor() { super('go'); } async parseAST(content, _context) { try { return this.parseGoWithRegex(content); } catch (error) { throw new Error(`Failed to parse Go AST: ${error}`); } } async extractNodes(ast, context) { const nodes = []; // Extract package declaration if (ast.packageDeclaration) { nodes.push({ id: this.generateId(), type: 'PackageDeclaration', name: ast.packageDeclaration.name, startLine: ast.packageDeclaration.line, endLine: ast.packageDeclaration.line, startColumn: 0, endColumn: 0, filePath: context.filePath, parent: undefined, children: [], properties: { packageName: ast.packageDeclaration.name } }); } // Extract import declarations for (const imp of ast.imports || []) { nodes.push({ id: this.generateId(), type: 'ImportDeclaration', name: imp.path, startLine: imp.line, endLine: imp.line, startColumn: 0, endColumn: 0, filePath: context.filePath, parent: undefined, children: [], properties: { importPath: imp.path, alias: imp.alias } }); } // Extract function declarations for (const func of ast.functions || []) { nodes.push({ id: this.generateId(), type: 'FunctionDeclaration', name: func.name, startLine: func.startLine, endLine: func.endLine, startColumn: 0, endColumn: 0, filePath: context.filePath, parent: undefined, children: [], properties: { parameters: func.parameters, returnTypes: func.returnTypes, receiver: func.receiver } }); } // Extract struct declarations (Go's equivalent to classes) for (const struct of ast.structs || []) { const structNode = { id: this.generateId(), type: 'StructDeclaration', name: struct.name, startLine: struct.startLine, endLine: struct.endLine, startColumn: 0, endColumn: 0, filePath: context.filePath, parent: undefined, children: [], properties: { fields: struct.fields } }; nodes.push(structNode); // Extract struct fields for (const field of struct.fields || []) { nodes.push({ id: this.generateId(), type: 'FieldDeclaration', name: field.name, startLine: field.line, endLine: field.line, startColumn: 0, endColumn: 0, filePath: context.filePath, parent: structNode.id, children: [], properties: { type: field.type, tags: field.tags } }); } } // Extract interface declarations for (const iface of ast.interfaces || []) { const interfaceNode = { id: this.generateId(), type: 'InterfaceDeclaration', name: iface.name, startLine: iface.startLine, endLine: iface.endLine, startColumn: 0, endColumn: 0, filePath: context.filePath, parent: undefined, children: [], properties: { methods: iface.methods } }; nodes.push(interfaceNode); } return nodes; } async extractFunctions(ast, context) { const functions = []; for (const func of ast.functions || []) { const goFunc = { id: this.generateId(), name: func.name, parameters: this.extractGoParameters(func.parameters || []), returnType: func.returnTypes ? func.returnTypes.join(', ') : undefined, complexity: this.calculateGoFunctionComplexity(func), startLine: func.startLine, endLine: func.endLine, filePath: context.filePath, isAsync: false, // Go uses goroutines, not async/await isExported: this.isGoExported(func.name), visibility: this.isGoExported(func.name) ? 'public' : 'private', dependencies: this.extractGoFunctionDependencies(func), calls: this.extractGoFunctionCalls(func) }; functions.push(goFunc); } return functions; } async extractClasses(ast, context) { const classes = []; // In Go, structs are the closest equivalent to classes for (const struct of ast.structs || []) { const structClass = { id: this.generateId(), name: struct.name, methods: this.extractGoStructMethods(ast.functions || [], struct.name), properties: this.extractGoStructProperties(struct), extends: undefined, // Go doesn't have inheritance implements: this.extractGoStructInterfaces(ast.interfaces || [], struct.name), startLine: struct.startLine, endLine: struct.endLine, filePath: context.filePath, isExported: this.isGoExported(struct.name), visibility: this.isGoExported(struct.name) ? 'public' : 'private', isAbstract: false // Go doesn't have abstract structs }; classes.push(structClass); } return classes; } async extractImports(ast, context) { const imports = []; for (const imp of ast.imports || []) { const importDecl = { id: this.generateId(), source: imp.path, imports: [{ name: imp.alias || this.getGoPackageName(imp.path), alias: imp.alias || undefined, isDefault: false, isNamespace: true }], filePath: context.filePath, startLine: imp.line, endLine: imp.line }; imports.push(importDecl); } return imports; } async extractExports(ast, context) { const exports = []; // In Go, exported items start with capital letters // Exported functions for (const func of ast.functions || []) { if (this.isGoExported(func.name)) { exports.push({ id: this.generateId(), name: func.name, type: 'function', isDefault: false, filePath: context.filePath, startLine: func.startLine, endLine: func.endLine }); } } // Exported structs for (const struct of ast.structs || []) { if (this.isGoExported(struct.name)) { exports.push({ id: this.generateId(), name: struct.name, type: 'class', isDefault: false, filePath: context.filePath, startLine: struct.startLine, endLine: struct.endLine }); } } // Exported interfaces for (const iface of ast.interfaces || []) { if (this.isGoExported(iface.name)) { exports.push({ id: this.generateId(), name: iface.name, type: 'interface', isDefault: false, filePath: context.filePath, startLine: iface.startLine, endLine: iface.endLine }); } } return exports; } async extractDependencies(ast, _context) { const dependencies = new Set(); // Add imports as dependencies for (const imp of ast.imports || []) { const packageName = this.getGoPackageName(imp.path); dependencies.add(packageName); } return Array.from(dependencies); } async calculateComplexity(ast, _context) { let cyclomaticComplexity = 1; let cognitiveComplexity = 0; let linesOfCode = 0; for (const func of ast.functions || []) { cyclomaticComplexity += this.calculateGoFunctionComplexity(func); cognitiveComplexity += this.calculateGoCognitiveComplexity(func); linesOfCode += func.endLine - func.startLine + 1; } const maintainabilityIndex = Math.max(0, 171 - 5.2 * Math.log(linesOfCode) - 0.23 * cyclomaticComplexity); const technicalDebt = Math.max(0, (cyclomaticComplexity - 10) * 0.5 + (cognitiveComplexity - 15) * 0.3); return { cyclomaticComplexity, cognitiveComplexity, linesOfCode, maintainabilityIndex, technicalDebt }; } getParserConfig() { return { language: 'go', sourceType: 'module', allowImportExportEverywhere: false, allowReturnOutsideFunction: false, strictMode: true, plugins: [] }; } // Helper methods parseGoWithRegex(content) { const result = { packageDeclaration: null, imports: [], functions: [], structs: [], interfaces: [] }; // Parse package declaration const packageMatch = content.match(/package\s+(\w+)/); if (packageMatch) { result.packageDeclaration = { name: packageMatch[1], line: this.findLineNumber(content, packageMatch.index || 0) }; } // Parse imports const importRegex = /import\s+(?:(\w+)\s+)?"([^"]+)"/g; let importMatch; while ((importMatch = importRegex.exec(content)) !== null) { result.imports.push({ path: importMatch[2], alias: importMatch[1] || null, line: this.findLineNumber(content, importMatch.index) }); } // Parse import blocks const importBlockRegex = /import\s*\(\s*([\s\S]*?)\s*\)/g; let importBlockMatch; while ((importBlockMatch = importBlockRegex.exec(content)) !== null) { const importBlock = importBlockMatch[1]; const importLineRegex = /(?:(\w+)\s+)?"([^"]+)"/g; let importLineMatch; while ((importLineMatch = importLineRegex.exec(importBlock)) !== null) { result.imports.push({ path: importLineMatch[2], alias: importLineMatch[1] || null, line: this.findLineNumber(content, importBlockMatch.index + importLineMatch.index) }); } } // Parse functions const funcRegex = /func\s+(?:\(([^)]*)\)\s+)?(\w+)\s*\(([^)]*)\)\s*(?:\(([^)]*)\)|(\w+(?:\[\])*))?\s*\{/g; let funcMatch; while ((funcMatch = funcRegex.exec(content)) !== null) { const receiver = funcMatch[1]; const name = funcMatch[2]; const params = funcMatch[3]; const returnTypes = funcMatch[4] || funcMatch[5]; const startLine = this.findLineNumber(content, funcMatch.index); const endLine = this.findGoFunctionEndLine(content, funcMatch.index); result.functions.push({ name, receiver, parameters: this.parseGoParameters(params), returnTypes: returnTypes ? this.parseGoReturnTypes(returnTypes) : [], startLine, endLine, body: this.extractGoFunctionBody(content, funcMatch.index) }); } // Parse structs const structRegex = /type\s+(\w+)\s+struct\s*\{([^}]*)\}/g; let structMatch; while ((structMatch = structRegex.exec(content)) !== null) { const name = structMatch[1]; const fieldsStr = structMatch[2]; result.structs.push({ name, startLine: this.findLineNumber(content, structMatch.index), endLine: this.findLineNumber(content, structMatch.index + structMatch[0].length), fields: this.parseGoStructFields(fieldsStr) }); } // Parse interfaces const interfaceRegex = /type\s+(\w+)\s+interface\s*\{([^}]*)\}/g; let interfaceMatch; while ((interfaceMatch = interfaceRegex.exec(content)) !== null) { const name = interfaceMatch[1]; const methodsStr = interfaceMatch[2]; result.interfaces.push({ name, startLine: this.findLineNumber(content, interfaceMatch.index), endLine: this.findLineNumber(content, interfaceMatch.index + interfaceMatch[0].length), methods: this.parseGoInterfaceMethods(methodsStr) }); } return result; } findLineNumber(content, index) { return content.substring(0, index).split('\n').length; } findGoFunctionEndLine(content, startIndex) { let braceCount = 0; let inString = false; let inComment = false; for (let i = startIndex; i < content.length; i++) { const char = content[i]; const nextChar = content[i + 1]; if (!inString && !inComment) { if (char === '/' && nextChar === '/') { inComment = true; continue; } if (char === '/' && nextChar === '*') { inComment = true; continue; } if (char === '"' || char === '`') { inString = true; continue; } if (char === '{') { braceCount++; } else if (char === '}') { braceCount--; if (braceCount === 0) { return this.findLineNumber(content, i); } } } else if (inString && (char === '"' || char === '`') && content[i - 1] !== '\\') { inString = false; } else if (inComment && char === '\n') { inComment = false; } else if (inComment && char === '*' && nextChar === '/') { inComment = false; i++; // Skip the '/' } } return this.findLineNumber(content, content.length); } parseGoParameters(paramStr) { if (!paramStr.trim()) return []; const params = []; const paramParts = paramStr.split(',').map(p => p.trim()); for (const part of paramParts) { const match = part.match(/(\w+)\s+(.+)/); if (match) { params.push({ name: match[1], type: match[2] }); } } return params; } parseGoReturnTypes(returnStr) { if (!returnStr.trim()) return []; // Handle multiple return types in parentheses if (returnStr.startsWith('(') && returnStr.endsWith(')')) { return returnStr.slice(1, -1).split(',').map(t => t.trim()); } return [returnStr.trim()]; } parseGoStructFields(fieldsStr) { const fields = []; const lines = fieldsStr.split('\n').map(l => l.trim()).filter(l => l); for (const line of lines) { const match = line.match(/(\w+)\s+(.+?)(?:\s+`([^`]*)`)?/); if (match) { fields.push({ name: match[1], type: match[2], tags: match[3] || null, line: 0 // Line numbers extracted from regex parsing context }); } } return fields; } parseGoInterfaceMethods(methodsStr) { const methods = []; const lines = methodsStr.split('\n').map(l => l.trim()).filter(l => l); for (const line of lines) { const match = line.match(/(\w+)\s*\(([^)]*)\)\s*(.+)?/); if (match) { methods.push({ name: match[1], parameters: match[2], returnType: match[3] || null }); } } return methods; } extractGoFunctionBody(content, startIndex) { const braceIndex = content.indexOf('{', startIndex); if (braceIndex === -1) return ''; let braceCount = 1; let i = braceIndex + 1; while (i < content.length && braceCount > 0) { if (content[i] === '{') braceCount++; else if (content[i] === '}') braceCount--; i++; } return content.substring(braceIndex + 1, i - 1); } extractGoParameters(goParams) { return goParams.map(param => ({ name: param.name, type: param.type || undefined, optional: false, defaultValue: undefined })); } calculateGoFunctionComplexity(func) { if (!func.body) return 1; let complexity = 1; const body = func.body; // Count decision points complexity += (body.match(/\bif\b/g) || []).length; complexity += (body.match(/\belse\b/g) || []).length; complexity += (body.match(/\bfor\b/g) || []).length; complexity += (body.match(/\bswitch\b/g) || []).length; complexity += (body.match(/\bcase\b/g) || []).length; complexity += (body.match(/\bselect\b/g) || []).length; // Go-specific return complexity; } calculateGoCognitiveComplexity(func) { // Calculate cognitive complexity using established 0.8 multiplier for Go return Math.floor(this.calculateGoFunctionComplexity(func) * 0.8); } isGoExported(name) { // In Go, exported identifiers start with capital letters return name.length > 0 && name[0] === name[0].toUpperCase(); } getGoPackageName(importPath) { const parts = importPath.split('/'); return parts[parts.length - 1]; } extractGoStructMethods(functions, structName) { // Find methods that have this struct as receiver return functions .filter(func => func.receiver && func.receiver.includes(structName)) .map(func => ({ id: this.generateId(), name: func.name, parameters: this.extractGoParameters(func.parameters || []), returnType: func.returnTypes ? func.returnTypes.join(', ') : undefined, complexity: this.calculateGoFunctionComplexity(func), startLine: func.startLine, endLine: func.endLine, filePath: '', isAsync: false, isExported: this.isGoExported(func.name), visibility: this.isGoExported(func.name) ? 'public' : 'private', dependencies: [], calls: [] })); } extractGoStructProperties(struct) { return (struct.fields || []).map((field) => ({ name: field.name, type: field.type || undefined, visibility: this.isGoExported(field.name) ? 'public' : 'private', isStatic: false, isReadonly: false, defaultValue: undefined })); } extractGoStructInterfaces(_interfaces, _structName) { // Go uses implicit interface satisfaction, so this is complex to determine statically return []; } extractGoFunctionDependencies(func) { if (!func.body) return []; const dependencies = new Set(); const body = func.body; // Extract package references (simplified) const packageMatches = body.match(/\b\w+\.\w+/g) || []; packageMatches.forEach((match) => { const packageName = match.split('.')[0]; dependencies.add(packageName); }); return Array.from(dependencies); } extractGoFunctionCalls(func) { if (!func.body) return []; const calls = new Set(); const body = func.body; // Extract function calls (simplified) const callMatches = body.match(/\b\w+\s*\(/g) || []; callMatches.forEach((match) => { const functionName = match.replace(/\s*\($/, ''); calls.add(functionName); }); return Array.from(calls); } } exports.GoAnalyzer = GoAnalyzer; //# sourceMappingURL=GoAnalyzer.js.map