Skip to content

Go: method QNs ignore the receiver — every same-name method in a package collapses onto one node #1909

Description

@ilyabrykau-orca

Status: fix open — PR #1913 (receiver-qualified Go method QNs, mirroring the C++ out-of-line block; stacked on #1907). Post-fix: Close methods 15 → 86 nodes, Process 12 → 47, Task 1 → 8; one generated dir recovered 1 → 63 Close methods (source 66; the residual 3 are #1911's twins). Generic receivers verified on real code: 45 methods across 8 generic receivers carry clean receiver-qualified QNs (pkg.Cache.Add for func (c *Cache[K, V]) Add) and zero node QNs contain a [. Changes extracted QNs for Go methods → reindex needed (flagged per CONTRIBUTING).

Version

codebase-memory-mcp 0.10.8 (also reproduced on a source build @ d706c33)

Platform

macOS (Apple Silicon)

Install channel

Built from source

Binary variant

standard

What happened, and what did you expect?

Go concrete methods get a flat, package-scoped QN (proj.pkg.method — receiver ignored), so every same-name method in a package collides on one QN; cbm_gbuf_upsert_node keeps exactly one node and the losers vanish silently (parse coverage stays clean, so check_index_coverage cannot see it).

Measured (private Go repo): 20 Process/Name declarations across 15 files kept 2 nodes; 9 distinct Task() methods fused into one chimera node carrying all 9 bodies' CALLS edges (26 with line numbers outside the surviving body); 19 structs point DEFINES_METHOD at the same single Process; a _test.go mock Close won the smallest-file-path tie-break over the production Close, so callers of the "production" symbol got filtered as test noise.

Interface members already get proj.pkg.Iface.method; C++ out-of-line methods already get proj.file.Class.method; the extractor already computes the receiver type for parent_class — it just doesn't put it in the QN.

Expected: func (s *Storage) Close()proj.pkg.Storage.Close, one node per receiver.

Reproduction

// x.go
package m

type Storage struct{}
type Cache struct{}

func (s *Storage) Close() {}
func (c Cache) Close() {}

Actual: one Method node m.Close, both types' DEFINES_METHOD pointing at it. Expected: m.Storage.Close + m.Cache.Close.

Secondary effect this also fixes

resolve_same_module (conf 0.90) is an exact module.name lookup. Flat method QNs occupy that key, so pipeline.Process(event) binds whatever unrelated Process merged into the caller's package — a high-confidence false edge no filter can remove. Receiver-qualified QNs delete the flat key; those calls fall through to the typed/guarded paths.

Fix notes (all consumers already agree)

pxc_build_lsp_def passes def QNs verbatim (LSP registries follow automatically); check_go_class_implements already supports class-qualified method QNs; the call-scope side (compute_func_qn) mirrors the def formula via one shared receiver-type helper.

Related: #1906 (the merge made weak edges land on high-traffic symbols), #1114; tracked in #1932.

Confirmations

  • I searched existing issues and this is not a duplicate.
  • My reproduction uses shareable code (a dummy snippet or a public OSS repository), not proprietary code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    parsing/qualityGraph extraction bugs, false positives, missing edges

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions