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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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