@ctxo/lang-go 0.7.0 → 0.8.0-alpha.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/README.md +70 -0
- package/dist/index.d.ts +69 -2
- package/dist/index.js +483 -11
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
- package/tools/ctxo-go-analyzer/go.mod +10 -0
- package/tools/ctxo-go-analyzer/go.sum +8 -0
- package/tools/ctxo-go-analyzer/internal/edges/edges.go +303 -0
- package/tools/ctxo-go-analyzer/internal/edges/edges_test.go +180 -0
- package/tools/ctxo-go-analyzer/internal/emit/emit.go +189 -0
- package/tools/ctxo-go-analyzer/internal/emit/emit_test.go +84 -0
- package/tools/ctxo-go-analyzer/internal/extends/extends.go +138 -0
- package/tools/ctxo-go-analyzer/internal/extends/extends_test.go +141 -0
- package/tools/ctxo-go-analyzer/internal/implements/implements.go +115 -0
- package/tools/ctxo-go-analyzer/internal/implements/implements_test.go +141 -0
- package/tools/ctxo-go-analyzer/internal/load/load.go +67 -0
- package/tools/ctxo-go-analyzer/internal/reach/reach.go +332 -0
- package/tools/ctxo-go-analyzer/internal/reach/reach_test.go +142 -0
- package/tools/ctxo-go-analyzer/internal/symbols/symbols.go +172 -0
- package/tools/ctxo-go-analyzer/internal/symbols/symbols_test.go +159 -0
- package/tools/ctxo-go-analyzer/main.go +180 -0
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# @ctxo/lang-go
|
|
2
|
+
|
|
3
|
+
Go language plugin for [Ctxo](https://github.com/alperhankendi/Ctxo). Ships
|
|
4
|
+
two analysis tiers selected at runtime:
|
|
5
|
+
|
|
6
|
+
| Tier | Engine | Activated when |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| **Full** | `ctxo-go-analyzer` binary (Go stdlib + `x/tools` SSA + CHA) | Go ≥ 1.22 on PATH **and** the bundled analyzer source builds successfully |
|
|
9
|
+
| **Syntax** | `tree-sitter-go` | Go toolchain absent; binary build fails; or analyzer cannot locate `go.mod` / `go.work` |
|
|
10
|
+
|
|
11
|
+
The composite picks at `initialize()` and forwards every `extractSymbols` /
|
|
12
|
+
`extractEdges` call to the active layer. `extractComplexity` is always served
|
|
13
|
+
by tree-sitter — the analyzer intentionally emits empty complexity arrays so
|
|
14
|
+
the two layers compose cleanly.
|
|
15
|
+
|
|
16
|
+
## What full-tier adds over tree-sitter
|
|
17
|
+
|
|
18
|
+
- **Cross-package symbol-id resolution.** `pkg/a` calling `pkg/b.Do()` produces
|
|
19
|
+
an edge whose target is `pkg/b/file.go::Do::function`, not a synthetic
|
|
20
|
+
placeholder. Every `get_blast_radius`, `find_importers`, and `get_logic_slice`
|
|
21
|
+
query that crosses a package boundary now returns real results.
|
|
22
|
+
- **`implements` edges.** Go's structural typing means interface satisfaction
|
|
23
|
+
is invisible to a syntactic parser; the analyzer pairs every concrete type
|
|
24
|
+
against every interface and emits an edge when `types.Implements(T, I)` or
|
|
25
|
+
`types.Implements(*T, I)` is true.
|
|
26
|
+
- **`extends` edges for embedding.** Anonymous fields (struct embedding) and
|
|
27
|
+
embedded interface members produce explicit hierarchy edges so
|
|
28
|
+
`get_class_hierarchy` works on Go.
|
|
29
|
+
- **Generics with `typeArgs` metadata.** `List[int]` and `List[string]` both
|
|
30
|
+
produce a `uses` edge to the unconstructed `List` symbol; the type
|
|
31
|
+
arguments are preserved on edge metadata for future query precision.
|
|
32
|
+
See [ADR-013 §4 Q4](../../docs/architecture/ADR/adr-013-go-full-tier-via-ctxo-go-analyzer-binary.md#open-question-decisions-resolved-2026-04-13).
|
|
33
|
+
- **Dead-code detection via CHA reachability.** Unreachable functions and
|
|
34
|
+
methods are emitted in a single `dead` record. A reflect-safe pass marks
|
|
35
|
+
methods of any type touched by `reflect.TypeOf` / `reflect.ValueOf` /
|
|
36
|
+
`reflect.New` or `json.Marshal` / `Unmarshal` / `NewDecoder` / `NewEncoder`
|
|
37
|
+
as live to prevent false positives.
|
|
38
|
+
|
|
39
|
+
## How the binary is built
|
|
40
|
+
|
|
41
|
+
On first use, `GoAnalyzerAdapter.initialize()` hashes the analyzer source +
|
|
42
|
+
`go version` and runs `go build -trimpath -o
|
|
43
|
+
~/.cache/ctxo/lang-go-analyzer/<hash-goVersion>/ctxo-go-analyzer[.exe]`.
|
|
44
|
+
Subsequent runs reuse the cached binary. No build happens during `npm install`
|
|
45
|
+
to keep package installs sandbox-safe (ADR-013 §4 Q2).
|
|
46
|
+
|
|
47
|
+
If `go` is not on PATH, build fails, or no `go.mod`/`go.work` is found, the
|
|
48
|
+
plugin transparently falls back to tree-sitter and `ctxo doctor` surfaces the
|
|
49
|
+
missing toolchain.
|
|
50
|
+
|
|
51
|
+
## Limitations (v0.8.0-alpha.0)
|
|
52
|
+
|
|
53
|
+
- **Library mode is approximate.** Without a `main.main` to anchor RTA-style
|
|
54
|
+
reachability, every exported function/method becomes a root. This
|
|
55
|
+
over-approximates liveness — fewer false dead-code reports, but symbols
|
|
56
|
+
that nothing actually consumes still look live.
|
|
57
|
+
- **Generic types skipped from `implements` / `extends` enumeration.** Pairing
|
|
58
|
+
generic named types is unstable in current `go/types`; we revisit when the
|
|
59
|
+
upstream API stabilizes.
|
|
60
|
+
- **Closures and reflective construction.** Functions passed to stdlib
|
|
61
|
+
callbacks (`sort.Slice(..., func(i,j) bool {...})`) follow a CHA edge so
|
|
62
|
+
they ARE reachable; constructed-via-reflection types are kept alive only
|
|
63
|
+
for the explicit reflect/json call sites listed above.
|
|
64
|
+
|
|
65
|
+
## Architecture and rationale
|
|
66
|
+
|
|
67
|
+
See [ADR-013](../../docs/architecture/ADR/adr-013-go-full-tier-via-ctxo-go-analyzer-binary.md)
|
|
68
|
+
for the full decision record (alternative options considered, library
|
|
69
|
+
choices, open question resolutions). Mirrors the C# pattern from
|
|
70
|
+
[ADR-007](../../docs/architecture/ADR/adr-007-csharp-roslyn-lsp.md).
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ILanguageAdapter, SymbolKind, SymbolNode, GraphEdge, ComplexityMetrics, CtxoLanguagePlugin } from '@ctxo/plugin-api';
|
|
2
2
|
import Parser, { Tree, SyntaxNode } from 'tree-sitter';
|
|
3
3
|
|
|
4
|
-
type Language = Parameters<InstanceType<typeof Parser>['setLanguage']>[0];
|
|
4
|
+
type Language = Parameters<InstanceType<typeof Parser>['setLanguage']>[0] | object;
|
|
5
5
|
|
|
6
6
|
declare abstract class TreeSitterAdapter implements ILanguageAdapter {
|
|
7
7
|
abstract readonly extensions: readonly string[];
|
|
@@ -38,6 +38,73 @@ declare class GoAdapter extends TreeSitterAdapter {
|
|
|
38
38
|
private findFirstExportedSymbolId;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Full-tier Go adapter. Owns the analyzer binary lifecycle and an
|
|
43
|
+
* in-memory cache of batch results keyed by project-relative file path.
|
|
44
|
+
*
|
|
45
|
+
* Caching strategy: the first extractSymbols/extractEdges call after
|
|
46
|
+
* initialize() triggers a single batch analysis run. Subsequent calls
|
|
47
|
+
* read from cache. This keeps the ILanguageAdapter contract intact
|
|
48
|
+
* (per-file methods) while the binary runs once per index.
|
|
49
|
+
*/
|
|
50
|
+
declare class GoAnalyzerAdapter implements ILanguageAdapter {
|
|
51
|
+
readonly extensions: readonly [".go"];
|
|
52
|
+
readonly tier: "full";
|
|
53
|
+
private moduleRoot;
|
|
54
|
+
private binaryPath;
|
|
55
|
+
private modulePathPrefix;
|
|
56
|
+
private cache;
|
|
57
|
+
private deadSymbolIds;
|
|
58
|
+
private hasMain;
|
|
59
|
+
private timedOut;
|
|
60
|
+
private batchPromise;
|
|
61
|
+
private initialized;
|
|
62
|
+
isSupported(filePath: string): boolean;
|
|
63
|
+
isReady(): boolean;
|
|
64
|
+
initialize(rootDir: string): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Returns the dead-symbol set emitted by the last batch run. Empty until
|
|
67
|
+
* extractSymbols/Edges has been called at least once. Consumed by cli's
|
|
68
|
+
* find_dead_code MCP tool via the composite delegate.
|
|
69
|
+
*/
|
|
70
|
+
getDeadSymbolIds(): Set<string>;
|
|
71
|
+
/** True when the analyzer ran in binary mode (precise dead-code). */
|
|
72
|
+
reachabilityHasMain(): boolean;
|
|
73
|
+
/** True when reach analysis exceeded the deadline (degraded precision). */
|
|
74
|
+
reachabilityTimedOut(): boolean;
|
|
75
|
+
extractSymbols(filePath: string, _source: string): Promise<SymbolNode[]>;
|
|
76
|
+
extractEdges(filePath: string, _source: string): Promise<GraphEdge[]>;
|
|
77
|
+
extractComplexity(_filePath: string, _source: string): Promise<ComplexityMetrics[]>;
|
|
78
|
+
dispose(): Promise<void>;
|
|
79
|
+
private ensureBatch;
|
|
80
|
+
private runBatch;
|
|
81
|
+
private absorbBatch;
|
|
82
|
+
private normalizePath;
|
|
83
|
+
private buildPathRewriter;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Picks between the full-tier ctxo-go-analyzer and the syntax-tier
|
|
88
|
+
* tree-sitter adapter at initialize() time. Symbols and edges come from
|
|
89
|
+
* whichever delegate is active; complexity is ALWAYS sourced from
|
|
90
|
+
* tree-sitter because the binary intentionally emits empty complexity.
|
|
91
|
+
*/
|
|
92
|
+
declare class GoCompositeAdapter implements ILanguageAdapter {
|
|
93
|
+
private analyzer;
|
|
94
|
+
private treeSitter;
|
|
95
|
+
constructor();
|
|
96
|
+
initialize(rootDir: string): Promise<void>;
|
|
97
|
+
dispose(): Promise<void>;
|
|
98
|
+
extractSymbols(filePath: string, source: string): Promise<SymbolNode[]>;
|
|
99
|
+
extractEdges(filePath: string, source: string): Promise<GraphEdge[]>;
|
|
100
|
+
extractComplexity(filePath: string, source: string): Promise<ComplexityMetrics[]>;
|
|
101
|
+
isSupported(filePath: string): boolean;
|
|
102
|
+
setSymbolRegistry(registry: Map<string, SymbolKind>): void;
|
|
103
|
+
/** Exposed for cli optimizations. Null when running in syntax tier. */
|
|
104
|
+
getAnalyzerDelegate(): GoAnalyzerAdapter | null;
|
|
105
|
+
getTier(): 'full' | 'syntax' | 'unavailable';
|
|
106
|
+
}
|
|
107
|
+
|
|
41
108
|
declare const plugin: CtxoLanguagePlugin;
|
|
42
109
|
|
|
43
|
-
export { GoAdapter, TreeSitterAdapter, plugin as default, plugin };
|
|
110
|
+
export { GoAdapter, GoAnalyzerAdapter, GoCompositeAdapter, TreeSitterAdapter, plugin as default, plugin };
|