Skip to content

Go AST: method receivers and cross-file call resolution produce disconnected graphs #85

Description

@naveenspark

Problem

The Go AST extractor produces disconnected nodes for files that only contain methods on types defined in other files within the same package. This causes graphify to report these files as orphans when they're actually heavily used.

In a large Go codebase, graphify reported 12 files as orphaned with zero edges. Manual grep revealed that methods in those files have 34+ callers — they're some of the most-used code in the project.

Root Cause

1. Method receiver type nodes are file-scoped, not package-scoped

extract_go() at line ~1238:

parent_nid = _make_id(stem, receiver_type)

If a type is defined in foo.go but has methods in bar.go and baz.go, each file creates its own type node with a different ID. These are separate disconnected nodes in the graph, not one canonical type.

2. Call resolution is single-file only

label_to_nid (line ~1290) is built from nodes extracted from the current file only. When one file calls a method defined in another file within the same package, the call resolver can't find it because it only searches the current file's label map.

This means all cross-file method calls within a Go package are invisible to the graph.

Impact

  • Files with only method definitions appear as orphans (zero edges)
  • The god-nodes analysis misses heavily-called methods
  • Community detection can't cluster types with their methods across files
  • Dead code detection (zero-degree nodes) produces false positives

Suggested Fix

For type node IDs

Use the package path + type name instead of file stem + type name:

# Instead of: _make_id(stem, receiver_type)
# Use: _make_id(package_path, receiver_type)

Where package_path is derived from the directory. All methods on a type across all files in the same package would share one node.

For cross-file call resolution

Run extraction in two passes:

  1. Per-file pass: Extract all nodes (functions, methods, types) from each file
  2. Per-package pass: Build a combined label_to_nid map for all files sharing a Go package, then resolve calls against the full map

This mirrors how Go itself resolves identifiers — package-scoped, not file-scoped.

Environment

  • graphify v0.3.12 via pipx
  • Large Go codebase (500+ files)
  • tree-sitter-go via graphify's bundled parser

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions