linguclaw 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +161 -0
- package/dist/agent-system.d.ts +196 -0
- package/dist/agent-system.d.ts.map +1 -0
- package/dist/agent-system.js +738 -0
- package/dist/agent-system.js.map +1 -0
- package/dist/alphabeta.d.ts +54 -0
- package/dist/alphabeta.d.ts.map +1 -0
- package/dist/alphabeta.js +193 -0
- package/dist/alphabeta.js.map +1 -0
- package/dist/browser.d.ts +62 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +224 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +565 -0
- package/dist/cli.js.map +1 -0
- package/dist/code-parser.d.ts +39 -0
- package/dist/code-parser.d.ts.map +1 -0
- package/dist/code-parser.js +385 -0
- package/dist/code-parser.js.map +1 -0
- package/dist/config.d.ts +66 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +232 -0
- package/dist/config.js.map +1 -0
- package/dist/core/engine.d.ts +359 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +127 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/daemon.d.ts +29 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +212 -0
- package/dist/daemon.js.map +1 -0
- package/dist/email-receiver.d.ts +63 -0
- package/dist/email-receiver.d.ts.map +1 -0
- package/dist/email-receiver.js +553 -0
- package/dist/email-receiver.js.map +1 -0
- package/dist/git-integration.d.ts +180 -0
- package/dist/git-integration.d.ts.map +1 -0
- package/dist/git-integration.js +850 -0
- package/dist/git-integration.js.map +1 -0
- package/dist/inbox.d.ts +84 -0
- package/dist/inbox.d.ts.map +1 -0
- package/dist/inbox.js +198 -0
- package/dist/inbox.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/languages/cpp.d.ts +51 -0
- package/dist/languages/cpp.d.ts.map +1 -0
- package/dist/languages/cpp.js +930 -0
- package/dist/languages/cpp.js.map +1 -0
- package/dist/languages/csharp.d.ts +79 -0
- package/dist/languages/csharp.d.ts.map +1 -0
- package/dist/languages/csharp.js +1776 -0
- package/dist/languages/csharp.js.map +1 -0
- package/dist/languages/go.d.ts +50 -0
- package/dist/languages/go.d.ts.map +1 -0
- package/dist/languages/go.js +882 -0
- package/dist/languages/go.js.map +1 -0
- package/dist/languages/java.d.ts +47 -0
- package/dist/languages/java.d.ts.map +1 -0
- package/dist/languages/java.js +649 -0
- package/dist/languages/java.js.map +1 -0
- package/dist/languages/python.d.ts +47 -0
- package/dist/languages/python.d.ts.map +1 -0
- package/dist/languages/python.js +655 -0
- package/dist/languages/python.js.map +1 -0
- package/dist/languages/rust.d.ts +61 -0
- package/dist/languages/rust.d.ts.map +1 -0
- package/dist/languages/rust.js +1064 -0
- package/dist/languages/rust.js.map +1 -0
- package/dist/logger.d.ts +20 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +133 -0
- package/dist/logger.js.map +1 -0
- package/dist/longterm-memory.d.ts +47 -0
- package/dist/longterm-memory.d.ts.map +1 -0
- package/dist/longterm-memory.js +300 -0
- package/dist/longterm-memory.js.map +1 -0
- package/dist/memory.d.ts +42 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +274 -0
- package/dist/memory.js.map +1 -0
- package/dist/messaging.d.ts +103 -0
- package/dist/messaging.d.ts.map +1 -0
- package/dist/messaging.js +645 -0
- package/dist/messaging.js.map +1 -0
- package/dist/multi-provider.d.ts +69 -0
- package/dist/multi-provider.d.ts.map +1 -0
- package/dist/multi-provider.js +484 -0
- package/dist/multi-provider.js.map +1 -0
- package/dist/orchestrator.d.ts +65 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +441 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/plugins.d.ts +52 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/plugins.js +215 -0
- package/dist/plugins.js.map +1 -0
- package/dist/prism-orchestrator.d.ts +26 -0
- package/dist/prism-orchestrator.d.ts.map +1 -0
- package/dist/prism-orchestrator.js +191 -0
- package/dist/prism-orchestrator.js.map +1 -0
- package/dist/prism.d.ts +46 -0
- package/dist/prism.d.ts.map +1 -0
- package/dist/prism.js +188 -0
- package/dist/prism.js.map +1 -0
- package/dist/privacy.d.ts +23 -0
- package/dist/privacy.d.ts.map +1 -0
- package/dist/privacy.js +220 -0
- package/dist/privacy.js.map +1 -0
- package/dist/proactive.d.ts +30 -0
- package/dist/proactive.d.ts.map +1 -0
- package/dist/proactive.js +260 -0
- package/dist/proactive.js.map +1 -0
- package/dist/refactoring-engine.d.ts +100 -0
- package/dist/refactoring-engine.d.ts.map +1 -0
- package/dist/refactoring-engine.js +717 -0
- package/dist/refactoring-engine.js.map +1 -0
- package/dist/resilience.d.ts +43 -0
- package/dist/resilience.d.ts.map +1 -0
- package/dist/resilience.js +200 -0
- package/dist/resilience.js.map +1 -0
- package/dist/safety.d.ts +40 -0
- package/dist/safety.d.ts.map +1 -0
- package/dist/safety.js +133 -0
- package/dist/safety.js.map +1 -0
- package/dist/sandbox.d.ts +33 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +173 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/scheduler.d.ts +72 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +374 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/semantic-memory.d.ts +70 -0
- package/dist/semantic-memory.d.ts.map +1 -0
- package/dist/semantic-memory.js +430 -0
- package/dist/semantic-memory.js.map +1 -0
- package/dist/skills.d.ts +97 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +575 -0
- package/dist/skills.js.map +1 -0
- package/dist/static/dashboard.html +853 -0
- package/dist/static/hub.html +772 -0
- package/dist/static/index.html +818 -0
- package/dist/static/logo.svg +24 -0
- package/dist/static/workflow-editor.html +913 -0
- package/dist/tools.d.ts +67 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +303 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +295 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +90 -0
- package/dist/types.js.map +1 -0
- package/dist/web.d.ts +76 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +2139 -0
- package/dist/web.js.map +1 -0
- package/dist/workflow-engine.d.ts +114 -0
- package/dist/workflow-engine.d.ts.map +1 -0
- package/dist/workflow-engine.js +855 -0
- package/dist/workflow-engine.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,882 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Go Language Support for LinguClaw
|
|
4
|
+
* Advanced Go parser with goroutine and channel analysis
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.GoLanguageSupport = exports.GoAnalyzer = exports.GoParser = void 0;
|
|
8
|
+
class GoParser {
|
|
9
|
+
source = '';
|
|
10
|
+
lines = [];
|
|
11
|
+
parse(source, filePath) {
|
|
12
|
+
this.source = source;
|
|
13
|
+
this.lines = source.split('\n');
|
|
14
|
+
try {
|
|
15
|
+
const ast = this.parseGo(source, filePath);
|
|
16
|
+
return {
|
|
17
|
+
ast,
|
|
18
|
+
errors: this.findSyntaxErrors(),
|
|
19
|
+
warnings: [],
|
|
20
|
+
tokens: [],
|
|
21
|
+
comments: this.extractComments(),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return this.fallbackParse(filePath);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async *parseStream(source) {
|
|
29
|
+
const reader = source.getReader();
|
|
30
|
+
let buffer = '';
|
|
31
|
+
while (true) {
|
|
32
|
+
const { done, value } = await reader.read();
|
|
33
|
+
if (done)
|
|
34
|
+
break;
|
|
35
|
+
buffer += value;
|
|
36
|
+
// Go statements end with newlines or semicolons
|
|
37
|
+
const lines = buffer.split('\n');
|
|
38
|
+
const complete = [];
|
|
39
|
+
let remaining = '';
|
|
40
|
+
let braceCount = 0;
|
|
41
|
+
let parenCount = 0;
|
|
42
|
+
let inRawString = false;
|
|
43
|
+
for (const line of lines) {
|
|
44
|
+
for (let i = 0; i < line.length; i++) {
|
|
45
|
+
const char = line[i];
|
|
46
|
+
const nextChar = line[i + 1] || '';
|
|
47
|
+
// Handle raw string literals
|
|
48
|
+
if (char === '`') {
|
|
49
|
+
inRawString = !inRawString;
|
|
50
|
+
}
|
|
51
|
+
if (!inRawString) {
|
|
52
|
+
if (char === '{')
|
|
53
|
+
braceCount++;
|
|
54
|
+
if (char === '}')
|
|
55
|
+
braceCount--;
|
|
56
|
+
if (char === '(')
|
|
57
|
+
parenCount++;
|
|
58
|
+
if (char === ')')
|
|
59
|
+
parenCount--;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (braceCount === 0 && parenCount === 0 && !inRawString) {
|
|
63
|
+
complete.push(line);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
remaining += line + '\n';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (complete.length > 0) {
|
|
70
|
+
yield this.parse(complete.join('\n'), '<stream>');
|
|
71
|
+
}
|
|
72
|
+
buffer = remaining;
|
|
73
|
+
}
|
|
74
|
+
if (buffer.trim()) {
|
|
75
|
+
yield this.parse(buffer, '<stream>');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
parseGo(source, filePath) {
|
|
79
|
+
const root = {
|
|
80
|
+
type: 'Program',
|
|
81
|
+
id: `${filePath}:0:0`,
|
|
82
|
+
location: this.createLocation(filePath, 0, 0, this.lines.length, 0),
|
|
83
|
+
children: [],
|
|
84
|
+
metadata: { language: 'go', package: '' },
|
|
85
|
+
};
|
|
86
|
+
// Parse package declaration
|
|
87
|
+
const packageMatch = source.match(/package\s+(\w+)/);
|
|
88
|
+
if (packageMatch) {
|
|
89
|
+
root.metadata.package = packageMatch[1];
|
|
90
|
+
root.children.push({
|
|
91
|
+
type: 'PackageDeclaration',
|
|
92
|
+
id: `${filePath}:1:0`,
|
|
93
|
+
location: this.createLocation(filePath, 1, 0, 1, packageMatch[0].length),
|
|
94
|
+
children: [],
|
|
95
|
+
metadata: { name: packageMatch[1] },
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// Parse imports
|
|
99
|
+
const imports = this.parseImports(source, filePath);
|
|
100
|
+
root.children.push(...imports);
|
|
101
|
+
// Parse declarations
|
|
102
|
+
const declarations = this.parseDeclarations(source, filePath);
|
|
103
|
+
root.children.push(...declarations);
|
|
104
|
+
return root;
|
|
105
|
+
}
|
|
106
|
+
parseImports(source, filePath) {
|
|
107
|
+
const imports = [];
|
|
108
|
+
const importRegex = /import\s*(?:\(|([^)]+))/g;
|
|
109
|
+
let match;
|
|
110
|
+
while ((match = importRegex.exec(source)) !== null) {
|
|
111
|
+
if (match[1]) {
|
|
112
|
+
// Single import
|
|
113
|
+
const importPath = match[1].trim().replace(/["']/g, '');
|
|
114
|
+
const alias = match[1].match(/^(\w+)\s+/)?.[1];
|
|
115
|
+
imports.push({
|
|
116
|
+
type: 'ImportDeclaration',
|
|
117
|
+
id: `${filePath}:0:${match.index}`,
|
|
118
|
+
location: this.createLocation(filePath, 0, match.index, 0, match.index + match[0].length),
|
|
119
|
+
children: [],
|
|
120
|
+
metadata: {
|
|
121
|
+
path: importPath,
|
|
122
|
+
alias,
|
|
123
|
+
isStandard: !importPath.includes('.'),
|
|
124
|
+
isCgo: importPath === 'C',
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// Block import - find closing paren
|
|
130
|
+
const startIdx = match.index;
|
|
131
|
+
const blockEnd = this.findMatchingParen(source, startIdx + 6);
|
|
132
|
+
const blockContent = source.substring(startIdx + 7, blockEnd);
|
|
133
|
+
const blockLines = blockContent.split('\n');
|
|
134
|
+
for (const line of blockLines) {
|
|
135
|
+
const trimmed = line.trim();
|
|
136
|
+
if (trimmed && !trimmed.startsWith('//')) {
|
|
137
|
+
const pathMatch = trimmed.match(/["']([^"']+)["']/);
|
|
138
|
+
if (pathMatch) {
|
|
139
|
+
const aliasMatch = trimmed.match(/^(\w+)\s+/);
|
|
140
|
+
imports.push({
|
|
141
|
+
type: 'ImportDeclaration',
|
|
142
|
+
id: `${filePath}:0:0`,
|
|
143
|
+
location: this.createLocation(filePath, 0, 0, 0, 0),
|
|
144
|
+
children: [],
|
|
145
|
+
metadata: {
|
|
146
|
+
path: pathMatch[1],
|
|
147
|
+
alias: aliasMatch?.[1],
|
|
148
|
+
isStandard: !pathMatch[1].includes('.'),
|
|
149
|
+
isCgo: pathMatch[1] === 'C',
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return imports;
|
|
158
|
+
}
|
|
159
|
+
parseDeclarations(source, filePath) {
|
|
160
|
+
const declarations = [];
|
|
161
|
+
const lines = source.split('\n');
|
|
162
|
+
let i = 0;
|
|
163
|
+
while (i < lines.length) {
|
|
164
|
+
const line = lines[i].trim();
|
|
165
|
+
// Skip comments and empty lines
|
|
166
|
+
if (!line || line.startsWith('//')) {
|
|
167
|
+
i++;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
// Type declarations
|
|
171
|
+
if (line.startsWith('type ')) {
|
|
172
|
+
const typeDecl = this.parseTypeDeclaration(lines, i, filePath);
|
|
173
|
+
if (typeDecl) {
|
|
174
|
+
declarations.push(typeDecl.node);
|
|
175
|
+
i = typeDecl.endIndex + 1;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Const declarations
|
|
180
|
+
if (line.startsWith('const ')) {
|
|
181
|
+
const constDecl = this.parseConstDeclaration(lines, i, filePath);
|
|
182
|
+
if (constDecl) {
|
|
183
|
+
declarations.push(constDecl.node);
|
|
184
|
+
i = constDecl.endIndex + 1;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Var declarations
|
|
189
|
+
if (line.startsWith('var ')) {
|
|
190
|
+
const varDecl = this.parseVarDeclaration(lines, i, filePath);
|
|
191
|
+
if (varDecl) {
|
|
192
|
+
declarations.push(varDecl.node);
|
|
193
|
+
i = varDecl.endIndex + 1;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Function declarations
|
|
198
|
+
if (line.match(/^func\s/)) {
|
|
199
|
+
const funcDecl = this.parseFunction(lines, i, filePath);
|
|
200
|
+
if (funcDecl) {
|
|
201
|
+
declarations.push(funcDecl.node);
|
|
202
|
+
i = funcDecl.endIndex + 1;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
i++;
|
|
207
|
+
}
|
|
208
|
+
return declarations;
|
|
209
|
+
}
|
|
210
|
+
parseTypeDeclaration(lines, startIdx, filePath) {
|
|
211
|
+
const line = lines[startIdx].trim();
|
|
212
|
+
const match = line.match(/type\s+(\w+)\s*(?:\[([^\]]*)\])?\s*(.+)/);
|
|
213
|
+
if (!match && !line.includes('type '))
|
|
214
|
+
return null;
|
|
215
|
+
const typeName = match?.[1] || line.match(/type\s+(\w+)/)?.[1];
|
|
216
|
+
if (!typeName)
|
|
217
|
+
return null;
|
|
218
|
+
let endIdx = startIdx;
|
|
219
|
+
let braceCount = 0;
|
|
220
|
+
// Check if it's a block type (struct, interface)
|
|
221
|
+
if (line.includes('{') || (!match?.[3] && lines[startIdx + 1]?.trim().startsWith('{'))) {
|
|
222
|
+
for (let i = startIdx; i < lines.length; i++) {
|
|
223
|
+
for (const char of lines[i]) {
|
|
224
|
+
if (char === '{')
|
|
225
|
+
braceCount++;
|
|
226
|
+
if (char === '}')
|
|
227
|
+
braceCount--;
|
|
228
|
+
}
|
|
229
|
+
if (braceCount === 0 && i > startIdx) {
|
|
230
|
+
endIdx = i;
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const body = lines.slice(startIdx, endIdx + 1).join('\n');
|
|
236
|
+
const isStruct = body.includes('struct {');
|
|
237
|
+
const isInterface = body.includes('interface {');
|
|
238
|
+
const isAlias = !isStruct && !isInterface && match?.[3];
|
|
239
|
+
const node = {
|
|
240
|
+
type: 'TypeDeclaration',
|
|
241
|
+
id: `${filePath}:${startIdx + 1}:0`,
|
|
242
|
+
location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
|
|
243
|
+
children: [],
|
|
244
|
+
metadata: {
|
|
245
|
+
name: typeName,
|
|
246
|
+
isStruct,
|
|
247
|
+
isInterface,
|
|
248
|
+
isAlias,
|
|
249
|
+
underlyingType: isAlias ? match[3] : undefined,
|
|
250
|
+
fields: isStruct ? this.extractStructFields(body) : [],
|
|
251
|
+
methods: isInterface ? this.extractInterfaceMethods(body) : [],
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
return { node, endIndex: endIdx };
|
|
255
|
+
}
|
|
256
|
+
parseFunction(lines, startIdx, filePath) {
|
|
257
|
+
const line = lines[startIdx].trim();
|
|
258
|
+
// Match function signature
|
|
259
|
+
const sigMatch = line.match(/^func\s+(?:\(([^)]+)\)\s+)?(\w+)\s*\(([^)]*)\)\s*(\([^)]*\)|[^{]+)?/);
|
|
260
|
+
if (!sigMatch)
|
|
261
|
+
return null;
|
|
262
|
+
const receiver = sigMatch[1];
|
|
263
|
+
const funcName = sigMatch[2];
|
|
264
|
+
const params = sigMatch[3];
|
|
265
|
+
const returnType = sigMatch[4]?.trim();
|
|
266
|
+
// Find function body
|
|
267
|
+
let endIdx = startIdx;
|
|
268
|
+
let braceCount = 0;
|
|
269
|
+
let foundOpening = false;
|
|
270
|
+
for (let i = startIdx; i < lines.length; i++) {
|
|
271
|
+
for (const char of lines[i]) {
|
|
272
|
+
if (char === '{') {
|
|
273
|
+
braceCount++;
|
|
274
|
+
foundOpening = true;
|
|
275
|
+
}
|
|
276
|
+
if (char === '}')
|
|
277
|
+
braceCount--;
|
|
278
|
+
}
|
|
279
|
+
if (foundOpening && braceCount === 0) {
|
|
280
|
+
endIdx = i;
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const body = lines.slice(startIdx, endIdx + 1).join('\n');
|
|
285
|
+
const statements = this.parseGoStatements(lines.slice(startIdx + 1, endIdx), filePath, startIdx + 1);
|
|
286
|
+
// Analyze concurrency patterns
|
|
287
|
+
const concurrency = this.analyzeConcurrency(body);
|
|
288
|
+
const node = {
|
|
289
|
+
type: 'FunctionDeclaration',
|
|
290
|
+
id: `${filePath}:${startIdx + 1}:0`,
|
|
291
|
+
location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
|
|
292
|
+
children: statements,
|
|
293
|
+
metadata: {
|
|
294
|
+
name: funcName,
|
|
295
|
+
receiver: receiver ? this.parseReceiver(receiver) : undefined,
|
|
296
|
+
parameters: this.parseParameters(params),
|
|
297
|
+
returnType,
|
|
298
|
+
isMethod: !!receiver,
|
|
299
|
+
isExported: /^[A-Z]/.test(funcName),
|
|
300
|
+
hasDefer: body.includes('defer '),
|
|
301
|
+
hasPanic: body.includes('panic('),
|
|
302
|
+
hasRecover: body.includes('recover()'),
|
|
303
|
+
goroutines: concurrency.goroutines,
|
|
304
|
+
channels: concurrency.channels,
|
|
305
|
+
isConcurrent: concurrency.goroutines.length > 0,
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
return { node, endIndex: endIdx };
|
|
309
|
+
}
|
|
310
|
+
parseReceiver(receiver) {
|
|
311
|
+
const parts = receiver.trim().split(/\s+/);
|
|
312
|
+
if (parts.length === 2) {
|
|
313
|
+
return {
|
|
314
|
+
name: parts[0],
|
|
315
|
+
type: parts[1].replace('*', ''),
|
|
316
|
+
pointer: parts[1].startsWith('*'),
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
return { name: 'this', type: receiver.replace('*', ''), pointer: receiver.startsWith('*') };
|
|
320
|
+
}
|
|
321
|
+
parseParameters(params) {
|
|
322
|
+
const parameters = [];
|
|
323
|
+
if (!params.trim())
|
|
324
|
+
return parameters;
|
|
325
|
+
// Go allows grouped types: "a, b int"
|
|
326
|
+
const segments = this.splitParams(params);
|
|
327
|
+
for (const segment of segments) {
|
|
328
|
+
const trimmed = segment.trim();
|
|
329
|
+
if (!trimmed)
|
|
330
|
+
continue;
|
|
331
|
+
const parts = trimmed.split(/\s+/);
|
|
332
|
+
if (parts.length >= 2) {
|
|
333
|
+
const type = parts[parts.length - 1];
|
|
334
|
+
const isVariadic = type.startsWith('...');
|
|
335
|
+
// All names before the type share the same type
|
|
336
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
337
|
+
parameters.push({
|
|
338
|
+
name: parts[i].replace(',', ''),
|
|
339
|
+
type: type.replace(',', ''),
|
|
340
|
+
variadic: isVariadic,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return parameters;
|
|
346
|
+
}
|
|
347
|
+
parseConstDeclaration(lines, startIdx, filePath) {
|
|
348
|
+
const line = lines[startIdx].trim();
|
|
349
|
+
if (line.includes('(')) {
|
|
350
|
+
// Block const declaration
|
|
351
|
+
let endIdx = startIdx;
|
|
352
|
+
let parenCount = 0;
|
|
353
|
+
for (let i = startIdx; i < lines.length; i++) {
|
|
354
|
+
for (const char of lines[i]) {
|
|
355
|
+
if (char === '(')
|
|
356
|
+
parenCount++;
|
|
357
|
+
if (char === ')')
|
|
358
|
+
parenCount--;
|
|
359
|
+
}
|
|
360
|
+
if (parenCount === 0 && i > startIdx) {
|
|
361
|
+
endIdx = i;
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const blockContent = lines.slice(startIdx + 1, endIdx).join('\n');
|
|
366
|
+
const consts = this.extractConstsFromBlock(blockContent);
|
|
367
|
+
const node = {
|
|
368
|
+
type: 'ConstDeclaration',
|
|
369
|
+
id: `${filePath}:${startIdx + 1}:0`,
|
|
370
|
+
location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
|
|
371
|
+
children: consts.map((c, idx) => ({
|
|
372
|
+
type: 'ConstSpec',
|
|
373
|
+
id: `${filePath}:${startIdx + 1 + idx}:0`,
|
|
374
|
+
location: this.createLocation(filePath, startIdx + 1 + idx, 0, startIdx + 1 + idx, 0),
|
|
375
|
+
children: [],
|
|
376
|
+
metadata: c,
|
|
377
|
+
})),
|
|
378
|
+
metadata: { isBlock: true },
|
|
379
|
+
};
|
|
380
|
+
return { node, endIndex: endIdx };
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
// Single const
|
|
384
|
+
const match = line.match(/const\s+(\w+)\s+(\w+)?\s*=\s*(.+)/);
|
|
385
|
+
if (match) {
|
|
386
|
+
const node = {
|
|
387
|
+
type: 'ConstDeclaration',
|
|
388
|
+
id: `${filePath}:${startIdx + 1}:0`,
|
|
389
|
+
location: this.createLocation(filePath, startIdx + 1, 0, startIdx + 1, line.length),
|
|
390
|
+
children: [],
|
|
391
|
+
metadata: {
|
|
392
|
+
name: match[1],
|
|
393
|
+
type: match[2],
|
|
394
|
+
value: match[3],
|
|
395
|
+
isBlock: false,
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
return { node, endIndex: startIdx };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
parseVarDeclaration(lines, startIdx, filePath) {
|
|
404
|
+
const line = lines[startIdx].trim();
|
|
405
|
+
if (line.includes('(')) {
|
|
406
|
+
// Block var declaration
|
|
407
|
+
let endIdx = startIdx;
|
|
408
|
+
let parenCount = 0;
|
|
409
|
+
for (let i = startIdx; i < lines.length; i++) {
|
|
410
|
+
for (const char of lines[i]) {
|
|
411
|
+
if (char === '(')
|
|
412
|
+
parenCount++;
|
|
413
|
+
if (char === ')')
|
|
414
|
+
parenCount--;
|
|
415
|
+
}
|
|
416
|
+
if (parenCount === 0 && i > startIdx) {
|
|
417
|
+
endIdx = i;
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
const blockContent = lines.slice(startIdx + 1, endIdx).join('\n');
|
|
422
|
+
const vars = this.extractVarsFromBlock(blockContent);
|
|
423
|
+
const node = {
|
|
424
|
+
type: 'VariableDeclaration',
|
|
425
|
+
id: `${filePath}:${startIdx + 1}:0`,
|
|
426
|
+
location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
|
|
427
|
+
children: vars.map((v, idx) => ({
|
|
428
|
+
type: 'VarSpec',
|
|
429
|
+
id: `${filePath}:${startIdx + 1 + idx}:0`,
|
|
430
|
+
location: this.createLocation(filePath, startIdx + 1 + idx, 0, startIdx + 1 + idx, 0),
|
|
431
|
+
children: [],
|
|
432
|
+
metadata: v,
|
|
433
|
+
})),
|
|
434
|
+
metadata: { isBlock: true },
|
|
435
|
+
};
|
|
436
|
+
return { node, endIndex: endIdx };
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
// Single var or short declaration
|
|
440
|
+
const shortMatch = line.match(/^(\w+)\s*:=\s*(.+)/);
|
|
441
|
+
if (shortMatch) {
|
|
442
|
+
const node = {
|
|
443
|
+
type: 'ShortVariableDeclaration',
|
|
444
|
+
id: `${filePath}:${startIdx + 1}:0`,
|
|
445
|
+
location: this.createLocation(filePath, startIdx + 1, 0, startIdx + 1, line.length),
|
|
446
|
+
children: [],
|
|
447
|
+
metadata: {
|
|
448
|
+
name: shortMatch[1],
|
|
449
|
+
initializer: shortMatch[2],
|
|
450
|
+
inferred: true,
|
|
451
|
+
},
|
|
452
|
+
};
|
|
453
|
+
return { node, endIndex: startIdx };
|
|
454
|
+
}
|
|
455
|
+
const varMatch = line.match(/var\s+(\w+)\s+(\w+)?\s*(?:=\s*(.+))?/);
|
|
456
|
+
if (varMatch) {
|
|
457
|
+
const node = {
|
|
458
|
+
type: 'VariableDeclaration',
|
|
459
|
+
id: `${filePath}:${startIdx + 1}:0`,
|
|
460
|
+
location: this.createLocation(filePath, startIdx + 1, 0, startIdx + 1, line.length),
|
|
461
|
+
children: [],
|
|
462
|
+
metadata: {
|
|
463
|
+
name: varMatch[1],
|
|
464
|
+
type: varMatch[2],
|
|
465
|
+
initializer: varMatch[3],
|
|
466
|
+
isBlock: false,
|
|
467
|
+
},
|
|
468
|
+
};
|
|
469
|
+
return { node, endIndex: startIdx };
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
parseGoStatements(lines, filePath, lineOffset) {
|
|
475
|
+
const statements = [];
|
|
476
|
+
for (let i = 0; i < lines.length; i++) {
|
|
477
|
+
const line = lines[i].trim();
|
|
478
|
+
// Goroutine spawn
|
|
479
|
+
if (line.startsWith('go ')) {
|
|
480
|
+
statements.push({
|
|
481
|
+
type: 'GoStatement',
|
|
482
|
+
id: `${filePath}:${lineOffset + i + 1}:0`,
|
|
483
|
+
location: this.createLocation(filePath, lineOffset + i + 1, 0, lineOffset + i + 1, line.length),
|
|
484
|
+
children: [],
|
|
485
|
+
metadata: {
|
|
486
|
+
call: line.substring(3).trim(),
|
|
487
|
+
},
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
// Channel operations
|
|
491
|
+
if (line.includes('<-') || line.includes('chan ')) {
|
|
492
|
+
const isSend = line.match(/([^<]+)\s*<-\s*(.+)/);
|
|
493
|
+
const isRecv = line.match(/<-\s*(.+)/);
|
|
494
|
+
if (isSend || isRecv) {
|
|
495
|
+
statements.push({
|
|
496
|
+
type: 'ChannelOperation',
|
|
497
|
+
id: `${filePath}:${lineOffset + i + 1}:0`,
|
|
498
|
+
location: this.createLocation(filePath, lineOffset + i + 1, 0, lineOffset + i + 1, line.length),
|
|
499
|
+
children: [],
|
|
500
|
+
metadata: {
|
|
501
|
+
isSend: !!isSend,
|
|
502
|
+
isRecv: !!isRecv,
|
|
503
|
+
channel: isSend?.[1]?.trim() || 'unknown',
|
|
504
|
+
value: isSend?.[2]?.trim() || isRecv?.[1]?.trim(),
|
|
505
|
+
},
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
// Select statement
|
|
510
|
+
if (line.startsWith('select {')) {
|
|
511
|
+
statements.push({
|
|
512
|
+
type: 'SelectStatement',
|
|
513
|
+
id: `${filePath}:${lineOffset + i + 1}:0`,
|
|
514
|
+
location: this.createLocation(filePath, lineOffset + i + 1, 0, lineOffset + i + 1, line.length),
|
|
515
|
+
children: [],
|
|
516
|
+
metadata: {},
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
// Defer
|
|
520
|
+
if (line.startsWith('defer ')) {
|
|
521
|
+
statements.push({
|
|
522
|
+
type: 'DeferStatement',
|
|
523
|
+
id: `${filePath}:${lineOffset + i + 1}:0`,
|
|
524
|
+
location: this.createLocation(filePath, lineOffset + i + 1, 0, lineOffset + i + 1, line.length),
|
|
525
|
+
children: [],
|
|
526
|
+
metadata: {
|
|
527
|
+
call: line.substring(6).trim(),
|
|
528
|
+
},
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
// Panic/Recover
|
|
532
|
+
if (line.includes('panic(')) {
|
|
533
|
+
statements.push({
|
|
534
|
+
type: 'PanicStatement',
|
|
535
|
+
id: `${filePath}:${lineOffset + i + 1}:0`,
|
|
536
|
+
location: this.createLocation(filePath, lineOffset + i + 1, 0, lineOffset + i + 1, line.length),
|
|
537
|
+
children: [],
|
|
538
|
+
metadata: {},
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
// Range statement
|
|
542
|
+
if (line.startsWith('for ') && line.includes(' range ')) {
|
|
543
|
+
statements.push({
|
|
544
|
+
type: 'RangeStatement',
|
|
545
|
+
id: `${filePath}:${lineOffset + i + 1}:0`,
|
|
546
|
+
location: this.createLocation(filePath, lineOffset + i + 1, 0, lineOffset + i + 1, line.length),
|
|
547
|
+
children: [],
|
|
548
|
+
metadata: {},
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return statements;
|
|
553
|
+
}
|
|
554
|
+
analyzeConcurrency(body) {
|
|
555
|
+
const goroutines = [];
|
|
556
|
+
const channels = [];
|
|
557
|
+
// Find goroutine spawns
|
|
558
|
+
const goRegex = /go\s+(\w+)\s*\(/g;
|
|
559
|
+
let match;
|
|
560
|
+
while ((match = goRegex.exec(body)) !== null) {
|
|
561
|
+
goroutines.push(match[1]);
|
|
562
|
+
}
|
|
563
|
+
// Find channel operations
|
|
564
|
+
const chanRegex = /make\(chan\s+(\w+)/g;
|
|
565
|
+
while ((match = chanRegex.exec(body)) !== null) {
|
|
566
|
+
channels.push(match[1]);
|
|
567
|
+
}
|
|
568
|
+
return { goroutines, channels };
|
|
569
|
+
}
|
|
570
|
+
extractStructFields(body) {
|
|
571
|
+
const fields = [];
|
|
572
|
+
const fieldRegex = /(\w+)\s+(\S+)(?:\s+`([^`]+)`)?/g;
|
|
573
|
+
let match;
|
|
574
|
+
while ((match = fieldRegex.exec(body)) !== null) {
|
|
575
|
+
fields.push({
|
|
576
|
+
name: match[1],
|
|
577
|
+
type: match[2],
|
|
578
|
+
tag: match[3],
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
return fields;
|
|
582
|
+
}
|
|
583
|
+
extractInterfaceMethods(body) {
|
|
584
|
+
const methods = [];
|
|
585
|
+
const methodRegex = /(\w+)\s*\(([^)]*)\)\s*(\([^)]*\)|\w+)?/g;
|
|
586
|
+
let match;
|
|
587
|
+
while ((match = methodRegex.exec(body)) !== null) {
|
|
588
|
+
methods.push({
|
|
589
|
+
name: match[1],
|
|
590
|
+
signature: match[0],
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
return methods;
|
|
594
|
+
}
|
|
595
|
+
extractConstsFromBlock(content) {
|
|
596
|
+
const consts = [];
|
|
597
|
+
const lines = content.split('\n');
|
|
598
|
+
for (const line of lines) {
|
|
599
|
+
const trimmed = line.trim();
|
|
600
|
+
if (!trimmed || trimmed.startsWith('//'))
|
|
601
|
+
continue;
|
|
602
|
+
const match = trimmed.match(/(\w+)\s+(\w+)?\s*=\s*(.+)/);
|
|
603
|
+
if (match) {
|
|
604
|
+
consts.push({
|
|
605
|
+
name: match[1],
|
|
606
|
+
type: match[2],
|
|
607
|
+
value: match[3].replace(/,$/, ''),
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return consts;
|
|
612
|
+
}
|
|
613
|
+
extractVarsFromBlock(content) {
|
|
614
|
+
const vars = [];
|
|
615
|
+
const lines = content.split('\n');
|
|
616
|
+
for (const line of lines) {
|
|
617
|
+
const trimmed = line.trim();
|
|
618
|
+
if (!trimmed || trimmed.startsWith('//'))
|
|
619
|
+
continue;
|
|
620
|
+
const match = trimmed.match(/(\w+)\s+(\w+)?\s*(?:=\s*(.+))?/);
|
|
621
|
+
if (match) {
|
|
622
|
+
vars.push({
|
|
623
|
+
name: match[1],
|
|
624
|
+
type: match[2],
|
|
625
|
+
value: match[3]?.replace(/,$/, ''),
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return vars;
|
|
630
|
+
}
|
|
631
|
+
splitParams(params) {
|
|
632
|
+
const result = [];
|
|
633
|
+
let current = '';
|
|
634
|
+
let depth = 0;
|
|
635
|
+
for (const char of params) {
|
|
636
|
+
if (char === '(' || char === '[' || char === '<')
|
|
637
|
+
depth++;
|
|
638
|
+
if (char === ')' || char === ']' || char === '>')
|
|
639
|
+
depth--;
|
|
640
|
+
if (char === ',' && depth === 0) {
|
|
641
|
+
result.push(current.trim());
|
|
642
|
+
current = '';
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
current += char;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (current.trim()) {
|
|
649
|
+
result.push(current.trim());
|
|
650
|
+
}
|
|
651
|
+
return result;
|
|
652
|
+
}
|
|
653
|
+
findMatchingParen(source, startIdx) {
|
|
654
|
+
let depth = 1;
|
|
655
|
+
for (let i = startIdx + 1; i < source.length; i++) {
|
|
656
|
+
if (source[i] === '(')
|
|
657
|
+
depth++;
|
|
658
|
+
if (source[i] === ')')
|
|
659
|
+
depth--;
|
|
660
|
+
if (depth === 0)
|
|
661
|
+
return i;
|
|
662
|
+
}
|
|
663
|
+
return source.length - 1;
|
|
664
|
+
}
|
|
665
|
+
findSyntaxErrors() {
|
|
666
|
+
const errors = [];
|
|
667
|
+
let braceCount = 0;
|
|
668
|
+
let parenCount = 0;
|
|
669
|
+
for (let i = 0; i < this.lines.length; i++) {
|
|
670
|
+
const line = this.lines[i];
|
|
671
|
+
// Skip comments and strings
|
|
672
|
+
let inString = false;
|
|
673
|
+
let stringChar = '';
|
|
674
|
+
let escaped = false;
|
|
675
|
+
for (const char of line) {
|
|
676
|
+
if (escaped) {
|
|
677
|
+
escaped = false;
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
if (char === '\\') {
|
|
681
|
+
escaped = true;
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
if (!inString && (char === '"' || char === "'" || char === '`')) {
|
|
685
|
+
inString = true;
|
|
686
|
+
stringChar = char;
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
if (inString && char === stringChar) {
|
|
690
|
+
inString = false;
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
if (!inString) {
|
|
694
|
+
if (char === '{')
|
|
695
|
+
braceCount++;
|
|
696
|
+
if (char === '}')
|
|
697
|
+
braceCount--;
|
|
698
|
+
if (char === '(')
|
|
699
|
+
parenCount++;
|
|
700
|
+
if (char === ')')
|
|
701
|
+
parenCount--;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
if (braceCount < 0) {
|
|
705
|
+
errors.push({ message: 'Unexpected }', line: i + 1, severity: 'error' });
|
|
706
|
+
braceCount = 0;
|
|
707
|
+
}
|
|
708
|
+
if (parenCount < 0) {
|
|
709
|
+
errors.push({ message: 'Unexpected )', line: i + 1, severity: 'error' });
|
|
710
|
+
parenCount = 0;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
if (braceCount > 0) {
|
|
714
|
+
errors.push({ message: `Missing ${braceCount} closing brace(s)`, line: this.lines.length, severity: 'error' });
|
|
715
|
+
}
|
|
716
|
+
if (parenCount > 0) {
|
|
717
|
+
errors.push({ message: `Missing ${parenCount} closing paren(s)`, line: this.lines.length, severity: 'error' });
|
|
718
|
+
}
|
|
719
|
+
return errors;
|
|
720
|
+
}
|
|
721
|
+
extractComments() {
|
|
722
|
+
const comments = [];
|
|
723
|
+
for (let i = 0; i < this.lines.length; i++) {
|
|
724
|
+
const line = this.lines[i];
|
|
725
|
+
const commentIdx = line.indexOf('//');
|
|
726
|
+
if (commentIdx !== -1) {
|
|
727
|
+
comments.push({
|
|
728
|
+
type: 'Line',
|
|
729
|
+
value: line.substring(commentIdx + 2).trim(),
|
|
730
|
+
line: i + 1,
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
// Block comments /* */
|
|
735
|
+
const blockRegex = /\/\*[\s\S]*?\*\//g;
|
|
736
|
+
let match;
|
|
737
|
+
while ((match = blockRegex.exec(this.source)) !== null) {
|
|
738
|
+
const lineNum = this.source.substring(0, match.index).split('\n').length;
|
|
739
|
+
comments.push({
|
|
740
|
+
type: 'Block',
|
|
741
|
+
value: match[0].substring(2, match[0].length - 2).trim(),
|
|
742
|
+
line: lineNum,
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
return comments;
|
|
746
|
+
}
|
|
747
|
+
createLocation(file, startLine, startCol, endLine, endCol) {
|
|
748
|
+
return {
|
|
749
|
+
file,
|
|
750
|
+
startLine,
|
|
751
|
+
startColumn: startCol,
|
|
752
|
+
endLine,
|
|
753
|
+
endColumn: endCol,
|
|
754
|
+
byteOffset: 0,
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
fallbackParse(filePath) {
|
|
758
|
+
return {
|
|
759
|
+
ast: this.parseGo(this.source, filePath),
|
|
760
|
+
errors: this.findSyntaxErrors(),
|
|
761
|
+
warnings: [{ message: 'Using fallback parser' }],
|
|
762
|
+
tokens: [],
|
|
763
|
+
comments: this.extractComments(),
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
exports.GoParser = GoParser;
|
|
768
|
+
class GoAnalyzer {
|
|
769
|
+
analyze(ast, context) {
|
|
770
|
+
const symbolTable = this.buildSymbolTable(ast);
|
|
771
|
+
const concurrencyAnalysis = this.analyzeConcurrency(ast);
|
|
772
|
+
const errorAnalysis = this.analyzeErrorHandling(ast);
|
|
773
|
+
return {
|
|
774
|
+
symbols: symbolTable,
|
|
775
|
+
callGraph: this.buildCallGraph(ast),
|
|
776
|
+
dataFlow: this.buildDataFlow(ast),
|
|
777
|
+
controlFlow: this.buildControlFlow(ast),
|
|
778
|
+
typeInference: new Map(),
|
|
779
|
+
metrics: this.calculateMetrics(ast),
|
|
780
|
+
suggestions: [
|
|
781
|
+
...concurrencyAnalysis.suggestions,
|
|
782
|
+
...errorAnalysis.suggestions,
|
|
783
|
+
],
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
buildSymbolTable(ast) {
|
|
787
|
+
return {
|
|
788
|
+
variables: new Map(),
|
|
789
|
+
functions: new Map(),
|
|
790
|
+
classes: new Map(),
|
|
791
|
+
modules: new Map(),
|
|
792
|
+
imports: [],
|
|
793
|
+
exports: [],
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
analyzeConcurrency(ast) {
|
|
797
|
+
const suggestions = [];
|
|
798
|
+
let goroutineCount = 0;
|
|
799
|
+
let channelCount = 0;
|
|
800
|
+
let selectCount = 0;
|
|
801
|
+
const traverse = (node) => {
|
|
802
|
+
if (node.type === 'GoStatement')
|
|
803
|
+
goroutineCount++;
|
|
804
|
+
if (node.type === 'ChannelOperation')
|
|
805
|
+
channelCount++;
|
|
806
|
+
if (node.type === 'SelectStatement')
|
|
807
|
+
selectCount++;
|
|
808
|
+
node.children.forEach(traverse);
|
|
809
|
+
};
|
|
810
|
+
traverse(ast);
|
|
811
|
+
if (goroutineCount > 0 && channelCount === 0) {
|
|
812
|
+
suggestions.push({
|
|
813
|
+
type: 'concurrency',
|
|
814
|
+
severity: 'warning',
|
|
815
|
+
message: `Spawning ${goroutineCount} goroutines without explicit synchronization`,
|
|
816
|
+
remediation: 'Use channels or sync.WaitGroup for coordination',
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
if (goroutineCount > 10) {
|
|
820
|
+
suggestions.push({
|
|
821
|
+
type: 'performance',
|
|
822
|
+
severity: 'info',
|
|
823
|
+
message: 'High number of goroutines - consider using worker pools',
|
|
824
|
+
remediation: 'Use a bounded worker pool pattern',
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
return { suggestions };
|
|
828
|
+
}
|
|
829
|
+
analyzeErrorHandling(ast) {
|
|
830
|
+
const suggestions = [];
|
|
831
|
+
let panicCount = 0;
|
|
832
|
+
let recoverCount = 0;
|
|
833
|
+
const traverse = (node) => {
|
|
834
|
+
if (node.type === 'PanicStatement')
|
|
835
|
+
panicCount++;
|
|
836
|
+
if (node.metadata?.hasRecover)
|
|
837
|
+
recoverCount++;
|
|
838
|
+
node.children.forEach(traverse);
|
|
839
|
+
};
|
|
840
|
+
traverse(ast);
|
|
841
|
+
if (panicCount > 0 && recoverCount === 0) {
|
|
842
|
+
suggestions.push({
|
|
843
|
+
type: 'error-handling',
|
|
844
|
+
severity: 'warning',
|
|
845
|
+
message: `Using ${panicCount} panic(s) without recover`,
|
|
846
|
+
remediation: 'Add defer/recover or return errors instead',
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
return { suggestions };
|
|
850
|
+
}
|
|
851
|
+
buildCallGraph(ast) {
|
|
852
|
+
return { nodes: [], edges: [], entryPoints: [], deadCode: [] };
|
|
853
|
+
}
|
|
854
|
+
buildDataFlow(ast) {
|
|
855
|
+
return { definitions: new Map(), uses: new Map(), taintedSources: [], sinks: [] };
|
|
856
|
+
}
|
|
857
|
+
buildControlFlow(ast) {
|
|
858
|
+
return { nodes: [], edges: [], loops: [], branches: [] };
|
|
859
|
+
}
|
|
860
|
+
calculateMetrics(ast) {
|
|
861
|
+
return {
|
|
862
|
+
linesOfCode: 0,
|
|
863
|
+
logicalLines: 0,
|
|
864
|
+
commentLines: 0,
|
|
865
|
+
blankLines: 0,
|
|
866
|
+
cyclomaticComplexity: 0,
|
|
867
|
+
cognitiveComplexity: 0,
|
|
868
|
+
halsteadMetrics: { operators: 0, operands: 0, uniqueOperators: 0, uniqueOperands: 0, volume: 0, difficulty: 0, effort: 0, timeToProgram: 0, bugsDelivered: 0 },
|
|
869
|
+
maintainabilityIndex: 0,
|
|
870
|
+
duplicateRate: 0,
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
exports.GoAnalyzer = GoAnalyzer;
|
|
875
|
+
exports.GoLanguageSupport = {
|
|
876
|
+
id: 'go',
|
|
877
|
+
name: 'Go',
|
|
878
|
+
extensions: ['.go'],
|
|
879
|
+
parser: new GoParser(),
|
|
880
|
+
analyzer: new GoAnalyzer(),
|
|
881
|
+
};
|
|
882
|
+
//# sourceMappingURL=go.js.map
|