From b18cd05438df4a509bd1640de780593eaac21c97 Mon Sep 17 00:00:00 2001 From: Mohit Agarwal Date: Sat, 22 Aug 2026 13:16:07 +0530 Subject: [PATCH] feat(graph): add Go language extractor Add code-graph extraction support for Go using tree-sitter-go grammar (https://github.com/tree-sitter/tree-sitter-go). - New `src/graph/extraction/languages/go.ts` with `GoWalker` extracting: structs (class), interfaces, type aliases, functions, methods, generics, imports, calls, struct instantiations, field types. - New fixture `src/graph/__tests__/fixtures/sample.go` and 11 tests in `extractor-go.test.ts` covering declarations, containment, references, generics, and docstrings. - Register Go in `languages/index.ts` and `grammars.ts` (`.go` extension). - Vendor `tree-sitter-go.wasm` from tree-sitter-wasms@0.1.13. Update docs: `docs/code-graph-support.md` (Go now Supported), `docs/extractors.md` (grammar source documentation). Fix `src/graph/extraction/node-id.ts`: add guard in `getNodeText()` for nodes missing `startIndex`/`endIndex` (tree-sitter-go comment nodes lack byte offsets when accessed via `previousSibling`). Fixes #93 --- .mex/ROUTER.md | 12 + docs/code-graph-support.md | 3 +- docs/extractors.md | 8 + src/graph/__tests__/extractor-go.test.ts | 122 +++++ src/graph/__tests__/fixtures/sample.go | 67 +++ src/graph/extraction/grammars.ts | 2 + src/graph/extraction/languages/go.ts | 574 +++++++++++++++++++++++ src/graph/extraction/languages/index.ts | 2 + src/graph/extraction/node-id.ts | 1 + src/graph/wasm/tree-sitter-go.wasm | Bin 0 -> 235957 bytes 10 files changed, 790 insertions(+), 1 deletion(-) create mode 100644 src/graph/__tests__/extractor-go.test.ts create mode 100644 src/graph/__tests__/fixtures/sample.go create mode 100644 src/graph/extraction/languages/go.ts create mode 100644 src/graph/wasm/tree-sitter-go.wasm diff --git a/.mex/ROUTER.md b/.mex/ROUTER.md index c96f2a1e..9615b197 100644 --- a/.mex/ROUTER.md +++ b/.mex/ROUTER.md @@ -40,6 +40,18 @@ Then read this file fully before doing anything else in this session. **Known issues:** - Pagination breaks on filtered queries with more than 1000 results --> +**Working:** +- Code graph extraction for TypeScript, TSX, JavaScript, JSX, Python, Rust, and Go +- Go language support with tree-sitter-go grammar (structs, interfaces, type aliases, functions, methods, generics, imports, calls, struct fields) +- Cross-file reference resolution and grounding + +**Not yet built:** +- Framework resolvers for Go (e.g., Gin, Echo, Chi) +- Additional language extractors (Java, C#, etc.) + +**Known issues:** +- None for Go extractor in current scope + ## Routing Table Load the relevant file based on the current task. Always load `context/architecture.md` first if not already in context this session. diff --git a/docs/code-graph-support.md b/docs/code-graph-support.md index a98c8b94..2a0264b9 100644 --- a/docs/code-graph-support.md +++ b/docs/code-graph-support.md @@ -26,7 +26,8 @@ extractor registry lives in | **Supported** | JSX | `.jsx` | [`jsx-component.jsx`](../src/graph/__tests__/fixtures/jsx-component.jsx) and [`extraction-regression.test.ts`](../src/graph/__tests__/extraction-regression.test.ts) cover components, imports, calls, and construction. | | **Supported** | Python | `.py` | [`sample.py`](../src/graph/__tests__/fixtures/sample.py), [`extractor-python.test.ts`](../src/graph/__tests__/extractor-python.test.ts), and the [`python-package`](../src/graph/__tests__/fixtures/python-package) integration fixture cover extraction and cross-file package resolution. | | **Supported** | Rust | `.rs` | [`sample.rs`](../src/graph/__tests__/fixtures/sample.rs) and [`extractor-rust.test.ts`](../src/graph/__tests__/extractor-rust.test.ts) cover structs, traits, enums, modules, functions, methods, generics, imports, calls, implementations, construction, returns, and field types. | -| **Unsupported** | Go and other languages | All other extensions | These names may be reserved in [`src/graph/types.ts`](../src/graph/types.ts), but no grammar or extractor is registered for them. Unsupported files are skipped rather than failing a graph build. | +| **Supported** | Go | `.go` | [`sample.go`](../src/graph/__tests__/fixtures/sample.go) and [`extractor-go.test.ts`](../src/graph/__tests__/extractor-go.test.ts) cover structs, interfaces, type aliases, functions, methods, generics, imports, calls, and struct field types. | +| **Unsupported** | Other languages | All other extensions | These names may be reserved in [`src/graph/types.ts`](../src/graph/types.ts), but no grammar or extractor is registered for them. Unsupported files are skipped rather than failing a graph build. | `src/graph/types.ts` contains a wider future-facing language vocabulary. A name in that type union is not a support promise; the grammar and extractor diff --git a/docs/extractors.md b/docs/extractors.md index b9cbfefb..3032bbc3 100644 --- a/docs/extractors.md +++ b/docs/extractors.md @@ -115,6 +115,14 @@ When adding a language, document the grammar source and version. - **Upstream grammar:** [tree-sitter/tree-sitter-rust](https://github.com/tree-sitter/tree-sitter-rust) - **Upstream grammar license:** MIT +### Go +- **Binary source:** `tree-sitter-wasms` package, version `0.1.13` +- **Vendored path:** `src/graph/wasm/tree-sitter-go.wasm` +- **SHA-256:** matches `node_modules/tree-sitter-wasms@0.1.13/out/tree-sitter-go.wasm` exactly +- **Binary package license:** Unlicense +- **Upstream grammar:** [tree-sitter/tree-sitter-go](https://github.com/tree-sitter/tree-sitter-go) +- **Upstream grammar license:** MIT + ## Pull request proof Before opening a pull request, run: diff --git a/src/graph/__tests__/extractor-go.test.ts b/src/graph/__tests__/extractor-go.test.ts new file mode 100644 index 00000000..29120261 --- /dev/null +++ b/src/graph/__tests__/extractor-go.test.ts @@ -0,0 +1,122 @@ +import { readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { beforeAll, describe, expect, it } from "vitest"; +import { extractFile, loadGrammars } from "../extraction/index.js"; +import type { FileExtraction } from "../extraction/index.js"; + +const FIXTURE = join(dirname(fileURLToPath(import.meta.url)), "fixtures", "sample.go"); + +describe("Go extractor", () => { + let result: FileExtraction; + + beforeAll(async () => { + await loadGrammars(["go"]); + const source = readFileSync(FIXTURE, "utf-8"); + result = extractFile("fixtures/sample.go", source, "go")!; + expect(result).not.toBeNull(); + }); + + const node = (kind: string, name: string) => + result.nodes.find((n) => n.kind === kind && n.name === name); + const hasEdge = (kind: string, targetName: string) => + result.edges.some((e) => e.kind === kind && e.targetName === targetName); + + it("emits a file node and stamps the language", () => { + expect(result.language).toBe("go"); + expect(node("file", "sample.go")).toBeDefined(); + }); + + it("extracts structs", () => { + const user = node("class", "User"); + expect(user).toBeDefined(); + expect(user!.isExported).toBe(true); + expect(user!.docstring).toContain("represents a user"); + + expect(node("property", "Name")).toBeDefined(); + expect(node("property", "Age")).toBeDefined(); + + const order = node("class", "Order"); + expect(order).toBeDefined(); + expect(node("property", "ID")).toBeDefined(); + expect(node("property", "Items")).toBeDefined(); + }); + + it("extracts type aliases", () => { + expect(node("type_alias", "Role")).toBeDefined(); + }); + + it("extracts functions and methods", () => { + const createUser = node("function", "CreateUser"); + expect(createUser).toBeDefined(); + expect(createUser!.isExported).toBe(true); + + const greet = result.nodes.find( + (n) => n.kind === "method" && n.qualifiedName === "User::Greet", + ); + expect(greet).toBeDefined(); + expect(greet!.qualifiedName).toBe("User::Greet"); + }); + + it("extracts interfaces", () => { + const greeter = node("interface", "Greeter"); + expect(greeter).toBeDefined(); + expect(greeter!.isExported).toBe(true); + + const repo = node("interface", "Repo"); + expect(repo).toBeDefined(); + expect(repo!.typeParameters).toEqual(["T"]); + }); + + it("extracts constants and variables", () => { + expect(node("constant", "RoleAdmin")).toBeDefined(); + expect(node("constant", "RoleMember")).toBeDefined(); + expect(node("variable", "globalFlag")).toBeDefined(); + }); + + it("emits import edges", () => { + expect(hasEdge("imports", "fmt")).toBe(true); + expect(hasEdge("imports", "strings")).toBe(true); + }); + + it("emits calls and implements references", () => { + expect(hasEdge("calls", "fmt.Sprintf")).toBe(true); + expect(hasEdge("calls", "processOrder")).toBe(true); + expect(hasEdge("calls", "consume")).toBe(true); + expect(hasEdge("instantiates", "Box")).toBe(true); + expect(hasEdge("instantiates", "Order")).toBe(true); + expect(hasEdge("instantiates", "User")).toBe(true); + }); + + it("nests methods under their class via contains edges", () => { + const userClass = node("class", "User")!; + const greet = result.nodes.find( + (n) => n.kind === "method" && n.qualifiedName === "User::Greet", + )!; + expect( + result.edges.some( + (e) => e.kind === "contains" && e.source === userClass.id && e.target === greet.id, + ), + ).toBe(true); + }); + + it("emits `type_of` edge for struct fields", () => { + const nameField = result.nodes.find( + (n) => n.kind === "property" && n.name === "Name", + )!; + expect(nameField).toBeDefined(); + expect( + result.edges.some( + (e) => e.kind === "type_of" && e.source === nameField.id && e.targetName === "string", + ), + ).toBe(true); + }); + + it("handles generic type parameters", () => { + const box = node("class", "Box")!; + expect(box.typeParameters).toEqual(["T"]); + + const makeBox = node("function", "makeBox")!; + expect(makeBox.typeParameters).toEqual(["T"]); + }); +}); \ No newline at end of file diff --git a/src/graph/__tests__/fixtures/sample.go b/src/graph/__tests__/fixtures/sample.go new file mode 100644 index 00000000..dd10e90e --- /dev/null +++ b/src/graph/__tests__/fixtures/sample.go @@ -0,0 +1,67 @@ +// Package main is a sample Go file for testing the code-graph extractor. + +package main + +import ( + "fmt" + "strings" +) + +// User represents a user in the system. +type User struct { + Name string + Age int +} + +type Role string + +const ( + RoleAdmin Role = "admin" + RoleMember Role = "member" +) + +var globalFlag = true + +// Greeter is an interface for greeting. +type Greeter interface { + Greet() string +} + +func (u *User) Greet() string { + return fmt.Sprintf("Hello, my name is %s", u.Name) +} + +func processOrder(order *Order) { + fmt.Println("Processing order:", order.ID) +} + +type Order struct { + ID int + Items []string +} + +func (o *Order) AddItem(item string) { + o.Items = append(o.Items, item) +} + +type Box[T any] struct { + Value T +} + +func makeBox[T any](val T) Box[T] { + return Box[T]{Value: val} +} + +func consume(val any) {} + +func CreateUser(name string) *User { + u := &User{Name: name, Age: 30} + processOrder(&Order{ID: 1}) + u.Greet() + consume(makeBox(42)) + return u +} + +type Repo[T any] interface { + Get() T +} diff --git a/src/graph/extraction/grammars.ts b/src/graph/extraction/grammars.ts index c6477b51..135c4152 100644 --- a/src/graph/extraction/grammars.ts +++ b/src/graph/extraction/grammars.ts @@ -30,6 +30,7 @@ const WASM_GRAMMAR_FILES: Partial> = { jsx: "tree-sitter-javascript.wasm", python: "tree-sitter-python.wasm", rust: "tree-sitter-rust.wasm", + go: "tree-sitter-go.wasm", }; let grammarHashCache: string | null = null; @@ -63,6 +64,7 @@ const EXTENSION_MAP: Record = { ".jsx": "jsx", ".py": "python", ".rs": "rust", + ".go": "go", }; /** Glob pattern for every extension registered above. */ diff --git a/src/graph/extraction/languages/go.ts b/src/graph/extraction/languages/go.ts new file mode 100644 index 00000000..13b6d44c --- /dev/null +++ b/src/graph/extraction/languages/go.ts @@ -0,0 +1,574 @@ +import type { Language, NodeKind } from "../../types.js"; +import type { + ExtractedEdge, + ExtractedNode, + LanguageExtractor, + TSNode, + TSTree, +} from "../types.js"; +import { + canonicalNodeIdentity, + generateNodeId, + getChildByField, + getNodeText, +} from "../node-id.js"; + +const FUNCTION_TYPES = new Set(["function_declaration"]); +const METHOD_TYPES = new Set(["method_declaration"]); +const STRUCT_TYPES = new Set(["struct_type"]); +const INTERFACE_TYPES = new Set(["interface_type"]); +const TYPE_DECL_TYPES = new Set(["type_declaration", "type_spec", "type_alias"]); +const CONST_TYPES = new Set(["const_declaration"]); +const VAR_TYPES = new Set(["var_declaration", "var_spec", "var_spec_list"]); +const CALL_TYPES = new Set(["call_expression"]); +const IMPORT_TYPES = new Set(["import_declaration"]); +const COMPOSITE_LITERAL_TYPES = new Set(["composite_literal"]); + +function getGoDocstring(node: TSNode, source: string): string | undefined { + const docs: string[] = []; + let cur: TSNode | null = node.previousSibling; + while (cur && cur.type === "comment") { + const text = getNodeText(cur, source).trim(); + if (text.startsWith("//")) { + docs.unshift(text.slice(2).trim()); + } + cur = cur.previousSibling; + } + if (docs.length === 0 && node.parent) { + cur = node.parent.previousSibling; + while (cur && cur.type === "comment") { + const text = getNodeText(cur, source).trim(); + if (text.startsWith("//")) { + docs.unshift(text.slice(2).trim()); + } + cur = cur.previousSibling; + } + } + return docs.length > 0 ? docs.join("\n") : undefined; +} + +function isExported(name: string): boolean { + return name.length > 0 && name[0] === name[0].toUpperCase(); +} + +function visibilityOf(node: TSNode, source: string): ExtractedNode["visibility"] { + const name = nameOf(node, source); + return name && isExported(name) ? "public" : "private"; +} + +class GoWalker { + private readonly nodes: ExtractedNode[] = []; + private readonly edges: ExtractedEdge[] = []; + private scopeStack: string[] = []; + private readonly identityOccurrences = new Map(); + + constructor( + private readonly filePath: string, + private readonly source: string, + private readonly language: Language, + ) {} + + run(root: TSNode): { nodes: ExtractedNode[]; edges: ExtractedEdge[] } { + const fileName = baseName(this.filePath); + const fileId = generateNodeId(this.filePath, "file", fileName, this.filePath, "source-file"); + this.nodes.push({ + id: fileId, + identityKey: canonicalNodeIdentity(this.filePath, "file", this.filePath, "source-file"), + kind: "file", + name: fileName, + qualifiedName: this.filePath, + filePath: this.filePath, + language: this.language, + startLine: 1, + endLine: root.endPosition.row + 1, + startColumn: 0, + endColumn: 0, + isExported: false, + }); + + this.scopeStack.push(fileId); + + for (const child of root.namedChildren) { + this.visit(child); + } + + this.scopeStack.pop(); + + return { nodes: this.nodes, edges: this.edges }; + } + + private visit(node: TSNode): void { + const type = node.type; + + if (type === "package_clause") return this.extractPackage(node); + if (FUNCTION_TYPES.has(type)) return this.extractFunction(node); + if (METHOD_TYPES.has(type)) return this.extractMethod(node); + if (TYPE_DECL_TYPES.has(type)) return this.extractType(node); + if (CONST_TYPES.has(type)) return this.extractConst(node); + if (VAR_TYPES.has(type)) return this.extractVar(node); + if (IMPORT_TYPES.has(type)) return this.extractImport(node); + + for (const child of node.namedChildren) this.visit(child); + } + + private createNode( + kind: NodeKind, + name: string, + node: TSNode, + extra?: Partial, + ): string | null { + if (!name) return null; + const qualifiedName = this.qualify(name); + const baseIdentity = canonicalNodeIdentity(this.filePath, kind, qualifiedName, kind, extra?.signature); + const ordinal = this.identityOccurrences.get(baseIdentity) ?? 0; + this.identityOccurrences.set(baseIdentity, ordinal + 1); + const declarationRole = ordinal === 0 ? kind : `${kind}:ordinal:${ordinal}`; + const identityKey = canonicalNodeIdentity(this.filePath, kind, qualifiedName, declarationRole, extra?.signature); + const id = generateNodeId(this.filePath, kind, name, qualifiedName, declarationRole, extra?.signature); + const newNode = { + id, + identityKey, + kind, + name, + qualifiedName, + filePath: this.filePath, + language: this.language, + startLine: node.startPosition.row + 1, + endLine: node.endPosition.row + 1, + startColumn: node.startPosition.column, + endColumn: node.endPosition.column, + docstring: getGoDocstring(node, this.source), + isExported: isExported(name), + ...extra, + }; + this.nodes.push(newNode); + + const parent = this.scopeStack[this.scopeStack.length - 1]; + if (parent) { + this.edges.push({ source: parent, target: id, kind: "contains" }); + } + return id; + } + + private qualify(name: string): string { + const parts: string[] = []; + for (const scopeId of this.scopeStack) { + const scope = this.nodes.find((n) => n.id === scopeId); + if (scope && scope.kind !== "file") parts.push(scope.name); + } + parts.push(name); + return parts.join("::"); + } + + private extractPackage(node: TSNode): void { + const pkgNode = getChildByField(node, "name"); + if (pkgNode) { + const pkgName = getNodeText(pkgNode, this.source); + this.edges.push({ + source: this.scopeStack[0], + targetName: pkgName, + kind: "imports", + line: node.startPosition.row, + column: node.startPosition.column, + }); + } + } + + private extractFunction(node: TSNode): void { + const name = nameOf(node, this.source); + if (!name) return; + + const id = this.createNode("function", name, node, { + signature: signatureOf(node, this.source), + returnType: returnTypeOf(node, this.source), + typeParameters: typeParametersOf(node, this.source), + }); + if (!id) return; + + const body = getChildByField(node, "body"); + if (body) this.walkBody(body, id); + } + + private extractMethod(node: TSNode): void { + const name = nameOf(node, this.source); + if (!name) return; + + const receiver = getChildByField(node, "receiver"); + let receiverType = ""; + if (receiver) { + for (const param of receiver.namedChildren) { + if (param.type === "parameter_declaration") { + const typeNode = getChildByField(param, "type"); + if (typeNode) { + receiverType = baseTypeName(typeNode, this.source); + break; + } + } + } + } + + // Determine qualified name based on receiver type + let qualifiedName = name; + if (receiverType) { + qualifiedName = `${receiverType}::${name}`; + } + + const id = this.createNode("method", name, node, { + signature: signatureOf(node, this.source), + returnType: returnTypeOf(node, this.source), + visibility: visibilityOf(node, this.source), + typeParameters: typeParametersOf(node, this.source), + }); + if (!id) return; + + // Override the qualifiedName if we have a receiver type + if (receiverType) { + const methodNode = this.nodes.find(n => n.id === id); + if (methodNode) { + methodNode.qualifiedName = qualifiedName; + } + } + + if (receiverType) { + this.addRef(id, receiverType, "receiver", node); + // Also create a contains edge from the receiver type to this method + const receiverNode = this.nodes.find(n => n.name === receiverType && (n.kind === "class" || n.kind === "interface")); + if (receiverNode) { + this.edges.push({ source: receiverNode.id, target: id, kind: "contains" }); + } + } + + const body = getChildByField(node, "body"); + if (body) this.walkBody(body, id); + } + + private extractType(node: TSNode): void { + if (node.type === "type_alias") { + const nameNode = getChildByField(node, "name"); + if (nameNode) { + const name = getNodeText(nameNode, this.source); + this.createNode("type_alias", name, node); + } + return; + } + + if (node.type === "type_spec" || node.type === "type_declaration") { + for (const child of node.namedChildren) { + if (child.type === "type_spec") { + this.extractTypeSpec(child); + } + } + return; + } + } + + private extractTypeSpec(node: TSNode): void { + const nameNode = getChildByField(node, "name"); + const typeNode = getChildByField(node, "type"); + if (!nameNode || !typeNode) return; + + const name = getNodeText(nameNode, this.source); + const typeKind = typeNode.type; + + if (typeKind === "struct_type") { + const id = this.createNode("class", name, node, { + visibility: visibilityOf(node, this.source), + typeParameters: typeParametersOf(node, this.source), + }); + if (!id) return; + + this.extractStructBody(typeNode, id); + } else if (typeKind === "interface_type") { + const id = this.createNode("interface", name, node, { + visibility: visibilityOf(node, this.source), + typeParameters: typeParametersOf(node, this.source), + }); + if (!id) return; + + this.extractInterfaceBody(typeNode, id); + } else { + this.createNode("type_alias", name, node, { + signature: getNodeText(typeNode, this.source).slice(0, 200), + }); + } + } + + private extractStructBody(structNode: TSNode, ownerId: string): void { + // struct_type has field_declaration_list as a direct named child, not a "body" field + const fieldList = structNode.namedChildren.find(c => c.type === "field_declaration_list"); + if (!fieldList) return; + + this.scopeStack.push(ownerId); + for (const member of fieldList.namedChildren) { + if (member.type === "field_declaration") { + const names = member.namedChildren.filter(c => c.type === "field_identifier"); + const typeNode = getChildByField(member, "type"); + for (const nameNode of names) { + const fieldName = getNodeText(nameNode, this.source); + const id = this.createNode("property", fieldName, member, { + visibility: visibilityOf(member, this.source), + }); + if (id && typeNode) { + const fieldTypeName = baseTypeName(typeNode, this.source); + if (fieldTypeName) { + this.addRef(id, fieldTypeName, "type_of", member); + } + } + } + } + } + this.scopeStack.pop(); + } + + private extractInterfaceBody(interfaceNode: TSNode, ownerId: string): void { + this.scopeStack.push(ownerId); + for (const member of interfaceNode.namedChildren) { + if (member.type === "method_elem" || member.type === "method_spec") { + const nameNode = getChildByField(member, "name"); + if (nameNode) { + const name = getNodeText(nameNode, this.source); + this.createNode("method", name, member, { + signature: signatureOf(member, this.source), + returnType: returnTypeOf(member, this.source), + }); + } + } else if (member.type === "type_elem") { + for (const typeChild of member.namedChildren) { + const typeName = baseTypeName(typeChild, this.source); + if (typeName) { + this.addRef(ownerId, typeName, "implements", member); + } + } + } + } + this.scopeStack.pop(); + } + + private extractConst(node: TSNode): void { + for (const spec of node.namedChildren) { + if (spec.type === "const_spec") { + for (const nameNode of spec.namedChildren) { + if (nameNode.type === "identifier") { + const name = getNodeText(nameNode, this.source); + const valueNode = getChildByField(spec, "value"); + this.createNode("constant", name, spec, { + signature: valueNode ? getNodeText(valueNode, this.source).slice(0, 200) : undefined, + visibility: visibilityOf(spec, this.source), + }); + } + } + } + } + } + + private extractVar(node: TSNode): void { + if (node.type === "var_spec_list") { + for (const spec of node.namedChildren) { + if (spec.type === "var_spec") this.extractVarSpec(spec); + } + return; + } + if (node.type === "var_spec") return this.extractVarSpec(node); + if (node.type === "var_declaration") { + for (const spec of node.namedChildren) { + if (spec.type === "var_spec") this.extractVarSpec(spec); + } + } + } + + private extractVarSpec(node: TSNode): void { + const left = getChildByField(node, "left") ?? node.namedChild(0); + if (!left) return; + + let names: TSNode[] = []; + if (left.type === "identifier") { + names = [left]; + } else if (left.type === "expression_list") { + names = left.namedChildren.filter(c => c.type === "identifier"); + } + + const right = getChildByField(node, "right"); + const typeNode = getChildByField(node, "type"); + + for (const nameNode of names) { + const name = getNodeText(nameNode, this.source); + const isConst = false; + this.createNode(isConst ? "constant" : "variable", name, node, { + signature: right ? getNodeText(right, this.source).slice(0, 200) : (typeNode ? getNodeText(typeNode, this.source) : undefined), + visibility: visibilityOf(node, this.source), + }); + } + } + + private extractImport(node: TSNode): void { + const fileId = this.scopeStack[0]; + if (!fileId) return; + + for (const spec of node.namedChildren) { + if (spec.type === "import_spec" || spec.type === "import_spec_list") { + if (spec.type === "import_spec_list") { + for (const inner of spec.namedChildren) { + if (inner.type === "import_spec") this.processImportSpec(fileId, inner); + } + } else { + this.processImportSpec(fileId, spec); + } + } + } + } + + private processImportSpec(fileId: string, spec: TSNode): void { + const pathNode = getChildByField(spec, "path"); + if (!pathNode) return; + const moduleSpecifier = getNodeText(pathNode, this.source).replace(/^"|"$/g, ''); + const nameNode = getChildByField(spec, "name"); + let localName = ""; + if (nameNode) { + localName = getNodeText(nameNode, this.source); + } + this.addRef(fileId, moduleSpecifier, "imports", spec, { + bindings: [{ localName: localName || moduleSpecifier.split("/").pop() || moduleSpecifier, importedName: "*" }], + }); + } + + private walkBody(body: TSNode, ownerId: string): void { + const type = body.type; + + if (CALL_TYPES.has(type)) { + this.extractCall(body, ownerId); + } else if (COMPOSITE_LITERAL_TYPES.has(type)) { + this.extractCompositeLiteral(body, ownerId); + } else if (FUNCTION_TYPES.has(type) || METHOD_TYPES.has(type)) { + this.scopeStack.push(ownerId); + this.visit(body); + this.scopeStack.pop(); + return; + } + + for (const child of body.namedChildren) this.walkBody(child, ownerId); + } + + private extractCall(node: TSNode, ownerId: string): void { + const fn = getChildByField(node, "function"); + let calleeName = ""; + if (fn) { + if (fn.type === "selector_expression") { + const field = getChildByField(fn, "field"); + const operand = getChildByField(fn, "operand"); + const methodName = field ? getNodeText(field, this.source) : ""; + const pkgName = operand ? getNodeText(operand, this.source) : ""; + calleeName = pkgName && methodName ? `${pkgName}.${methodName}` : methodName; + } else if (fn.type === "identifier") { + calleeName = getNodeText(fn, this.source); + } else { + calleeName = getNodeText(fn, this.source); + } + } + if (calleeName) this.addRef(ownerId, calleeName, "calls", node); + + const args = getChildByField(node, "arguments"); + if (args) for (const child of args.namedChildren) this.walkBody(child, ownerId); + } + + private extractCompositeLiteral(node: TSNode, ownerId: string): void { + const typeNode = getChildByField(node, "type"); + if (typeNode) { + const structName = baseTypeName(typeNode, this.source); + if (structName) this.addRef(ownerId, structName, "instantiates", node); + } + const body = getChildByField(node, "body"); + if (body) { + for (const child of body.namedChildren) this.walkBody(child, ownerId); + } + } + + private addRef( + source: string, + targetName: string, + kind: ExtractedEdge["kind"], + node: TSNode, + metadata?: Record, + ): void { + if (!targetName) return; + this.edges.push({ + source, + targetName, + kind, + line: node.startPosition.row, + column: node.startPosition.column, + metadata, + }); + } +} + +function nameOf(node: TSNode, source: string): string { + const nameNode = getChildByField(node, "name"); + return nameNode ? getNodeText(nameNode, source) : ""; +} + +function signatureOf(node: TSNode, source: string): string | undefined { + const params = getChildByField(node, "parameters"); + if (!params) return undefined; + let sig = getNodeText(params, source); + const ret = getChildByField(node, "result"); + if (ret) sig += " " + getNodeText(ret, source); + return sig; +} + +function returnTypeOf(node: TSNode, source: string): string | undefined { + const ret = getChildByField(node, "result"); + if (!ret) return undefined; + return getNodeText(ret, source); +} + +function typeParametersOf(node: TSNode, source: string): string[] | undefined { + const typeParams = getChildByField(node, "type_parameters"); + if (!typeParams) return undefined; + const params: string[] = []; + for (let i = 0; i < typeParams.namedChildCount; i++) { + const child = typeParams.namedChild(i); + if (child && child.type === "parameter_declaration") { + const nameNode = getChildByField(child, "name"); + if (nameNode) { + params.push(getNodeText(nameNode, source)); + } + } else if (child) { + params.push(getNodeText(child, source)); + } + } + return params.length > 0 ? params : undefined; +} + +function baseTypeName(node: TSNode, source: string): string | undefined { + if (node.type === "generic_type") { + const base = node.namedChild(0); + return base ? baseTypeName(base, source) : undefined; + } + if (node.type === "qualified_type") { + const name = getChildByField(node, "name"); + return name ? getNodeText(name, source) : undefined; + } + if (node.type === "pointer_type") { + for (let i = node.namedChildCount - 1; i >= 0; i--) { + const child = node.namedChild(i); + if (child) return baseTypeName(child, source); + } + return undefined; + } + return getNodeText(node, source); +} + +function baseName(filePath: string): string { + const normalized = filePath.replace(/\\/g, "/"); + const slash = normalized.lastIndexOf("/"); + return slash < 0 ? normalized : normalized.slice(slash + 1); +} + +export const goExtractor: LanguageExtractor = { + language: "go", + fileExtensions: [".go"], + grammarWasm: "tree-sitter-go.wasm", + extract(tree: TSTree, filePath: string, source: string) { + return new GoWalker(filePath, source, "go").run(tree.rootNode); + }, +}; \ No newline at end of file diff --git a/src/graph/extraction/languages/index.ts b/src/graph/extraction/languages/index.ts index de5e34ad..4529881e 100644 --- a/src/graph/extraction/languages/index.ts +++ b/src/graph/extraction/languages/index.ts @@ -12,6 +12,7 @@ import { typescriptExtractor, tsxExtractor } from "./typescript.js"; import { javascriptExtractor, jsxExtractor } from "./javascript.js"; import { pythonExtractor } from "./python.js"; import { rustExtractor } from "./rust.js"; +import { goExtractor } from "./go.js"; /** Registered extractors, keyed by the language id they emit. */ export const EXTRACTORS: Partial> = { @@ -21,6 +22,7 @@ export const EXTRACTORS: Partial> = { jsx: jsxExtractor, python: pythonExtractor, rust: rustExtractor, + go: goExtractor, }; /** The extractor for a language, or undefined if unsupported in this release. */ diff --git a/src/graph/extraction/node-id.ts b/src/graph/extraction/node-id.ts index 8e944c80..960c2c0e 100644 --- a/src/graph/extraction/node-id.ts +++ b/src/graph/extraction/node-id.ts @@ -59,6 +59,7 @@ export function canonicalNodeIdentity( /** Source text spanned by a syntax node (its byte range into `source`). */ export function getNodeText(node: TSNode, source: string): string { + if (!node || typeof node.startIndex !== "number" || typeof node.endIndex !== "number") return ""; return source.substring(node.startIndex, node.endIndex); } diff --git a/src/graph/wasm/tree-sitter-go.wasm b/src/graph/wasm/tree-sitter-go.wasm new file mode 100644 index 0000000000000000000000000000000000000000..a20aba8a91a55746d084e8dafc767f60260f3b4e GIT binary patch literal 235957 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