graphitect 0.2.0__py3-none-any.whl
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.
- graphify/__init__.py +30 -0
- graphify/__main__.py +757 -0
- graphify/_minhash.py +107 -0
- graphify/affected.py +318 -0
- graphify/always_on/agents-md.md +12 -0
- graphify/always_on/antigravity-rules.md +14 -0
- graphify/always_on/claude-md.md +9 -0
- graphify/always_on/gemini-md.md +9 -0
- graphify/always_on/kiro-steering.md +5 -0
- graphify/always_on/vscode-instructions.md +17 -0
- graphify/analyze.py +769 -0
- graphify/benchmark.py +152 -0
- graphify/build.py +2300 -0
- graphify/cache.py +1746 -0
- graphify/callflow_html.py +2051 -0
- graphify/cargo_introspect.py +109 -0
- graphify/cli.py +4745 -0
- graphify/cluster.py +409 -0
- graphify/command-kilo.md +15 -0
- graphify/cross_repo_calls.py +216 -0
- graphify/cross_repo_types.py +75 -0
- graphify/csharp_dispatch.py +154 -0
- graphify/dedup.py +1213 -0
- graphify/detect.py +2566 -0
- graphify/diagnostics.py +406 -0
- graphify/export.py +1349 -0
- graphify/exporters/__init__.py +1 -0
- graphify/exporters/base.py +14 -0
- graphify/exporters/graphdb.py +173 -0
- graphify/exporters/html.py +637 -0
- graphify/extract.py +7856 -0
- graphify/extractors/MIGRATION.md +107 -0
- graphify/extractors/__init__.py +66 -0
- graphify/extractors/apex.py +215 -0
- graphify/extractors/base.py +85 -0
- graphify/extractors/bash.py +579 -0
- graphify/extractors/blade.py +53 -0
- graphify/extractors/commonlisp.py +540 -0
- graphify/extractors/csharp.py +448 -0
- graphify/extractors/dart.py +564 -0
- graphify/extractors/dm.py +494 -0
- graphify/extractors/elixir.py +241 -0
- graphify/extractors/engine.py +6509 -0
- graphify/extractors/fortran.py +311 -0
- graphify/extractors/go.py +527 -0
- graphify/extractors/json_config.py +240 -0
- graphify/extractors/julia.py +289 -0
- graphify/extractors/markdown.py +408 -0
- graphify/extractors/models.py +131 -0
- graphify/extractors/objc.py +566 -0
- graphify/extractors/ocaml.py +289 -0
- graphify/extractors/pascal.py +688 -0
- graphify/extractors/pascal_forms.py +196 -0
- graphify/extractors/powershell.py +522 -0
- graphify/extractors/razor.py +192 -0
- graphify/extractors/resolution.py +3584 -0
- graphify/extractors/robot.py +296 -0
- graphify/extractors/rust.py +470 -0
- graphify/extractors/sln.py +92 -0
- graphify/extractors/sql.py +720 -0
- graphify/extractors/terraform.py +181 -0
- graphify/extractors/verilog.py +329 -0
- graphify/extractors/zig.py +181 -0
- graphify/file_slice.py +246 -0
- graphify/global_graph.py +194 -0
- graphify/google_workspace.py +237 -0
- graphify/hooks.py +933 -0
- graphify/ids.py +93 -0
- graphify/ingest.py +358 -0
- graphify/install.py +2366 -0
- graphify/llm.py +3544 -0
- graphify/manifest.py +4 -0
- graphify/manifest_ingest.py +311 -0
- graphify/mcp_ingest.py +386 -0
- graphify/multigraph_compat.py +212 -0
- graphify/pascal_resolution.py +129 -0
- graphify/paths.py +436 -0
- graphify/pg_introspect.py +165 -0
- graphify/prs.py +770 -0
- graphify/querylog.py +80 -0
- graphify/reflect.py +882 -0
- graphify/report.py +346 -0
- graphify/resolver_registry.py +85 -0
- graphify/ruby_resolution.py +242 -0
- graphify/scip_ingest.py +363 -0
- graphify/security.py +460 -0
- graphify/semantic_cleanup.py +336 -0
- graphify/serve.py +2608 -0
- graphify/skill-agents.md +710 -0
- graphify/skill-aider.md +1283 -0
- graphify/skill-amp.md +710 -0
- graphify/skill-claw.md +713 -0
- graphify/skill-codex.md +710 -0
- graphify/skill-copilot.md +713 -0
- graphify/skill-devin.md +1410 -0
- graphify/skill-droid.md +710 -0
- graphify/skill-kilo.md +722 -0
- graphify/skill-kiro.md +713 -0
- graphify/skill-opencode.md +705 -0
- graphify/skill-pi.md +713 -0
- graphify/skill-trae.md +711 -0
- graphify/skill-vscode.md +709 -0
- graphify/skill-windows.md +755 -0
- graphify/skill.md +713 -0
- graphify/skills/agents/references/add-watch.md +56 -0
- graphify/skills/agents/references/exports.md +87 -0
- graphify/skills/agents/references/extraction-spec.md +70 -0
- graphify/skills/agents/references/github-and-merge.md +46 -0
- graphify/skills/agents/references/hooks.md +33 -0
- graphify/skills/agents/references/query.md +311 -0
- graphify/skills/agents/references/transcribe.md +52 -0
- graphify/skills/agents/references/update.md +210 -0
- graphify/skills/amp/references/add-watch.md +56 -0
- graphify/skills/amp/references/exports.md +87 -0
- graphify/skills/amp/references/extraction-spec.md +70 -0
- graphify/skills/amp/references/github-and-merge.md +46 -0
- graphify/skills/amp/references/hooks.md +33 -0
- graphify/skills/amp/references/query.md +311 -0
- graphify/skills/amp/references/transcribe.md +52 -0
- graphify/skills/amp/references/update.md +210 -0
- graphify/skills/claude/references/add-watch.md +56 -0
- graphify/skills/claude/references/exports.md +87 -0
- graphify/skills/claude/references/extraction-spec.md +70 -0
- graphify/skills/claude/references/github-and-merge.md +46 -0
- graphify/skills/claude/references/hooks.md +33 -0
- graphify/skills/claude/references/query.md +311 -0
- graphify/skills/claude/references/transcribe.md +52 -0
- graphify/skills/claude/references/update.md +210 -0
- graphify/skills/claw/references/add-watch.md +56 -0
- graphify/skills/claw/references/exports.md +87 -0
- graphify/skills/claw/references/extraction-spec.md +31 -0
- graphify/skills/claw/references/github-and-merge.md +46 -0
- graphify/skills/claw/references/hooks.md +33 -0
- graphify/skills/claw/references/query.md +311 -0
- graphify/skills/claw/references/transcribe.md +52 -0
- graphify/skills/claw/references/update.md +210 -0
- graphify/skills/codex/references/add-watch.md +56 -0
- graphify/skills/codex/references/exports.md +87 -0
- graphify/skills/codex/references/extraction-spec.md +31 -0
- graphify/skills/codex/references/github-and-merge.md +46 -0
- graphify/skills/codex/references/hooks.md +33 -0
- graphify/skills/codex/references/query.md +311 -0
- graphify/skills/codex/references/transcribe.md +52 -0
- graphify/skills/codex/references/update.md +210 -0
- graphify/skills/copilot/references/add-watch.md +56 -0
- graphify/skills/copilot/references/exports.md +87 -0
- graphify/skills/copilot/references/extraction-spec.md +70 -0
- graphify/skills/copilot/references/github-and-merge.md +46 -0
- graphify/skills/copilot/references/hooks.md +33 -0
- graphify/skills/copilot/references/query.md +311 -0
- graphify/skills/copilot/references/transcribe.md +52 -0
- graphify/skills/copilot/references/update.md +210 -0
- graphify/skills/droid/references/add-watch.md +56 -0
- graphify/skills/droid/references/exports.md +87 -0
- graphify/skills/droid/references/extraction-spec.md +70 -0
- graphify/skills/droid/references/github-and-merge.md +46 -0
- graphify/skills/droid/references/hooks.md +33 -0
- graphify/skills/droid/references/query.md +311 -0
- graphify/skills/droid/references/transcribe.md +52 -0
- graphify/skills/droid/references/update.md +210 -0
- graphify/skills/kilo/references/add-watch.md +56 -0
- graphify/skills/kilo/references/exports.md +87 -0
- graphify/skills/kilo/references/extraction-spec.md +70 -0
- graphify/skills/kilo/references/github-and-merge.md +46 -0
- graphify/skills/kilo/references/hooks.md +33 -0
- graphify/skills/kilo/references/query.md +311 -0
- graphify/skills/kilo/references/transcribe.md +52 -0
- graphify/skills/kilo/references/update.md +210 -0
- graphify/skills/kiro/references/add-watch.md +56 -0
- graphify/skills/kiro/references/exports.md +87 -0
- graphify/skills/kiro/references/extraction-spec.md +31 -0
- graphify/skills/kiro/references/github-and-merge.md +46 -0
- graphify/skills/kiro/references/hooks.md +33 -0
- graphify/skills/kiro/references/query.md +311 -0
- graphify/skills/kiro/references/transcribe.md +52 -0
- graphify/skills/kiro/references/update.md +210 -0
- graphify/skills/opencode/references/add-watch.md +56 -0
- graphify/skills/opencode/references/exports.md +87 -0
- graphify/skills/opencode/references/extraction-spec.md +70 -0
- graphify/skills/opencode/references/github-and-merge.md +46 -0
- graphify/skills/opencode/references/hooks.md +33 -0
- graphify/skills/opencode/references/query.md +311 -0
- graphify/skills/opencode/references/transcribe.md +52 -0
- graphify/skills/opencode/references/update.md +210 -0
- graphify/skills/pi/references/add-watch.md +56 -0
- graphify/skills/pi/references/exports.md +87 -0
- graphify/skills/pi/references/extraction-spec.md +31 -0
- graphify/skills/pi/references/github-and-merge.md +46 -0
- graphify/skills/pi/references/hooks.md +33 -0
- graphify/skills/pi/references/query.md +311 -0
- graphify/skills/pi/references/transcribe.md +52 -0
- graphify/skills/pi/references/update.md +210 -0
- graphify/skills/trae/references/add-watch.md +56 -0
- graphify/skills/trae/references/exports.md +87 -0
- graphify/skills/trae/references/extraction-spec.md +70 -0
- graphify/skills/trae/references/github-and-merge.md +46 -0
- graphify/skills/trae/references/hooks.md +35 -0
- graphify/skills/trae/references/query.md +311 -0
- graphify/skills/trae/references/transcribe.md +52 -0
- graphify/skills/trae/references/update.md +210 -0
- graphify/skills/vscode/references/add-watch.md +56 -0
- graphify/skills/vscode/references/exports.md +87 -0
- graphify/skills/vscode/references/extraction-spec.md +70 -0
- graphify/skills/vscode/references/github-and-merge.md +46 -0
- graphify/skills/vscode/references/hooks.md +33 -0
- graphify/skills/vscode/references/query.md +311 -0
- graphify/skills/vscode/references/transcribe.md +52 -0
- graphify/skills/vscode/references/update.md +210 -0
- graphify/skills/windows/references/add-watch.md +56 -0
- graphify/skills/windows/references/exports.md +87 -0
- graphify/skills/windows/references/extraction-spec.md +70 -0
- graphify/skills/windows/references/github-and-merge.md +46 -0
- graphify/skills/windows/references/hooks.md +33 -0
- graphify/skills/windows/references/query.md +311 -0
- graphify/skills/windows/references/transcribe.md +52 -0
- graphify/skills/windows/references/update.md +210 -0
- graphify/symbol_resolution.py +556 -0
- graphify/transcribe.py +186 -0
- graphify/tree_html.py +603 -0
- graphify/validate.py +95 -0
- graphify/watch.py +2280 -0
- graphify/wiki.py +405 -0
- graphitect/__init__.py +28 -0
- graphitect/__main__.py +4 -0
- graphitect/_vendor/__init__.py +2 -0
- graphitect/_vendor/archify/LICENSE +22 -0
- graphitect/_vendor/archify/SKILL.md +137 -0
- graphitect/_vendor/archify/THIRD_PARTY_NOTICES.md +69 -0
- graphitect/_vendor/archify/assets/JetBrainsMono-OFL.txt +93 -0
- graphitect/_vendor/archify/assets/template.html +14935 -0
- graphitect/_vendor/archify/bin/archify.mjs +2091 -0
- graphitect/_vendor/archify/bin/open-artifact.mjs +86 -0
- graphitect/_vendor/archify/bin/preview.mjs +653 -0
- graphitect/_vendor/archify/bin/visual-check.mjs +829 -0
- graphitect/_vendor/archify/brand-marks/README.md +31 -0
- graphitect/_vendor/archify/brand-marks/catalog.json +131 -0
- graphitect/_vendor/archify/delta/architecture-delta.mjs +1221 -0
- graphitect/_vendor/archify/examples/agent-run.lifecycle.json +60 -0
- graphitect/_vendor/archify/examples/agent-tool-call.workflow.json +94 -0
- graphitect/_vendor/archify/examples/async-job-roundtrip.sequence.json +61 -0
- graphitect/_vendor/archify/examples/brand-aware-delivery.architecture.json +47 -0
- graphitect/_vendor/archify/examples/cache-miss-request.sequence.json +82 -0
- graphitect/_vendor/archify/examples/checkout-platform.base.architecture.json +31 -0
- graphitect/_vendor/archify/examples/checkout-platform.head.architecture.json +31 -0
- graphitect/_vendor/archify/examples/dataflow-product-analytics.html +15045 -0
- graphitect/_vendor/archify/examples/deployment-release.lifecycle.json +49 -0
- graphitect/_vendor/archify/examples/event-stream.dataflow.json +57 -0
- graphitect/_vendor/archify/examples/incident-response.workflow.json +64 -0
- graphitect/_vendor/archify/examples/lifecycle-agent-run.html +14980 -0
- graphitect/_vendor/archify/examples/product-analytics.dataflow.json +76 -0
- graphitect/_vendor/archify/examples/production-deployment.architecture.json +71 -0
- graphitect/_vendor/archify/examples/release-delivery.workflow.json +62 -0
- graphitect/_vendor/archify/examples/sequence-cache-miss-request.html +15060 -0
- graphitect/_vendor/archify/examples/web-app-rendered.html +15009 -0
- graphitect/_vendor/archify/examples/web-app.architecture.json +46 -0
- graphitect/_vendor/archify/examples/workflow-agent-tool-call-rendered.html +15051 -0
- graphitect/_vendor/archify/migrations/workflow-v2.mjs +279 -0
- graphitect/_vendor/archify/package-lock.json +149 -0
- graphitect/_vendor/archify/package.json +39 -0
- graphitect/_vendor/archify/recipes/scenarios.mjs +391 -0
- graphitect/_vendor/archify/references/authoring-contract.md +243 -0
- graphitect/_vendor/archify/references/brand-marks.md +65 -0
- graphitect/_vendor/archify/references/delivery-contract.md +120 -0
- graphitect/_vendor/archify/references/viewer-runtime.md +45 -0
- graphitect/_vendor/archify/renderers/architecture/grid.mjs +62 -0
- graphitect/_vendor/archify/renderers/architecture/render-architecture.mjs +1078 -0
- graphitect/_vendor/archify/renderers/dataflow/README.md +104 -0
- graphitect/_vendor/archify/renderers/dataflow/render-dataflow.mjs +483 -0
- graphitect/_vendor/archify/renderers/lifecycle/README.md +115 -0
- graphitect/_vendor/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
- graphitect/_vendor/archify/renderers/sequence/README.md +114 -0
- graphitect/_vendor/archify/renderers/sequence/render-sequence.mjs +464 -0
- graphitect/_vendor/archify/renderers/shared/brand-marks.mjs +563 -0
- graphitect/_vendor/archify/renderers/shared/cli.mjs +218 -0
- graphitect/_vendor/archify/renderers/shared/desktop-readability.mjs +26 -0
- graphitect/_vendor/archify/renderers/shared/diagnostics.mjs +127 -0
- graphitect/_vendor/archify/renderers/shared/engineering-profiles.mjs +157 -0
- graphitect/_vendor/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
- graphitect/_vendor/archify/renderers/shared/generated-validators.mjs +13 -0
- graphitect/_vendor/archify/renderers/shared/geometry.mjs +1423 -0
- graphitect/_vendor/archify/renderers/shared/i18n.mjs +595 -0
- graphitect/_vendor/archify/renderers/shared/layout-report.mjs +40 -0
- graphitect/_vendor/archify/renderers/shared/legend.mjs +217 -0
- graphitect/_vendor/archify/renderers/shared/output-path.mjs +340 -0
- graphitect/_vendor/archify/renderers/shared/repository-evidence.mjs +238 -0
- graphitect/_vendor/archify/renderers/shared/repository-location.mjs +58 -0
- graphitect/_vendor/archify/renderers/shared/text-fit.mjs +49 -0
- graphitect/_vendor/archify/renderers/shared/utils.mjs +232 -0
- graphitect/_vendor/archify/renderers/shared/validator.mjs +86 -0
- graphitect/_vendor/archify/renderers/workflow/README.md +223 -0
- graphitect/_vendor/archify/renderers/workflow/render-workflow.mjs +35 -0
- graphitect/_vendor/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
- graphitect/_vendor/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
- graphitect/_vendor/archify/schemas/README.md +211 -0
- graphitect/_vendor/archify/schemas/architecture.schema.json +178 -0
- graphitect/_vendor/archify/schemas/common.schema.json +115 -0
- graphitect/_vendor/archify/schemas/dataflow.schema.json +243 -0
- graphitect/_vendor/archify/schemas/lifecycle.schema.json +266 -0
- graphitect/_vendor/archify/schemas/sequence.schema.json +223 -0
- graphitect/_vendor/archify/schemas/workflow.schema.json +428 -0
- graphitect/_vendor/archify/scripts/check-render-output.mjs +836 -0
- graphitect/_vendor/archify/scripts/check-update.mjs +1667 -0
- graphitect/_vendor/archify/scripts/generate-brand-marks.mjs +141 -0
- graphitect/_vendor/archify/scripts/generate-validators.mjs +66 -0
- graphitect/_vendor/archify/scripts/render-examples.mjs +26 -0
- graphitect/_vendor/archify/scripts/update-contract.mjs +182 -0
- graphitect/_vendor/archify/skill-release.json +10 -0
- graphitect/cli.py +981 -0
- graphitect/deliver/__init__.py +5 -0
- graphitect/deliver/archify_adapter.py +1877 -0
- graphitect/deliver/archify_ir.py +160 -0
- graphitect/deliver/archify_repair.py +135 -0
- graphitect/deliver/doc_compiler.py +916 -0
- graphitect/ground/__init__.py +5 -0
- graphitect/ground/describe_source.py +27 -0
- graphitect/ground/fullread_source.py +56 -0
- graphitect/ground/graphify_source.py +107 -0
- graphitect/models.py +118 -0
- graphitect/skill/SKILL.md +80 -0
- graphitect/skill/agents/openai.yaml +4 -0
- graphitect/synthesize/__init__.py +5 -0
- graphitect/synthesize/engine.py +281 -0
- graphitect/synthesize/llm_backend.py +331 -0
- graphitect/synthesize/questions.py +139 -0
- graphitect/synthesize/rubric.py +104 -0
- graphitect-0.2.0.dist-info/METADATA +284 -0
- graphitect-0.2.0.dist-info/RECORD +336 -0
- graphitect-0.2.0.dist-info/WHEEL +5 -0
- graphitect-0.2.0.dist-info/entry_points.txt +2 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE +21 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE-ARCHIFY-MIT +22 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE-GRAPHIFY-APACHE-2.0 +202 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE-GRAPHIFY-MIT +21 -0
- graphitect-0.2.0.dist-info/licenses/NOTICE-ARCHIFY-THIRD-PARTY.md +69 -0
- graphitect-0.2.0.dist-info/licenses/NOTICE-GRAPHIFY +8 -0
- graphitect-0.2.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
"""Go extractor. Moved verbatim from graphify/extract.py."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from graphify.extractors.base import _LANGUAGE_BUILTIN_GLOBALS, _file_stem, _make_id, _read_text
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
_GO_PREDECLARED_TYPES = frozenset({
|
|
11
|
+
"bool", "byte", "complex64", "complex128", "error", "float32", "float64",
|
|
12
|
+
"int", "int8", "int16", "int32", "int64", "rune", "string",
|
|
13
|
+
"uint", "uint8", "uint16", "uint32", "uint64", "uintptr", "any", "comparable",
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
# Go predeclared functions, filtered only when the callee is a BARE identifier.
|
|
17
|
+
# The Go resolver looks a callee up by name, so an unexported method that happens
|
|
18
|
+
# to share a builtin's name (`func (h *history) append(...)`) absorbs every
|
|
19
|
+
# builtin call in the corpus: on an 8.9k-node Go codebase one such method
|
|
20
|
+
# collected 330 phantom inbound `calls` edges, inventing twelve database-layer ->
|
|
21
|
+
# service-layer edges — a layering violation absent from the source.
|
|
22
|
+
#
|
|
23
|
+
# Deliberately language-local (mirroring _RUST_TRAIT_METHOD_BLOCKLIST) rather
|
|
24
|
+
# than added to the shared _LANGUAGE_BUILTIN_GLOBALS: `new`, `close` and friends
|
|
25
|
+
# are ordinary method names in the ~11 other languages that consult the shared
|
|
26
|
+
# set — listing them there kills every in-file Rust `Type::new()` edge.
|
|
27
|
+
#
|
|
28
|
+
# Bare-identifier-only for the same reason within Go: `h.append(v)` and
|
|
29
|
+
# `pkg.Delete(x)` are selector_expression callees and are genuine calls, so the
|
|
30
|
+
# filter must not reach them. Builtin *types* stay out (see
|
|
31
|
+
# _GO_PREDECLARED_TYPES): Go conversions are call-shaped too, but they produced
|
|
32
|
+
# no phantom edges on that corpus and filtering them would suppress genuine
|
|
33
|
+
# constructor-like calls.
|
|
34
|
+
#
|
|
35
|
+
# The set is the Go spec's predeclared function list in full. Being Go-local and
|
|
36
|
+
# bare-identifier-only makes completeness safe here: `len`, `max`, `min` and
|
|
37
|
+
# `print` carry the same shadowing hazard as `append`, and a principled boundary
|
|
38
|
+
# (the spec list) beats a hand-picked subset.
|
|
39
|
+
_GO_PREDECLARED_FUNCS = frozenset({
|
|
40
|
+
"append", "cap", "clear", "close", "complex", "copy", "delete", "imag",
|
|
41
|
+
"len", "make", "max", "min", "new", "panic", "print", "println", "real",
|
|
42
|
+
"recover",
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
def _go_collect_type_refs(node, source: bytes, generic: bool, out: list[tuple[str, str]]) -> None:
|
|
46
|
+
"""Walk a Go type expression; append (name, role) tuples."""
|
|
47
|
+
if node is None:
|
|
48
|
+
return
|
|
49
|
+
t = node.type
|
|
50
|
+
if t == "type_identifier":
|
|
51
|
+
text = _read_text(node, source)
|
|
52
|
+
if text and text not in _GO_PREDECLARED_TYPES:
|
|
53
|
+
out.append((text, "generic_arg" if generic else "type"))
|
|
54
|
+
return
|
|
55
|
+
if t == "qualified_type":
|
|
56
|
+
# Keep the package qualifier so the generic stub rewire cannot attach
|
|
57
|
+
# `testing.T` to an unrelated local type or function named T.
|
|
58
|
+
text = _read_text(node, source)
|
|
59
|
+
if text:
|
|
60
|
+
out.append((text, "generic_arg" if generic else "type"))
|
|
61
|
+
return
|
|
62
|
+
if t == "generic_type":
|
|
63
|
+
type_field = node.child_by_field_name("type")
|
|
64
|
+
if type_field is not None:
|
|
65
|
+
sub: list[tuple[str, str]] = []
|
|
66
|
+
_go_collect_type_refs(type_field, source, generic, sub)
|
|
67
|
+
out.extend(sub)
|
|
68
|
+
for c in node.children:
|
|
69
|
+
if c.type == "type_arguments":
|
|
70
|
+
for arg in c.children:
|
|
71
|
+
if arg.is_named:
|
|
72
|
+
_go_collect_type_refs(arg, source, True, out)
|
|
73
|
+
return
|
|
74
|
+
if t in ("pointer_type", "slice_type", "array_type", "map_type",
|
|
75
|
+
"channel_type", "parenthesized_type"):
|
|
76
|
+
for c in node.children:
|
|
77
|
+
if c.is_named:
|
|
78
|
+
_go_collect_type_refs(c, source, generic, out)
|
|
79
|
+
return
|
|
80
|
+
if node.is_named:
|
|
81
|
+
for c in node.children:
|
|
82
|
+
if c.is_named:
|
|
83
|
+
_go_collect_type_refs(c, source, generic, out)
|
|
84
|
+
|
|
85
|
+
def extract_go(path: Path) -> dict:
|
|
86
|
+
"""Extract functions, methods, type declarations, and imports from a .go file."""
|
|
87
|
+
try:
|
|
88
|
+
import tree_sitter_go as tsgo
|
|
89
|
+
from tree_sitter import Language, Parser
|
|
90
|
+
except ImportError:
|
|
91
|
+
return {"nodes": [], "edges": [], "error": "tree-sitter-go not installed"}
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
language = Language(tsgo.language())
|
|
95
|
+
parser = Parser(language)
|
|
96
|
+
source = path.read_bytes()
|
|
97
|
+
tree = parser.parse(source)
|
|
98
|
+
root = tree.root_node
|
|
99
|
+
except Exception as e:
|
|
100
|
+
return {"nodes": [], "edges": [], "error": str(e)}
|
|
101
|
+
|
|
102
|
+
stem = _file_stem(path)
|
|
103
|
+
# Use directory name as package scope so methods on the same type across
|
|
104
|
+
# multiple files in a package share one canonical type node.
|
|
105
|
+
pkg_scope = path.parent.name or stem
|
|
106
|
+
str_path = str(path)
|
|
107
|
+
nodes: list[dict] = []
|
|
108
|
+
edges: list[dict] = []
|
|
109
|
+
seen_ids: set[str] = set()
|
|
110
|
+
function_bodies: list[tuple[str, object]] = []
|
|
111
|
+
# local package name (including aliases) -> written Go import path
|
|
112
|
+
go_imported_pkgs: dict[str, str] = {}
|
|
113
|
+
|
|
114
|
+
def add_node(nid: str, label: str, line: int) -> None:
|
|
115
|
+
if nid not in seen_ids:
|
|
116
|
+
seen_ids.add(nid)
|
|
117
|
+
nodes.append({
|
|
118
|
+
"id": nid,
|
|
119
|
+
"label": label,
|
|
120
|
+
"file_type": "code",
|
|
121
|
+
"source_file": str_path,
|
|
122
|
+
"source_location": f"L{line}",
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
def add_edge(src: str, tgt: str, relation: str, line: int,
|
|
126
|
+
confidence: str = "EXTRACTED", weight: float = 1.0,
|
|
127
|
+
context: str | None = None) -> None:
|
|
128
|
+
edge = {
|
|
129
|
+
"source": src,
|
|
130
|
+
"target": tgt,
|
|
131
|
+
"relation": relation,
|
|
132
|
+
"confidence": confidence,
|
|
133
|
+
"source_file": str_path,
|
|
134
|
+
"source_location": f"L{line}",
|
|
135
|
+
"weight": weight,
|
|
136
|
+
}
|
|
137
|
+
if context:
|
|
138
|
+
edge["context"] = context
|
|
139
|
+
edges.append(edge)
|
|
140
|
+
|
|
141
|
+
file_nid = _make_id(str(path))
|
|
142
|
+
add_node(file_nid, path.name, 1)
|
|
143
|
+
|
|
144
|
+
def ensure_named_node(name: str, line: int) -> str:
|
|
145
|
+
nid = _make_id(pkg_scope, name)
|
|
146
|
+
if nid in seen_ids:
|
|
147
|
+
return nid
|
|
148
|
+
nid = _make_id(name)
|
|
149
|
+
if nid not in seen_ids:
|
|
150
|
+
# The name isn't declared in this file, so this is a cross-file reference
|
|
151
|
+
# (e.g. a type defined in another file of the package). Emit a SOURCELESS
|
|
152
|
+
# stub — like the inheritance-base path in the other extractors — so the
|
|
153
|
+
# corpus-level rewire can collapse it onto the real definition. A sourced
|
|
154
|
+
# stub here makes _disambiguate_colliding_node_ids bake the referencing
|
|
155
|
+
# file's path (with extension) into the id and blocks the rewire, which is
|
|
156
|
+
# the phantom-duplicate-node bug (#1402).
|
|
157
|
+
seen_ids.add(nid)
|
|
158
|
+
nodes.append({
|
|
159
|
+
"id": nid,
|
|
160
|
+
"label": name,
|
|
161
|
+
"file_type": "code",
|
|
162
|
+
"source_file": "",
|
|
163
|
+
"source_location": "",
|
|
164
|
+
"origin_file": str_path,
|
|
165
|
+
})
|
|
166
|
+
return nid
|
|
167
|
+
|
|
168
|
+
def emit_go_method_refs(func_node, func_nid: str, line: int) -> None:
|
|
169
|
+
params = func_node.child_by_field_name("parameters")
|
|
170
|
+
if params is not None:
|
|
171
|
+
for p in params.children:
|
|
172
|
+
if p.type != "parameter_declaration":
|
|
173
|
+
continue
|
|
174
|
+
type_node = p.child_by_field_name("type")
|
|
175
|
+
refs: list[tuple[str, str]] = []
|
|
176
|
+
_go_collect_type_refs(type_node, source, False, refs)
|
|
177
|
+
for ref_name, role in refs:
|
|
178
|
+
ctx = "generic_arg" if role == "generic_arg" else "parameter_type"
|
|
179
|
+
tgt = ensure_named_node(ref_name, line)
|
|
180
|
+
if tgt != func_nid:
|
|
181
|
+
add_edge(func_nid, tgt, "references", line, context=ctx)
|
|
182
|
+
result = func_node.child_by_field_name("result")
|
|
183
|
+
if result is not None:
|
|
184
|
+
if result.type == "parameter_list":
|
|
185
|
+
for p in result.children:
|
|
186
|
+
if p.type != "parameter_declaration":
|
|
187
|
+
continue
|
|
188
|
+
type_node = p.child_by_field_name("type")
|
|
189
|
+
if type_node is None:
|
|
190
|
+
for c in p.children:
|
|
191
|
+
if c.is_named:
|
|
192
|
+
type_node = c
|
|
193
|
+
break
|
|
194
|
+
refs = []
|
|
195
|
+
_go_collect_type_refs(type_node, source, False, refs)
|
|
196
|
+
for ref_name, role in refs:
|
|
197
|
+
ctx = "generic_arg" if role == "generic_arg" else "return_type"
|
|
198
|
+
tgt = ensure_named_node(ref_name, line)
|
|
199
|
+
if tgt != func_nid:
|
|
200
|
+
add_edge(func_nid, tgt, "references", line, context=ctx)
|
|
201
|
+
else:
|
|
202
|
+
refs = []
|
|
203
|
+
_go_collect_type_refs(result, source, False, refs)
|
|
204
|
+
for ref_name, role in refs:
|
|
205
|
+
ctx = "generic_arg" if role == "generic_arg" else "return_type"
|
|
206
|
+
tgt = ensure_named_node(ref_name, line)
|
|
207
|
+
if tgt != func_nid:
|
|
208
|
+
add_edge(func_nid, tgt, "references", line, context=ctx)
|
|
209
|
+
|
|
210
|
+
# Node IDs are casefolded (ids.py), so `Run` and `run` declared in one file
|
|
211
|
+
# produce the same id and add_node silently dropped the second — the unexported
|
|
212
|
+
# half vanished from the graph and its call sites bound by bare name to a
|
|
213
|
+
# same-named function in another package, which Go's visibility rules make
|
|
214
|
+
# impossible (#2779). Salt the non-canonical member of a case-only collision
|
|
215
|
+
# so both survive.
|
|
216
|
+
#
|
|
217
|
+
# The EXPORTED member keeps the plain id. Only exported symbols are reachable
|
|
218
|
+
# across packages, so cross-package edges (and edges cached in graph.json from
|
|
219
|
+
# files an incremental rebuild does not touch) target the exported one — keeping
|
|
220
|
+
# its id stable means adding/removing an unexported sibling in an update never
|
|
221
|
+
# re-points them. Docs likewise reference the exported API, so the casefolded id
|
|
222
|
+
# a semantic node produces lands on the symbol it actually describes. Calls to
|
|
223
|
+
# the unexported sibling can only come from the same package and only resolve
|
|
224
|
+
# once the sibling exists, so salting it re-points nothing. When the collision
|
|
225
|
+
# has no unique exported member (`Run`/`RUN`), every member is salted rather
|
|
226
|
+
# than picking one arbitrarily, so the result never depends on declaration order.
|
|
227
|
+
case_groups: dict[str, set[str]] = {}
|
|
228
|
+
|
|
229
|
+
def _receiver_type_of(node) -> str | None:
|
|
230
|
+
receiver = node.child_by_field_name("receiver")
|
|
231
|
+
if not receiver:
|
|
232
|
+
return None
|
|
233
|
+
for param in receiver.children:
|
|
234
|
+
if param.type == "parameter_declaration":
|
|
235
|
+
type_node = param.child_by_field_name("type")
|
|
236
|
+
if type_node:
|
|
237
|
+
return _read_text(type_node, source).lstrip("*").strip()
|
|
238
|
+
break
|
|
239
|
+
return None
|
|
240
|
+
|
|
241
|
+
def _plain_symbol_nid(node) -> tuple[str, str] | None:
|
|
242
|
+
name_node = node.child_by_field_name("name")
|
|
243
|
+
if not name_node:
|
|
244
|
+
return None
|
|
245
|
+
name = _read_text(name_node, source)
|
|
246
|
+
if node.type == "method_declaration":
|
|
247
|
+
receiver_type = _receiver_type_of(node)
|
|
248
|
+
base = _make_id(pkg_scope, receiver_type) if receiver_type else stem
|
|
249
|
+
else:
|
|
250
|
+
base = stem
|
|
251
|
+
return _make_id(base, name), name
|
|
252
|
+
|
|
253
|
+
def _scan_declarations(node) -> None:
|
|
254
|
+
if node.type in ("function_declaration", "method_declaration"):
|
|
255
|
+
found = _plain_symbol_nid(node)
|
|
256
|
+
if found:
|
|
257
|
+
case_groups.setdefault(found[0], set()).add(found[1])
|
|
258
|
+
return
|
|
259
|
+
for child in node.children:
|
|
260
|
+
_scan_declarations(child)
|
|
261
|
+
|
|
262
|
+
def symbol_nid(plain_nid: str, name: str) -> str:
|
|
263
|
+
names = case_groups.get(plain_nid) or set()
|
|
264
|
+
if len(names) < 2:
|
|
265
|
+
return plain_nid
|
|
266
|
+
exported = [n for n in names if n[:1].isupper()]
|
|
267
|
+
if len(exported) == 1 and name == exported[0]:
|
|
268
|
+
return plain_nid
|
|
269
|
+
salt = hashlib.sha1(name.encode("utf-8"), usedforsecurity=False).hexdigest()[:6]
|
|
270
|
+
return _make_id(plain_nid, salt)
|
|
271
|
+
|
|
272
|
+
def walk(node) -> None:
|
|
273
|
+
t = node.type
|
|
274
|
+
|
|
275
|
+
if t == "function_declaration":
|
|
276
|
+
name_node = node.child_by_field_name("name")
|
|
277
|
+
if name_node:
|
|
278
|
+
func_name = _read_text(name_node, source)
|
|
279
|
+
line = node.start_point[0] + 1
|
|
280
|
+
func_nid = symbol_nid(_make_id(stem, func_name), func_name)
|
|
281
|
+
add_node(func_nid, f"{func_name}()", line)
|
|
282
|
+
add_edge(file_nid, func_nid, "contains", line)
|
|
283
|
+
emit_go_method_refs(node, func_nid, line)
|
|
284
|
+
body = node.child_by_field_name("body")
|
|
285
|
+
if body:
|
|
286
|
+
function_bodies.append((func_nid, body))
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
if t == "method_declaration":
|
|
290
|
+
receiver = node.child_by_field_name("receiver")
|
|
291
|
+
receiver_type: str | None = None
|
|
292
|
+
if receiver:
|
|
293
|
+
for param in receiver.children:
|
|
294
|
+
if param.type == "parameter_declaration":
|
|
295
|
+
type_node = param.child_by_field_name("type")
|
|
296
|
+
if type_node:
|
|
297
|
+
receiver_type = _read_text(type_node, source).lstrip("*").strip()
|
|
298
|
+
break
|
|
299
|
+
name_node = node.child_by_field_name("name")
|
|
300
|
+
if not name_node:
|
|
301
|
+
return
|
|
302
|
+
method_name = _read_text(name_node, source)
|
|
303
|
+
line = node.start_point[0] + 1
|
|
304
|
+
|
|
305
|
+
if receiver_type:
|
|
306
|
+
parent_nid = _make_id(pkg_scope, receiver_type)
|
|
307
|
+
add_node(parent_nid, receiver_type, line)
|
|
308
|
+
method_nid = symbol_nid(_make_id(parent_nid, method_name), method_name)
|
|
309
|
+
add_node(method_nid, f".{method_name}()", line)
|
|
310
|
+
add_edge(parent_nid, method_nid, "method", line)
|
|
311
|
+
else:
|
|
312
|
+
method_nid = symbol_nid(_make_id(stem, method_name), method_name)
|
|
313
|
+
add_node(method_nid, f"{method_name}()", line)
|
|
314
|
+
add_edge(file_nid, method_nid, "contains", line)
|
|
315
|
+
|
|
316
|
+
emit_go_method_refs(node, method_nid, line)
|
|
317
|
+
body = node.child_by_field_name("body")
|
|
318
|
+
if body:
|
|
319
|
+
function_bodies.append((method_nid, body))
|
|
320
|
+
return
|
|
321
|
+
|
|
322
|
+
if t == "type_declaration":
|
|
323
|
+
for child in node.children:
|
|
324
|
+
if child.type != "type_spec":
|
|
325
|
+
continue
|
|
326
|
+
name_node = child.child_by_field_name("name")
|
|
327
|
+
if not name_node:
|
|
328
|
+
continue
|
|
329
|
+
type_name = _read_text(name_node, source)
|
|
330
|
+
line = child.start_point[0] + 1
|
|
331
|
+
type_nid = _make_id(pkg_scope, type_name)
|
|
332
|
+
add_node(type_nid, type_name, line)
|
|
333
|
+
add_edge(file_nid, type_nid, "contains", line)
|
|
334
|
+
# Type body: struct fields (with embeds) or interface embedding.
|
|
335
|
+
type_body = None
|
|
336
|
+
for tc in child.children:
|
|
337
|
+
if tc.type in ("struct_type", "interface_type"):
|
|
338
|
+
type_body = tc
|
|
339
|
+
break
|
|
340
|
+
if type_body is None:
|
|
341
|
+
continue
|
|
342
|
+
if type_body.type == "struct_type":
|
|
343
|
+
for fdl in type_body.children:
|
|
344
|
+
if fdl.type != "field_declaration_list":
|
|
345
|
+
continue
|
|
346
|
+
for field in fdl.children:
|
|
347
|
+
if field.type != "field_declaration":
|
|
348
|
+
continue
|
|
349
|
+
has_name = any(
|
|
350
|
+
fc.type == "field_identifier" for fc in field.children
|
|
351
|
+
)
|
|
352
|
+
type_node = field.child_by_field_name("type")
|
|
353
|
+
if type_node is None:
|
|
354
|
+
for fc in field.children:
|
|
355
|
+
if fc.is_named and fc.type != "field_identifier":
|
|
356
|
+
type_node = fc
|
|
357
|
+
break
|
|
358
|
+
refs: list[tuple[str, str]] = []
|
|
359
|
+
_go_collect_type_refs(type_node, source, False, refs)
|
|
360
|
+
for ref_name, role in refs:
|
|
361
|
+
tgt = ensure_named_node(ref_name, field.start_point[0] + 1)
|
|
362
|
+
if tgt == type_nid:
|
|
363
|
+
continue
|
|
364
|
+
if not has_name and role == "type":
|
|
365
|
+
add_edge(type_nid, tgt, "embeds",
|
|
366
|
+
field.start_point[0] + 1)
|
|
367
|
+
else:
|
|
368
|
+
ctx = "generic_arg" if role == "generic_arg" else "field"
|
|
369
|
+
add_edge(type_nid, tgt, "references",
|
|
370
|
+
field.start_point[0] + 1, context=ctx)
|
|
371
|
+
elif type_body.type == "interface_type":
|
|
372
|
+
for elem in type_body.children:
|
|
373
|
+
if elem.type != "type_elem":
|
|
374
|
+
continue
|
|
375
|
+
# A type_elem that is a generics type-set constraint -
|
|
376
|
+
# a union (`A | B`) or an approximation (`~T`) - is NOT
|
|
377
|
+
# interface embedding. Go only embeds a lone interface
|
|
378
|
+
# type; union/approximation terms can never be embedded,
|
|
379
|
+
# so they must not emit `embeds` heritage edges. Keep the
|
|
380
|
+
# type link as a `references` (type_constraint) edge.
|
|
381
|
+
is_type_set = any(
|
|
382
|
+
c.type == "|" or c.type == "negated_type"
|
|
383
|
+
for c in elem.children
|
|
384
|
+
)
|
|
385
|
+
refs = []
|
|
386
|
+
for sub in elem.children:
|
|
387
|
+
if sub.is_named:
|
|
388
|
+
_go_collect_type_refs(sub, source, False, refs)
|
|
389
|
+
for ref_name, role in refs:
|
|
390
|
+
tgt = ensure_named_node(ref_name, elem.start_point[0] + 1)
|
|
391
|
+
if tgt == type_nid:
|
|
392
|
+
continue
|
|
393
|
+
if is_type_set:
|
|
394
|
+
add_edge(type_nid, tgt, "references",
|
|
395
|
+
elem.start_point[0] + 1, context="type_constraint")
|
|
396
|
+
elif role == "type":
|
|
397
|
+
add_edge(type_nid, tgt, "embeds",
|
|
398
|
+
elem.start_point[0] + 1)
|
|
399
|
+
else:
|
|
400
|
+
add_edge(type_nid, tgt, "references",
|
|
401
|
+
elem.start_point[0] + 1, context="generic_arg")
|
|
402
|
+
return
|
|
403
|
+
|
|
404
|
+
if t == "import_declaration":
|
|
405
|
+
for child in node.children:
|
|
406
|
+
if child.type == "import_spec_list":
|
|
407
|
+
for spec in child.children:
|
|
408
|
+
if spec.type == "import_spec":
|
|
409
|
+
path_node = spec.child_by_field_name("path")
|
|
410
|
+
if path_node:
|
|
411
|
+
raw = _read_text(path_node, source).strip('"')
|
|
412
|
+
# Prefix with go_pkg_ so stdlib names (e.g. "context")
|
|
413
|
+
# don't collide with local files of the same basename.
|
|
414
|
+
tgt_nid = _make_id("go", "pkg", raw)
|
|
415
|
+
add_edge(file_nid, tgt_nid, "imports_from", spec.start_point[0] + 1, context="import")
|
|
416
|
+
# Track local name (alias or last path segment)
|
|
417
|
+
alias = spec.child_by_field_name("name")
|
|
418
|
+
local_name = _read_text(alias, source) if alias else raw.split("/")[-1]
|
|
419
|
+
if local_name and local_name != "_" and local_name != ".":
|
|
420
|
+
go_imported_pkgs[local_name] = raw
|
|
421
|
+
elif child.type == "import_spec":
|
|
422
|
+
path_node = child.child_by_field_name("path")
|
|
423
|
+
if path_node:
|
|
424
|
+
raw = _read_text(path_node, source).strip('"')
|
|
425
|
+
tgt_nid = _make_id("go", "pkg", raw)
|
|
426
|
+
add_edge(file_nid, tgt_nid, "imports_from", child.start_point[0] + 1, context="import")
|
|
427
|
+
alias = child.child_by_field_name("name")
|
|
428
|
+
local_name = _read_text(alias, source) if alias else raw.split("/")[-1]
|
|
429
|
+
if local_name and local_name != "_" and local_name != ".":
|
|
430
|
+
go_imported_pkgs[local_name] = raw
|
|
431
|
+
return
|
|
432
|
+
|
|
433
|
+
for child in node.children:
|
|
434
|
+
walk(child)
|
|
435
|
+
|
|
436
|
+
_scan_declarations(root)
|
|
437
|
+
walk(root)
|
|
438
|
+
|
|
439
|
+
label_to_nid: dict[str, str] = {}
|
|
440
|
+
for n in nodes:
|
|
441
|
+
raw = n["label"]
|
|
442
|
+
normalised = raw.strip("()").lstrip(".")
|
|
443
|
+
label_to_nid[normalised] = n["id"]
|
|
444
|
+
|
|
445
|
+
seen_call_pairs: set[tuple[str, str]] = set()
|
|
446
|
+
raw_calls: list[dict] = []
|
|
447
|
+
|
|
448
|
+
def walk_calls(node, caller_nid: str) -> None:
|
|
449
|
+
if node.type in ("function_declaration", "method_declaration"):
|
|
450
|
+
return
|
|
451
|
+
if node.type == "call_expression":
|
|
452
|
+
func_node = node.child_by_field_name("function")
|
|
453
|
+
callee_name: str | None = None
|
|
454
|
+
is_member_call: bool = False
|
|
455
|
+
is_bare_identifier: bool = False
|
|
456
|
+
package_receiver: str | None = None
|
|
457
|
+
import_path: str | None = None
|
|
458
|
+
if func_node:
|
|
459
|
+
if func_node.type == "identifier":
|
|
460
|
+
is_bare_identifier = True
|
|
461
|
+
callee_name = _read_text(func_node, source)
|
|
462
|
+
elif func_node.type == "selector_expression":
|
|
463
|
+
field = func_node.child_by_field_name("field")
|
|
464
|
+
operand = func_node.child_by_field_name("operand")
|
|
465
|
+
receiver_name = _read_text(operand, source) if operand else ""
|
|
466
|
+
# Package-qualified call (e.g. fmt.Println) → allow cross-file resolution.
|
|
467
|
+
# Receiver method call (e.g. s.logger.Log) → skip, no import evidence.
|
|
468
|
+
is_member_call = receiver_name not in go_imported_pkgs
|
|
469
|
+
if not is_member_call:
|
|
470
|
+
package_receiver = receiver_name
|
|
471
|
+
import_path = go_imported_pkgs[receiver_name]
|
|
472
|
+
if field:
|
|
473
|
+
callee_name = _read_text(field, source)
|
|
474
|
+
if is_bare_identifier and callee_name in _GO_PREDECLARED_FUNCS:
|
|
475
|
+
# A bare `append(s, x)` is the builtin, never the same-named
|
|
476
|
+
# method a sibling file happens to declare. Skipping before both
|
|
477
|
+
# branches drops the in-file phantom edge and keeps the name out
|
|
478
|
+
# of raw_calls, so the cross-file pass cannot bind it either.
|
|
479
|
+
callee_name = None
|
|
480
|
+
if callee_name and callee_name not in _LANGUAGE_BUILTIN_GLOBALS:
|
|
481
|
+
# Never resolve an imported selector through a bare local name.
|
|
482
|
+
tgt_nid = None if import_path else label_to_nid.get(callee_name)
|
|
483
|
+
if tgt_nid and tgt_nid != caller_nid:
|
|
484
|
+
pair = (caller_nid, tgt_nid)
|
|
485
|
+
if pair not in seen_call_pairs:
|
|
486
|
+
seen_call_pairs.add(pair)
|
|
487
|
+
line = node.start_point[0] + 1
|
|
488
|
+
edges.append({
|
|
489
|
+
"source": caller_nid,
|
|
490
|
+
"target": tgt_nid,
|
|
491
|
+
"relation": "calls",
|
|
492
|
+
"context": "call",
|
|
493
|
+
"confidence": "EXTRACTED",
|
|
494
|
+
"source_file": str_path,
|
|
495
|
+
"source_location": f"L{line}",
|
|
496
|
+
"weight": 1.0,
|
|
497
|
+
})
|
|
498
|
+
elif callee_name:
|
|
499
|
+
raw_calls.append({
|
|
500
|
+
"caller_nid": caller_nid,
|
|
501
|
+
"callee": callee_name,
|
|
502
|
+
"is_member_call": is_member_call,
|
|
503
|
+
"language": "go",
|
|
504
|
+
"receiver": package_receiver,
|
|
505
|
+
"import_path": import_path,
|
|
506
|
+
"source_file": str_path,
|
|
507
|
+
"source_location": f"L{node.start_point[0] + 1}",
|
|
508
|
+
})
|
|
509
|
+
for child in node.children:
|
|
510
|
+
walk_calls(child, caller_nid)
|
|
511
|
+
|
|
512
|
+
for caller_nid, body_node in function_bodies:
|
|
513
|
+
walk_calls(body_node, caller_nid)
|
|
514
|
+
|
|
515
|
+
valid_ids = seen_ids
|
|
516
|
+
clean_edges = []
|
|
517
|
+
for edge in edges:
|
|
518
|
+
src, tgt = edge["source"], edge["target"]
|
|
519
|
+
if src in valid_ids and (tgt in valid_ids or edge["relation"] in ("imports", "imports_from")):
|
|
520
|
+
clean_edges.append(edge)
|
|
521
|
+
|
|
522
|
+
return {
|
|
523
|
+
"nodes": nodes,
|
|
524
|
+
"edges": clean_edges,
|
|
525
|
+
"raw_calls": raw_calls,
|
|
526
|
+
"go_imports": dict(go_imported_pkgs),
|
|
527
|
+
}
|