@shapeshift-labs/frontier-lang-compiler 0.2.28 → 0.2.29
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/README.md +9 -2
- package/bench/smoke.mjs +2 -1
- package/dist/index.d.ts +31 -0
- package/dist/index.js +550 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -333,6 +333,7 @@ Use injected parser adapters when a real language parser is available but should
|
|
|
333
333
|
import {
|
|
334
334
|
createBabelNativeImporterAdapter,
|
|
335
335
|
createClangAstNativeImporterAdapter,
|
|
336
|
+
createGoAstNativeImporterAdapter,
|
|
336
337
|
createPythonAstNativeImporterAdapter,
|
|
337
338
|
createRustSynNativeImporterAdapter,
|
|
338
339
|
importNativeProject,
|
|
@@ -354,6 +355,10 @@ const clangAstAdapter = createClangAstNativeImporterAdapter({
|
|
|
354
355
|
cStandard: 'c11',
|
|
355
356
|
compileFlags: ['-std=c11']
|
|
356
357
|
});
|
|
358
|
+
const goAstAdapter = createGoAstNativeImporterAdapter({
|
|
359
|
+
parserModule: hostGoAstParser,
|
|
360
|
+
goVersion: '1.22'
|
|
361
|
+
});
|
|
357
362
|
|
|
358
363
|
const imported = await runNativeImporterAdapter(babelAdapter, {
|
|
359
364
|
sourcePath: 'src/todo.ts',
|
|
@@ -362,12 +367,13 @@ const imported = await runNativeImporterAdapter(babelAdapter, {
|
|
|
362
367
|
|
|
363
368
|
const project = await importNativeProject({
|
|
364
369
|
projectRoot: 'src',
|
|
365
|
-
adapters: [babelAdapter, pythonAstAdapter, rustSynAdapter, clangAstAdapter],
|
|
370
|
+
adapters: [babelAdapter, pythonAstAdapter, rustSynAdapter, clangAstAdapter, goAstAdapter],
|
|
366
371
|
sources: [
|
|
367
372
|
{ language: 'typescript', adapter: babelAdapter.id, sourcePath: 'src/todo.ts', sourceText },
|
|
368
373
|
{ language: 'python', adapter: pythonAstAdapter.id, sourcePath: 'tools/todo.py', sourceText: pythonSource },
|
|
369
374
|
{ language: 'rust', adapter: rustSynAdapter.id, sourcePath: 'src/todo.rs', sourceText: rustSource },
|
|
370
|
-
{ language: 'c', adapter: clangAstAdapter.id, sourcePath: 'native/todo.c', sourceText: cSource }
|
|
375
|
+
{ language: 'c', adapter: clangAstAdapter.id, sourcePath: 'native/todo.c', sourceText: cSource },
|
|
376
|
+
{ language: 'go', adapter: goAstAdapter.id, sourcePath: 'cmd/todo/main.go', sourceText: goSource }
|
|
371
377
|
]
|
|
372
378
|
});
|
|
373
379
|
|
|
@@ -389,6 +395,7 @@ The built-in adapter factories are dependency-light wrappers for caller-owned pa
|
|
|
389
395
|
- `createPythonAstNativeImporterAdapter`
|
|
390
396
|
- `createRustSynNativeImporterAdapter`
|
|
391
397
|
- `createClangAstNativeImporterAdapter`
|
|
398
|
+
- `createGoAstNativeImporterAdapter`
|
|
392
399
|
- `createTreeSitterNativeImporterAdapter`
|
|
393
400
|
|
|
394
401
|
Adapter summaries include a structured `coverage` record so merge queues can distinguish exact parser AST imports from declaration scans. The record declares exactness, parser token/trivia support, diagnostics support, source-range and generated-range support, and semantic coverage. Built-in wrappers normalize native AST/CST nodes and declaration-level semantic indexes; they do not claim resolved references, types, control flow, generated ranges, or token/trivia fidelity unless the host adapter supplies that evidence.
|
package/bench/smoke.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
compileFrontierSource,
|
|
5
5
|
createClangAstNativeImporterAdapter,
|
|
6
6
|
createEstreeNativeImporterAdapter,
|
|
7
|
+
createGoAstNativeImporterAdapter,
|
|
7
8
|
createNativeImportCoverageMatrix,
|
|
8
9
|
createNativeParserAstFormatMatrix,
|
|
9
10
|
createProjectionTargetLossMatrix,
|
|
@@ -80,7 +81,7 @@ const matrixDurationMs = performance.now() - matrixStart;
|
|
|
80
81
|
const parserFormatMatrixStart = performance.now();
|
|
81
82
|
const parserFormatMatrix = createNativeParserAstFormatMatrix({
|
|
82
83
|
imports: nativeImportResults,
|
|
83
|
-
adapters: [estreeAdapter, createPythonAstNativeImporterAdapter(), createRustSynNativeImporterAdapter(), createClangAstNativeImporterAdapter()]
|
|
84
|
+
adapters: [estreeAdapter, createPythonAstNativeImporterAdapter(), createRustSynNativeImporterAdapter(), createClangAstNativeImporterAdapter(), createGoAstNativeImporterAdapter()]
|
|
84
85
|
});
|
|
85
86
|
const parserFormatMatrixDurationMs = performance.now() - parserFormatMatrixStart;
|
|
86
87
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1435,6 +1435,36 @@ export interface ClangAstNativeImporterAdapterOptions {
|
|
|
1435
1435
|
readonly maxNodes?: number;
|
|
1436
1436
|
}
|
|
1437
1437
|
|
|
1438
|
+
export interface GoAstNativeImporterAdapterOptions {
|
|
1439
|
+
readonly id?: string;
|
|
1440
|
+
readonly language?: FrontierSourceLanguage;
|
|
1441
|
+
readonly parser?: string;
|
|
1442
|
+
readonly version?: string;
|
|
1443
|
+
readonly capabilities?: readonly string[];
|
|
1444
|
+
readonly coverage?: NativeImporterAdapterCoverageInput;
|
|
1445
|
+
readonly supportedExtensions?: readonly string[];
|
|
1446
|
+
readonly diagnostics?: readonly NativeImporterAdapterDiagnostic[];
|
|
1447
|
+
readonly ast?: unknown;
|
|
1448
|
+
readonly file?: unknown;
|
|
1449
|
+
readonly sourceFile?: unknown;
|
|
1450
|
+
readonly package?: unknown;
|
|
1451
|
+
readonly parse?: (sourceText: string, options: Record<string, unknown>) => unknown;
|
|
1452
|
+
readonly parserModule?: { readonly parse: (sourceText: string, options: Record<string, unknown>) => unknown };
|
|
1453
|
+
readonly goAst?: { readonly parse: (sourceText: string, options: Record<string, unknown>) => unknown };
|
|
1454
|
+
readonly goParser?: { readonly parse: (sourceText: string, options: Record<string, unknown>) => unknown };
|
|
1455
|
+
readonly parserOptions?: Record<string, unknown>;
|
|
1456
|
+
readonly mode?: string | number;
|
|
1457
|
+
readonly goVersion?: string;
|
|
1458
|
+
readonly packageName?: string;
|
|
1459
|
+
readonly fileSet?: unknown;
|
|
1460
|
+
readonly fset?: unknown;
|
|
1461
|
+
readonly includeComments?: boolean;
|
|
1462
|
+
readonly buildTags?: readonly string[];
|
|
1463
|
+
readonly generated?: boolean;
|
|
1464
|
+
readonly typeEvidence?: unknown;
|
|
1465
|
+
readonly maxNodes?: number;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1438
1468
|
export interface TreeSitterNativeImporterAdapterOptions {
|
|
1439
1469
|
readonly id?: string;
|
|
1440
1470
|
readonly language?: FrontierSourceLanguage;
|
|
@@ -1807,6 +1837,7 @@ export declare function createTypeScriptCompilerNativeImporterAdapter(options?:
|
|
|
1807
1837
|
export declare function createPythonAstNativeImporterAdapter(options?: PythonAstNativeImporterAdapterOptions): NativeImporterAdapter;
|
|
1808
1838
|
export declare function createRustSynNativeImporterAdapter(options?: RustSynNativeImporterAdapterOptions): NativeImporterAdapter;
|
|
1809
1839
|
export declare function createClangAstNativeImporterAdapter(options?: ClangAstNativeImporterAdapterOptions): NativeImporterAdapter;
|
|
1840
|
+
export declare function createGoAstNativeImporterAdapter(options?: GoAstNativeImporterAdapterOptions): NativeImporterAdapter;
|
|
1810
1841
|
export declare function createTreeSitterNativeImporterAdapter(options?: TreeSitterNativeImporterAdapterOptions): NativeImporterAdapter;
|
|
1811
1842
|
export declare function runNativeImporterAdapter(adapter: NativeImporterAdapter, input: RunNativeImporterAdapterOptions): Promise<NativeImporterAdapterImportResult>;
|
|
1812
1843
|
export declare function runNativeTargetProjectionAdapter(adapter: NativeTargetProjectionAdapter, input: NativeTargetProjectionAdapterInput): NativeTargetProjectionResult;
|
package/dist/index.js
CHANGED
|
@@ -415,6 +415,18 @@ export const NativeParserAstFormatProfiles = Object.freeze([
|
|
|
415
415
|
supportsErrorRecovery: false,
|
|
416
416
|
notes: ['Clang JSON AST dumps expose compiler AST declarations and source ranges after preprocessing; compile commands, macros, inactive branches, type checking, and lossless formatting remain host-owned evidence.']
|
|
417
417
|
}),
|
|
418
|
+
nativeParserAstFormatProfile('go-ast', {
|
|
419
|
+
aliases: ['go/parser', 'goast', 'golang-ast'],
|
|
420
|
+
kind: 'compiler-ast',
|
|
421
|
+
languages: ['go'],
|
|
422
|
+
parserAdapters: ['go/ast', 'go/parser', 'go-ast'],
|
|
423
|
+
exactness: 'exact-parser-ast',
|
|
424
|
+
sourceRangeModel: 'token-position',
|
|
425
|
+
preservesTokens: false,
|
|
426
|
+
preservesTrivia: false,
|
|
427
|
+
supportsErrorRecovery: true,
|
|
428
|
+
notes: ['Go go/ast trees expose parser syntax nodes and token positions; FileSet, build tags, generated-code classification, package loading, go/types, comments/trivia, and control-flow evidence remain host-owned.']
|
|
429
|
+
}),
|
|
418
430
|
nativeParserAstFormatProfile('tree-sitter', {
|
|
419
431
|
kind: 'concrete-syntax-tree',
|
|
420
432
|
languages: ['mixed'],
|
|
@@ -2781,6 +2793,68 @@ export function createClangAstNativeImporterAdapter(options = {}) {
|
|
|
2781
2793
|
};
|
|
2782
2794
|
}
|
|
2783
2795
|
|
|
2796
|
+
export function createGoAstNativeImporterAdapter(options = {}) {
|
|
2797
|
+
return {
|
|
2798
|
+
id: options.id ?? 'frontier.go-ast-native-importer',
|
|
2799
|
+
language: options.language ?? 'go',
|
|
2800
|
+
parser: options.parser ?? 'go/parser',
|
|
2801
|
+
version: options.version,
|
|
2802
|
+
capabilities: uniqueStrings(['nativeAst', 'semanticIndex', 'sourceMaps', 'diagnostics', ...(options.capabilities ?? [])]),
|
|
2803
|
+
coverage: nativeImporterAdapterCoverage({
|
|
2804
|
+
exactness: 'exact-parser-ast',
|
|
2805
|
+
exactAst: true,
|
|
2806
|
+
tokens: false,
|
|
2807
|
+
trivia: false,
|
|
2808
|
+
diagnostics: true,
|
|
2809
|
+
sourceRanges: true,
|
|
2810
|
+
generatedRanges: false,
|
|
2811
|
+
semanticCoverage: declarationSemanticCoverage(),
|
|
2812
|
+
notes: [
|
|
2813
|
+
'Normalizes caller-owned Go go/ast-shaped File or Package trees into native AST nodes and declaration-level semantic index records.',
|
|
2814
|
+
'Go AST imports do not resolve types, imports, build tags, generated code, or control flow by themselves; attach FileSet, go/types, go/packages, and build context evidence for those claims.'
|
|
2815
|
+
]
|
|
2816
|
+
}, options.coverage),
|
|
2817
|
+
supportedExtensions: options.supportedExtensions ?? ['.go'],
|
|
2818
|
+
diagnostics: options.diagnostics,
|
|
2819
|
+
parse(input) {
|
|
2820
|
+
const parsed = input.options?.ast
|
|
2821
|
+
?? input.options?.nativeAst
|
|
2822
|
+
?? input.options?.file
|
|
2823
|
+
?? input.options?.sourceFile
|
|
2824
|
+
?? input.options?.package
|
|
2825
|
+
?? options.ast
|
|
2826
|
+
?? options.file
|
|
2827
|
+
?? options.sourceFile
|
|
2828
|
+
?? options.package
|
|
2829
|
+
?? parseGoAstSource(input, options);
|
|
2830
|
+
const root = goAstRoot(parsed);
|
|
2831
|
+
if (!root) {
|
|
2832
|
+
return missingInjectedParserResult(input, {
|
|
2833
|
+
parser: options.parser ?? 'go/parser',
|
|
2834
|
+
adapterId: options.id ?? 'frontier.go-ast-native-importer',
|
|
2835
|
+
message: 'createGoAstNativeImporterAdapter requires an injected Go ast.File/Package-shaped object, parserModule.parse function, parse function, or adapterOptions.ast.'
|
|
2836
|
+
});
|
|
2837
|
+
}
|
|
2838
|
+
const parseDiagnostics = normalizeParserErrors(parsed?.errors ?? parsed?.diagnostics, input, {
|
|
2839
|
+
parser: options.parser ?? 'go/parser'
|
|
2840
|
+
});
|
|
2841
|
+
return createNativeImportFromGoAst(root, input, {
|
|
2842
|
+
parser: options.parser ?? 'go/parser',
|
|
2843
|
+
astFormat: 'go-ast',
|
|
2844
|
+
maxNodes: options.maxNodes,
|
|
2845
|
+
diagnostics: parseDiagnostics,
|
|
2846
|
+
goVersion: options.goVersion ?? input.options?.goVersion ?? parsed?.goVersion,
|
|
2847
|
+
packageName: options.packageName ?? input.options?.packageName ?? parsed?.packageName ?? root?.Name?.Name ?? root?.Name,
|
|
2848
|
+
fileSet: input.options?.fileSet ?? input.options?.fset ?? options.fileSet ?? options.fset ?? parsed?.fileSet ?? parsed?.fset,
|
|
2849
|
+
includeComments: options.includeComments ?? input.options?.includeComments,
|
|
2850
|
+
buildTags: input.options?.buildTags ?? options.buildTags ?? parsed?.buildTags,
|
|
2851
|
+
generated: input.options?.generated ?? options.generated ?? parsed?.generated,
|
|
2852
|
+
typeEvidence: input.options?.typeEvidence ?? options.typeEvidence ?? parsed?.typeEvidence
|
|
2853
|
+
});
|
|
2854
|
+
}
|
|
2855
|
+
};
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2784
2858
|
export function createTreeSitterNativeImporterAdapter(options = {}) {
|
|
2785
2859
|
return {
|
|
2786
2860
|
id: options.id ?? `frontier.tree-sitter-${idFragment(options.language ?? 'source')}-native-importer`,
|
|
@@ -7222,6 +7296,7 @@ function parserAstFormatIdForParser(parser) {
|
|
|
7222
7296
|
if (text === 'syn' || text.includes('rust-syn')) return 'rust-syn';
|
|
7223
7297
|
if (text.includes('rust-analyzer') || text.includes('rowan')) return 'rust-analyzer-rowan';
|
|
7224
7298
|
if (text.includes('clang') || text.includes('libclang')) return 'clang-ast-json';
|
|
7299
|
+
if (text === 'go' || text.includes('go-parser') || text.includes('go-ast') || text.includes('go/parser') || text.includes('go/ast')) return 'go-ast';
|
|
7225
7300
|
if (text.includes('tree-sitter') || text.includes('treesitter')) return 'tree-sitter';
|
|
7226
7301
|
if (text.includes('babel')) return 'babel';
|
|
7227
7302
|
if (text.includes('estree')) return 'estree';
|
|
@@ -8287,6 +8362,22 @@ function parseClangAstSource(input, options) {
|
|
|
8287
8362
|
return parse(input.sourceText, parserOptions);
|
|
8288
8363
|
}
|
|
8289
8364
|
|
|
8365
|
+
function parseGoAstSource(input, options) {
|
|
8366
|
+
const parse = options.parse ?? options.parserModule?.parse ?? options.goAst?.parse ?? options.goParser?.parse;
|
|
8367
|
+
if (typeof parse !== 'function') return undefined;
|
|
8368
|
+
const parserOptions = {
|
|
8369
|
+
sourcePath: input.sourcePath,
|
|
8370
|
+
filename: input.sourcePath,
|
|
8371
|
+
mode: options.mode ?? input.options?.mode ?? 'ParseComments',
|
|
8372
|
+
goVersion: options.goVersion ?? input.options?.goVersion,
|
|
8373
|
+
packageName: options.packageName ?? input.options?.packageName,
|
|
8374
|
+
includeComments: options.includeComments ?? input.options?.includeComments,
|
|
8375
|
+
...(options.parserOptions ?? {}),
|
|
8376
|
+
...(input.options?.parserOptions ?? {})
|
|
8377
|
+
};
|
|
8378
|
+
return parse(input.sourceText, parserOptions);
|
|
8379
|
+
}
|
|
8380
|
+
|
|
8290
8381
|
function createNativeImportFromSyntaxAst(ast, input, options) {
|
|
8291
8382
|
const root = normalizeSyntaxAstRoot(ast, options.astFormat);
|
|
8292
8383
|
if (!root) {
|
|
@@ -8443,6 +8534,41 @@ function createNativeImportFromClangAst(root, input, options) {
|
|
|
8443
8534
|
};
|
|
8444
8535
|
}
|
|
8445
8536
|
|
|
8537
|
+
function createNativeImportFromGoAst(root, input, options) {
|
|
8538
|
+
const context = createAstNormalizationContext(input, options);
|
|
8539
|
+
visitGoAstNode(root, context, 'root');
|
|
8540
|
+
if (context.truncated) {
|
|
8541
|
+
context.losses.push(truncatedAstLoss(input, context, options));
|
|
8542
|
+
}
|
|
8543
|
+
const semantic = semanticIndexFromNativeDeclarations(context.declarations, input, options);
|
|
8544
|
+
return {
|
|
8545
|
+
rootId: context.rootId,
|
|
8546
|
+
nodes: context.nodes,
|
|
8547
|
+
semanticIndex: semantic.semanticIndex,
|
|
8548
|
+
mappings: semantic.mappings,
|
|
8549
|
+
losses: mergeNativeLosses(context.losses, options.diagnostics?.map((diagnostic, index) => adapterDiagnosticToLoss(diagnostic, index, {
|
|
8550
|
+
id: input.adapterId,
|
|
8551
|
+
version: input.adapterVersion
|
|
8552
|
+
}, input)) ?? []),
|
|
8553
|
+
evidence: semantic.evidence,
|
|
8554
|
+
diagnostics: options.diagnostics,
|
|
8555
|
+
metadata: {
|
|
8556
|
+
astFormat: options.astFormat,
|
|
8557
|
+
parser: options.parser,
|
|
8558
|
+
goVersion: options.goVersion,
|
|
8559
|
+
packageName: options.packageName,
|
|
8560
|
+
includeComments: Boolean(options.includeComments),
|
|
8561
|
+
buildTags: Array.isArray(options.buildTags) ? options.buildTags.slice() : options.buildTags,
|
|
8562
|
+
generated: options.generated,
|
|
8563
|
+
fileSetEvidence: Boolean(options.fileSet),
|
|
8564
|
+
typeEvidence: goTypeEvidenceSummary(options.typeEvidence),
|
|
8565
|
+
normalizedNodeCount: Object.keys(context.nodes).length,
|
|
8566
|
+
declarationCount: context.declarations.length,
|
|
8567
|
+
truncated: context.truncated
|
|
8568
|
+
}
|
|
8569
|
+
};
|
|
8570
|
+
}
|
|
8571
|
+
|
|
8446
8572
|
function createNativeImportFromTreeSitter(root, input, options) {
|
|
8447
8573
|
const context = createAstNormalizationContext(input, options);
|
|
8448
8574
|
visitTreeSitterNode(root, context, 'root');
|
|
@@ -8748,6 +8874,104 @@ function visitClangAstNode(node, context, propertyPath) {
|
|
|
8748
8874
|
return id;
|
|
8749
8875
|
}
|
|
8750
8876
|
|
|
8877
|
+
function visitGoAstNode(node, context, propertyPath) {
|
|
8878
|
+
if (!isGoAstNode(node) || context.truncated) return undefined;
|
|
8879
|
+
if (context.objectIds.has(node)) return context.objectIds.get(node);
|
|
8880
|
+
if (context.counter >= context.maxNodes) {
|
|
8881
|
+
context.truncated = true;
|
|
8882
|
+
return undefined;
|
|
8883
|
+
}
|
|
8884
|
+
const kind = goAstKind(node);
|
|
8885
|
+
const span = spanFromGoAstNode(node, context.input, context.options);
|
|
8886
|
+
const id = nativeNodeId(context, kind, { start: { line: span?.startLine, column: span?.startColumn } }, propertyPath);
|
|
8887
|
+
context.objectIds.set(node, id);
|
|
8888
|
+
if (!context.rootId) context.rootId = id;
|
|
8889
|
+
const children = [];
|
|
8890
|
+
for (const [field, value] of goAstChildEntries(node)) {
|
|
8891
|
+
if (Array.isArray(value)) {
|
|
8892
|
+
value.forEach((entry, index) => {
|
|
8893
|
+
const childId = visitGoAstNode(entry, context, `${propertyPath}.${field}[${index}]`);
|
|
8894
|
+
if (childId) children.push(childId);
|
|
8895
|
+
});
|
|
8896
|
+
} else {
|
|
8897
|
+
const childId = visitGoAstNode(value, context, `${propertyPath}.${field}`);
|
|
8898
|
+
if (childId) children.push(childId);
|
|
8899
|
+
}
|
|
8900
|
+
}
|
|
8901
|
+
const declarations = goAstDeclarations(node, kind, id, context.input);
|
|
8902
|
+
const declaration = declarations[0];
|
|
8903
|
+
const nativeNode = {
|
|
8904
|
+
id,
|
|
8905
|
+
kind,
|
|
8906
|
+
languageKind: `${context.input.language}.${kind}`,
|
|
8907
|
+
span,
|
|
8908
|
+
value: declaration?.name ?? goAstNodeValue(node),
|
|
8909
|
+
fields: primitiveGoAstFields(node, kind),
|
|
8910
|
+
children,
|
|
8911
|
+
metadata: {
|
|
8912
|
+
astFormat: context.options.astFormat,
|
|
8913
|
+
propertyPath,
|
|
8914
|
+
positionKind: goAstPositionKind(node),
|
|
8915
|
+
packageName: context.options.packageName
|
|
8916
|
+
}
|
|
8917
|
+
};
|
|
8918
|
+
context.nodes[id] = nativeNode;
|
|
8919
|
+
for (const entry of declarations) {
|
|
8920
|
+
context.declarations.push({ ...entry, nativeNode });
|
|
8921
|
+
}
|
|
8922
|
+
if (goBadAstKind(kind)) {
|
|
8923
|
+
context.losses.push({
|
|
8924
|
+
id: `loss_${idFragment(id)}_go_bad_node`,
|
|
8925
|
+
severity: 'error',
|
|
8926
|
+
phase: 'parse',
|
|
8927
|
+
sourceFormat: context.input.language,
|
|
8928
|
+
kind: 'unsupportedSyntax',
|
|
8929
|
+
message: 'Go parser recovered a BadDecl/BadExpr/BadStmt node; semantic import is partial until syntax errors are resolved.',
|
|
8930
|
+
span,
|
|
8931
|
+
nodeId: id,
|
|
8932
|
+
metadata: {
|
|
8933
|
+
parser: context.options.parser,
|
|
8934
|
+
astFormat: context.options.astFormat,
|
|
8935
|
+
nodeKind: kind
|
|
8936
|
+
}
|
|
8937
|
+
});
|
|
8938
|
+
}
|
|
8939
|
+
if (kind === 'FuncDecl' && goReceiverFieldCount(node) > 1) {
|
|
8940
|
+
context.losses.push({
|
|
8941
|
+
id: `loss_${idFragment(id)}_go_multiple_receivers`,
|
|
8942
|
+
severity: 'warning',
|
|
8943
|
+
phase: 'parse',
|
|
8944
|
+
sourceFormat: context.input.language,
|
|
8945
|
+
kind: 'unsupportedSyntax',
|
|
8946
|
+
message: 'Go parser accepted multiple receiver fields; valid method ownership requires a single receiver.',
|
|
8947
|
+
span,
|
|
8948
|
+
nodeId: id,
|
|
8949
|
+
metadata: {
|
|
8950
|
+
parser: context.options.parser,
|
|
8951
|
+
astFormat: context.options.astFormat,
|
|
8952
|
+
receiverFieldCount: goReceiverFieldCount(node)
|
|
8953
|
+
}
|
|
8954
|
+
});
|
|
8955
|
+
}
|
|
8956
|
+
if (goGeneratedCodeMarker(node, kind)) {
|
|
8957
|
+
context.losses.push({
|
|
8958
|
+
id: `loss_${idFragment(id)}_go_generated_code`,
|
|
8959
|
+
severity: 'warning',
|
|
8960
|
+
phase: 'parse',
|
|
8961
|
+
sourceFormat: context.input.language,
|
|
8962
|
+
kind: 'generatedCode',
|
|
8963
|
+
message: 'Go generated-code marker was imported; regeneration provenance and source ownership require host evidence.',
|
|
8964
|
+
span,
|
|
8965
|
+
nodeId: id,
|
|
8966
|
+
metadata: {
|
|
8967
|
+
parser: context.options.parser,
|
|
8968
|
+
astFormat: context.options.astFormat
|
|
8969
|
+
}
|
|
8970
|
+
});
|
|
8971
|
+
}
|
|
8972
|
+
return id;
|
|
8973
|
+
}
|
|
8974
|
+
|
|
8751
8975
|
function visitTreeSitterNode(node, context, propertyPath) {
|
|
8752
8976
|
if (!node || typeof node !== 'object' || context.truncated) return undefined;
|
|
8753
8977
|
if (context.objectIds.has(node)) return context.objectIds.get(node);
|
|
@@ -10657,6 +10881,332 @@ function serializableIncludeGraphSummary(value) {
|
|
|
10657
10881
|
return Object.keys(summary).length ? summary : { present: true };
|
|
10658
10882
|
}
|
|
10659
10883
|
|
|
10884
|
+
function goAstRoot(value) {
|
|
10885
|
+
if (!value || typeof value !== 'object') return undefined;
|
|
10886
|
+
if (isGoAstNode(value)) return value;
|
|
10887
|
+
if (isGoAstNode(value.ast)) return value.ast;
|
|
10888
|
+
if (isGoAstNode(value.file)) return value.file;
|
|
10889
|
+
if (isGoAstNode(value.sourceFile)) return value.sourceFile;
|
|
10890
|
+
if (isGoAstNode(value.root)) return value.root;
|
|
10891
|
+
if (isGoAstNode(value.package)) return value.package;
|
|
10892
|
+
if (value.files && typeof value.files === 'object') return { kind: 'Package', Name: value.name ?? value.packageName, Files: value.files };
|
|
10893
|
+
return undefined;
|
|
10894
|
+
}
|
|
10895
|
+
|
|
10896
|
+
function isGoAstNode(value) {
|
|
10897
|
+
return Boolean(value && typeof value === 'object' && typeof goAstKind(value) === 'string');
|
|
10898
|
+
}
|
|
10899
|
+
|
|
10900
|
+
function goAstKind(node) {
|
|
10901
|
+
if (!node || typeof node !== 'object') return undefined;
|
|
10902
|
+
const declared = node.kind ?? node._type ?? node.type ?? node.nodeType ?? node.astKind;
|
|
10903
|
+
if (typeof declared === 'string') return normalizeGoAstKind(declared);
|
|
10904
|
+
if (Array.isArray(node.Decls) || Array.isArray(node.decls)) return 'File';
|
|
10905
|
+
if (node.Files || node.files) return 'Package';
|
|
10906
|
+
if (node.Name && node.Type && (node.Body || node.Recv !== undefined || node.recv !== undefined)) return 'FuncDecl';
|
|
10907
|
+
if (node.Tok && (node.Specs || node.specs)) return 'GenDecl';
|
|
10908
|
+
if (node.Path && (node.Name !== undefined || node.EndPos !== undefined)) return 'ImportSpec';
|
|
10909
|
+
if (node.Names && node.Type !== undefined) return 'ValueSpec';
|
|
10910
|
+
if (node.Name && node.Type !== undefined) return 'TypeSpec';
|
|
10911
|
+
if (node.List && (node.Opening !== undefined || node.Closing !== undefined)) return 'FieldList';
|
|
10912
|
+
return undefined;
|
|
10913
|
+
}
|
|
10914
|
+
|
|
10915
|
+
function normalizeGoAstKind(kind) {
|
|
10916
|
+
const text = String(kind).replace(/^(?:ast\.)?\*/, '').replace(/^(?:go\/ast\.)/, '');
|
|
10917
|
+
if (/^file$/i.test(text)) return 'File';
|
|
10918
|
+
if (/^package$/i.test(text)) return 'Package';
|
|
10919
|
+
if (/^funcdecl$/i.test(text) || /^func_decl$/i.test(text)) return 'FuncDecl';
|
|
10920
|
+
if (/^gendecl$/i.test(text) || /^gen_decl$/i.test(text)) return 'GenDecl';
|
|
10921
|
+
if (/^importspec$/i.test(text) || /^import_spec$/i.test(text)) return 'ImportSpec';
|
|
10922
|
+
if (/^typespec$/i.test(text) || /^type_spec$/i.test(text)) return 'TypeSpec';
|
|
10923
|
+
if (/^valuespec$/i.test(text) || /^value_spec$/i.test(text)) return 'ValueSpec';
|
|
10924
|
+
if (/^structtype$/i.test(text) || /^struct_type$/i.test(text)) return 'StructType';
|
|
10925
|
+
if (/^interfacetype$/i.test(text) || /^interface_type$/i.test(text)) return 'InterfaceType';
|
|
10926
|
+
if (/^fieldlist$/i.test(text) || /^field_list$/i.test(text)) return 'FieldList';
|
|
10927
|
+
return text;
|
|
10928
|
+
}
|
|
10929
|
+
|
|
10930
|
+
function ignoredGoAstField(key) {
|
|
10931
|
+
return key === '_type'
|
|
10932
|
+
|| key === 'type'
|
|
10933
|
+
|| key === 'kind'
|
|
10934
|
+
|| key === 'nodeType'
|
|
10935
|
+
|| key === 'astKind'
|
|
10936
|
+
|| key === 'parent'
|
|
10937
|
+
|| key === 'Obj'
|
|
10938
|
+
|| key === 'object'
|
|
10939
|
+
|| key === 'Scope'
|
|
10940
|
+
|| key === 'scope'
|
|
10941
|
+
|| key === 'Unresolved'
|
|
10942
|
+
|| key === 'unresolved'
|
|
10943
|
+
|| key === 'FileStart'
|
|
10944
|
+
|| key === 'FileEnd'
|
|
10945
|
+
|| key === 'Package'
|
|
10946
|
+
|| key === 'Name'
|
|
10947
|
+
|| key === 'Path'
|
|
10948
|
+
|| key === 'Pos'
|
|
10949
|
+
|| key === 'End'
|
|
10950
|
+
|| key === 'pos'
|
|
10951
|
+
|| key === 'end';
|
|
10952
|
+
}
|
|
10953
|
+
|
|
10954
|
+
function primitiveGoAstFields(node, kind) {
|
|
10955
|
+
const fields = { kind };
|
|
10956
|
+
const name = goAstDeclarationName(node);
|
|
10957
|
+
if (name) fields.name = name;
|
|
10958
|
+
const type = goAstTypeName(node.Type ?? node.type);
|
|
10959
|
+
if (type) fields.type = type;
|
|
10960
|
+
const tok = goAstTokenName(node.Tok ?? node.tok);
|
|
10961
|
+
if (tok) fields.token = tok;
|
|
10962
|
+
const importPath = goAstImportPath(node);
|
|
10963
|
+
if (importPath) fields.importPath = importPath;
|
|
10964
|
+
const receiver = goAstReceiverName(node);
|
|
10965
|
+
if (receiver) fields.receiver = receiver;
|
|
10966
|
+
for (const key of ['Incomplete', 'Doc', 'Comment']) {
|
|
10967
|
+
const value = node[key] ?? node[key[0].toLowerCase() + key.slice(1)];
|
|
10968
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || value === null) fields[key[0].toLowerCase() + key.slice(1)] = value;
|
|
10969
|
+
}
|
|
10970
|
+
if (Array.isArray(node.Names ?? node.names)) {
|
|
10971
|
+
fields.names = (node.Names ?? node.names).map(goAstIdentName).filter(Boolean).join(',');
|
|
10972
|
+
}
|
|
10973
|
+
return fields;
|
|
10974
|
+
}
|
|
10975
|
+
|
|
10976
|
+
function spanFromGoAstNode(node, input, options = {}) {
|
|
10977
|
+
const start = goAstPosition(node.Pos ?? node.pos ?? node.Name?.NamePos ?? node.name?.namePos ?? node.Package, options);
|
|
10978
|
+
const end = goAstPosition(node.End ?? node.end ?? node.EndPos ?? node.endPos, options);
|
|
10979
|
+
if (!start) return undefined;
|
|
10980
|
+
return {
|
|
10981
|
+
sourceId: input.sourceHash,
|
|
10982
|
+
path: start.path ?? end?.path ?? input.sourcePath,
|
|
10983
|
+
startLine: start.line,
|
|
10984
|
+
startColumn: start.column,
|
|
10985
|
+
endLine: end?.line,
|
|
10986
|
+
endColumn: end?.column
|
|
10987
|
+
};
|
|
10988
|
+
}
|
|
10989
|
+
|
|
10990
|
+
function goAstPosition(value, options = {}) {
|
|
10991
|
+
if (!value) return undefined;
|
|
10992
|
+
if (typeof value === 'object') {
|
|
10993
|
+
const position = value.position ?? value.Position ?? value.pos ?? value.Pos ?? value;
|
|
10994
|
+
const line = position.Line ?? position.line;
|
|
10995
|
+
const column = position.Column ?? position.column ?? position.Col ?? position.col;
|
|
10996
|
+
if (typeof line === 'number') {
|
|
10997
|
+
return {
|
|
10998
|
+
path: position.Filename ?? position.filename ?? position.file ?? position.path,
|
|
10999
|
+
line,
|
|
11000
|
+
column: typeof column === 'number' ? column : undefined
|
|
11001
|
+
};
|
|
11002
|
+
}
|
|
11003
|
+
}
|
|
11004
|
+
const fileSet = options.fileSet ?? options.fset;
|
|
11005
|
+
const positionFor = typeof fileSet?.PositionFor === 'function'
|
|
11006
|
+
? fileSet.PositionFor.bind(fileSet)
|
|
11007
|
+
: typeof fileSet?.positionFor === 'function'
|
|
11008
|
+
? fileSet.positionFor.bind(fileSet)
|
|
11009
|
+
: typeof fileSet?.Position === 'function'
|
|
11010
|
+
? fileSet.Position.bind(fileSet)
|
|
11011
|
+
: typeof fileSet?.position === 'function'
|
|
11012
|
+
? fileSet.position.bind(fileSet)
|
|
11013
|
+
: undefined;
|
|
11014
|
+
if (positionFor) {
|
|
11015
|
+
const resolved = positionFor(value, true);
|
|
11016
|
+
return goAstPosition(resolved, options);
|
|
11017
|
+
}
|
|
11018
|
+
return undefined;
|
|
11019
|
+
}
|
|
11020
|
+
|
|
11021
|
+
function goAstPositionKind(node) {
|
|
11022
|
+
const pos = node.Pos ?? node.pos ?? node.Name?.NamePos ?? node.name?.namePos;
|
|
11023
|
+
if (!pos) return undefined;
|
|
11024
|
+
if (typeof pos === 'object') return 'token.Position';
|
|
11025
|
+
return 'token.Pos';
|
|
11026
|
+
}
|
|
11027
|
+
|
|
11028
|
+
function goAstDeclarations(node, kind, nativeNodeId, input) {
|
|
11029
|
+
if (kind === 'ImportSpec') {
|
|
11030
|
+
const name = goAstImportPath(node);
|
|
11031
|
+
return name ? [declarationRecord(input, nativeNodeId, name, 'module', 'import')] : [];
|
|
11032
|
+
}
|
|
11033
|
+
if (kind === 'FuncDecl') {
|
|
11034
|
+
const name = goAstDeclarationName(node);
|
|
11035
|
+
if (!name) return [];
|
|
11036
|
+
const receiver = goAstReceiverName(node);
|
|
11037
|
+
return [declarationRecord(input, nativeNodeId, receiver ? `${receiver}.${name}` : name, receiver ? 'method' : 'function', node.Body || node.body ? 'definition' : 'declaration')];
|
|
11038
|
+
}
|
|
11039
|
+
if (kind === 'TypeSpec') {
|
|
11040
|
+
const name = goAstDeclarationName(node);
|
|
11041
|
+
return name ? [declarationRecord(input, nativeNodeId, name, goAstTypeSpecSymbolKind(node), 'definition')] : [];
|
|
11042
|
+
}
|
|
11043
|
+
if (kind === 'ValueSpec') {
|
|
11044
|
+
const names = goAstValueSpecNames(node);
|
|
11045
|
+
const token = goAstTokenName(node.parentTok ?? node.Tok ?? node.tok);
|
|
11046
|
+
return names.map((name) => declarationRecord(input, nativeNodeId, name, token === 'CONST' || token === 'const' ? 'constant' : 'variable', 'definition'));
|
|
11047
|
+
}
|
|
11048
|
+
if (kind === 'Field') {
|
|
11049
|
+
return goAstValueSpecNames(node).map((name) => declarationRecord(input, nativeNodeId, name, 'property', 'definition'));
|
|
11050
|
+
}
|
|
11051
|
+
if (kind === 'Package' || kind === 'File') {
|
|
11052
|
+
const name = goAstPackageName(node);
|
|
11053
|
+
return name ? [declarationRecord(input, nativeNodeId, name, 'module', 'definition')] : [];
|
|
11054
|
+
}
|
|
11055
|
+
return [];
|
|
11056
|
+
}
|
|
11057
|
+
|
|
11058
|
+
function goAstChildEntries(node) {
|
|
11059
|
+
const fieldNames = Object.keys(node).filter((key) => !ignoredGoAstField(key));
|
|
11060
|
+
const entries = [];
|
|
11061
|
+
for (const field of fieldNames) {
|
|
11062
|
+
const value = node[field];
|
|
11063
|
+
if (field === 'Files' || field === 'files') {
|
|
11064
|
+
if (value && typeof value === 'object') entries.push([field, Array.isArray(value) ? value : Object.values(value)]);
|
|
11065
|
+
continue;
|
|
11066
|
+
}
|
|
11067
|
+
if (field === 'Specs' || field === 'specs') {
|
|
11068
|
+
const token = goAstTokenName(node.Tok ?? node.tok);
|
|
11069
|
+
entries.push([field, Array.isArray(value)
|
|
11070
|
+
? value.map((entry) => entry && typeof entry === 'object' ? { parentTok: token, ...entry } : entry)
|
|
11071
|
+
: value]);
|
|
11072
|
+
continue;
|
|
11073
|
+
}
|
|
11074
|
+
entries.push([field, value]);
|
|
11075
|
+
}
|
|
11076
|
+
return entries.filter(([, value]) => Array.isArray(value)
|
|
11077
|
+
? value.some(isGoAstNode)
|
|
11078
|
+
: isGoAstNode(value));
|
|
11079
|
+
}
|
|
11080
|
+
|
|
11081
|
+
function goAstNodeValue(node) {
|
|
11082
|
+
return goAstDeclarationName(node)
|
|
11083
|
+
?? goAstImportPath(node)
|
|
11084
|
+
?? goAstTypeName(node.Type ?? node.type)
|
|
11085
|
+
?? goAstLiteralValue(node);
|
|
11086
|
+
}
|
|
11087
|
+
|
|
11088
|
+
function goAstDeclarationName(node) {
|
|
11089
|
+
if (!node || typeof node !== 'object') return undefined;
|
|
11090
|
+
return goAstIdentName(node.Name ?? node.name)
|
|
11091
|
+
?? goAstIdentName(node.Ident ?? node.ident)
|
|
11092
|
+
?? goAstIdentName(node.Sel ?? node.sel)
|
|
11093
|
+
?? (typeof node.Name === 'string' ? node.Name : undefined)
|
|
11094
|
+
?? (typeof node.name === 'string' ? node.name : undefined);
|
|
11095
|
+
}
|
|
11096
|
+
|
|
11097
|
+
function goAstPackageName(node) {
|
|
11098
|
+
if (!node || typeof node !== 'object') return undefined;
|
|
11099
|
+
return goAstIdentName(node.Name ?? node.name) ?? node.PackageName ?? node.packageName;
|
|
11100
|
+
}
|
|
11101
|
+
|
|
11102
|
+
function goAstIdentName(value) {
|
|
11103
|
+
if (!value) return undefined;
|
|
11104
|
+
if (typeof value === 'string') return value;
|
|
11105
|
+
return value.Name ?? value.name ?? value.Value ?? value.value;
|
|
11106
|
+
}
|
|
11107
|
+
|
|
11108
|
+
function goAstImportPath(node) {
|
|
11109
|
+
if (!node || typeof node !== 'object') return undefined;
|
|
11110
|
+
const path = node.Path ?? node.path;
|
|
11111
|
+
const raw = typeof path === 'string' ? path : path?.Value ?? path?.value ?? path?.Kind;
|
|
11112
|
+
if (typeof raw !== 'string') return undefined;
|
|
11113
|
+
return raw.replace(/^"|"$/g, '').replace(/^`|`$/g, '');
|
|
11114
|
+
}
|
|
11115
|
+
|
|
11116
|
+
function goAstReceiverName(node) {
|
|
11117
|
+
const recv = node?.Recv ?? node?.recv;
|
|
11118
|
+
const list = recv?.List ?? recv?.list;
|
|
11119
|
+
if (!Array.isArray(list) || !list.length) return undefined;
|
|
11120
|
+
const first = list[0];
|
|
11121
|
+
return goAstTypeName(first?.Type ?? first?.type);
|
|
11122
|
+
}
|
|
11123
|
+
|
|
11124
|
+
function goAstValueSpecName(node) {
|
|
11125
|
+
return goAstValueSpecNames(node)[0];
|
|
11126
|
+
}
|
|
11127
|
+
|
|
11128
|
+
function goAstValueSpecNames(node) {
|
|
11129
|
+
const names = node.Names ?? node.names;
|
|
11130
|
+
if (Array.isArray(names)) return names.map(goAstIdentName).filter(Boolean);
|
|
11131
|
+
const name = goAstDeclarationName(node);
|
|
11132
|
+
return name ? [name] : [];
|
|
11133
|
+
}
|
|
11134
|
+
|
|
11135
|
+
function goAstTypeSpecSymbolKind(node) {
|
|
11136
|
+
const type = node.Type ?? node.type;
|
|
11137
|
+
const kind = goAstKind(type);
|
|
11138
|
+
if (kind === 'InterfaceType') return 'interface';
|
|
11139
|
+
if (kind === 'StructType') return 'class';
|
|
11140
|
+
return 'type';
|
|
11141
|
+
}
|
|
11142
|
+
|
|
11143
|
+
function goAstTypeName(value) {
|
|
11144
|
+
if (!value) return undefined;
|
|
11145
|
+
if (typeof value === 'string') return value;
|
|
11146
|
+
const kind = goAstKind(value);
|
|
11147
|
+
if (kind === 'Ident') return goAstIdentName(value);
|
|
11148
|
+
if (kind === 'StarExpr') {
|
|
11149
|
+
const inner = goAstTypeName(value.X ?? value.x);
|
|
11150
|
+
return inner ? `*${inner}` : '*';
|
|
11151
|
+
}
|
|
11152
|
+
if (kind === 'SelectorExpr') {
|
|
11153
|
+
const left = goAstTypeName(value.X ?? value.x);
|
|
11154
|
+
const right = goAstIdentName(value.Sel ?? value.sel);
|
|
11155
|
+
return [left, right].filter(Boolean).join('.');
|
|
11156
|
+
}
|
|
11157
|
+
if (kind === 'ArrayType') {
|
|
11158
|
+
const inner = goAstTypeName(value.Elt ?? value.elt);
|
|
11159
|
+
return `[]${inner ?? 'unknown'}`;
|
|
11160
|
+
}
|
|
11161
|
+
if (kind === 'MapType') {
|
|
11162
|
+
return `map[${goAstTypeName(value.Key ?? value.key) ?? 'unknown'}]${goAstTypeName(value.Value ?? value.value) ?? 'unknown'}`;
|
|
11163
|
+
}
|
|
11164
|
+
if (kind === 'StructType') return 'struct';
|
|
11165
|
+
if (kind === 'InterfaceType') return 'interface';
|
|
11166
|
+
if (kind === 'FuncType') return 'func';
|
|
11167
|
+
return goAstDeclarationName(value);
|
|
11168
|
+
}
|
|
11169
|
+
|
|
11170
|
+
function goAstTokenName(value) {
|
|
11171
|
+
if (!value) return undefined;
|
|
11172
|
+
if (typeof value === 'string') return value;
|
|
11173
|
+
if (typeof value === 'number') return String(value);
|
|
11174
|
+
return value.String ?? value.string ?? value.Name ?? value.name;
|
|
11175
|
+
}
|
|
11176
|
+
|
|
11177
|
+
function goAstLiteralValue(node) {
|
|
11178
|
+
const value = node.Value ?? node.value;
|
|
11179
|
+
if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return value;
|
|
11180
|
+
return undefined;
|
|
11181
|
+
}
|
|
11182
|
+
|
|
11183
|
+
function goGeneratedCodeMarker(node, kind) {
|
|
11184
|
+
if (kind !== 'File') return false;
|
|
11185
|
+
if (node.Generated || node.generated) return true;
|
|
11186
|
+
const comments = node.Comments ?? node.comments;
|
|
11187
|
+
if (!Array.isArray(comments)) return false;
|
|
11188
|
+
return comments.some((group) => JSON.stringify(group).includes('Code generated') && JSON.stringify(group).includes('DO NOT EDIT'));
|
|
11189
|
+
}
|
|
11190
|
+
|
|
11191
|
+
function goBadAstKind(kind) {
|
|
11192
|
+
return kind === 'BadDecl' || kind === 'BadExpr' || kind === 'BadStmt';
|
|
11193
|
+
}
|
|
11194
|
+
|
|
11195
|
+
function goReceiverFieldCount(node) {
|
|
11196
|
+
const list = (node?.Recv ?? node?.recv)?.List ?? (node?.Recv ?? node?.recv)?.list;
|
|
11197
|
+
return Array.isArray(list) ? list.length : 0;
|
|
11198
|
+
}
|
|
11199
|
+
|
|
11200
|
+
function goTypeEvidenceSummary(value) {
|
|
11201
|
+
if (!value || typeof value !== 'object') return undefined;
|
|
11202
|
+
const summary = {};
|
|
11203
|
+
if (typeof value.packagePath === 'string') summary.packagePath = value.packagePath;
|
|
11204
|
+
if (typeof value.hash === 'string') summary.hash = value.hash;
|
|
11205
|
+
if (Array.isArray(value.types)) summary.typeCount = value.types.length;
|
|
11206
|
+
if (Array.isArray(value.references)) summary.referenceCount = value.references.length;
|
|
11207
|
+
return Object.keys(summary).length ? summary : { present: true };
|
|
11208
|
+
}
|
|
11209
|
+
|
|
10660
11210
|
function declarationRecord(input, nativeNodeId, name, symbolKind, role = 'definition') {
|
|
10661
11211
|
return {
|
|
10662
11212
|
name: String(name),
|
package/package.json
CHANGED