Skip to content

Go: weak short-name strategies fabricate CALLS edges for selector calls (stdlib receivers hijack project symbols) #1906

Description

@ilyabrykau-orca

Status: fix open — PR #1907 (extractor is_method flag for Go selector calls + Go-gated cbm_go_suppress_weak_method_match). Full post-fix census inline below.

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?

A Go selector call whose receiver the Go LSP cannot type falls through to the generic registry, which binds it by bare short name to an arbitrary same-named project symbol (suffix_match / unique_name). Stdlib/third-party calls are the worst case: the real callee is not in the tree, so every candidate is wrong by construction. Go never sets is_method, so the receiver-aware guard TS/JS got in #592/#606 (and Perl in #476) never fires.

Measured (private Go monorepo, ~19.6k CALLS): suffix_match 3817 @ avg 0.30, unique_name 3162 @ avg 0.53 — 36% of all CALLS from name guesses. One 14-line checksum helper touching only os/io/crypto/sha256 got 3/3 false outbound edges: f.Close() → a project Close (conf 0.11, 15 candidates), sha256.New() → an unrelated New, io.Copy → the sole project Copy (unique_name 0.375). trace_path surfaces all of it unfiltered, so Close/New/Run accumulate dozens of phantom callers.

Expected: no CALLS edge for a call the resolver cannot place; typed, local, and import-qualified calls keep resolving.

Reproduction

Dummy fixture: go.mod (module example.com/myapp) + storage/storage.go (the lone project Close method + a typed same-package Boot caller) + hash/hash.go:

package hash

import (
	"os"

	"example.com/myapp/util"
)

func FileLen(path string) int64 {
	f, err := os.Open(path)
	if err != nil {
		return 0
	}
	defer f.Close() // ← false CALLS edge: hash.FileLen -> storage.Close
	st, err := f.Stat()
	if err != nil {
		return 0
	}
	return st.Size()
}

func UsesUtil() string { return util.Tag() } // import-qualified: must keep resolving

Actual: FileLen | Close | {"strategy":"unique_name","confidence":0.75,…} (with ≥2 candidates: suffix_match @ 0.1–0.4). Expected: no FileLen → Close; Boot → Close, bare local calls, and UsesUtil → Tag all survive.

Two Go-specific constraints (why the TS/JS drop-list can't be reused as-is)

Post-fix strategy census (same tree indexed with v0.10.8 vs Stack A = #1907+#1913+#1915)

strategy v0.10.8 with the fixes Δ
lsp_strategy_cross_file 5273 6468 +23%
lsp_direct 3033 3372 +11%
lsp_type_dispatch 137 687 +401%
lsp_interface_dispatch 17 79 +365%
lsp_embed_dispatch 42 new
import_map 284 88 −69%
field_type_hint 493 1484 +201% — see #1927/#1936
unique_name 3162 (avg 0.53) 1573 (avg 0.68) −50%
suffix_match 3817 (avg 0.30) 113 −97%
same_module 3267 224 −93%
CALLS total 19653 14316 −27%

Weak short-name share (suffix_match+unique_name+callee_suffix): 36.0% → 12.4%, with lsp_* rising 8481 → 10672 in absolute terms — the edges were re-resolved, not merely deleted. The field_type_hint growth is the direct consequence tracked as #1927 (guesses moved from a visibly-weak 0.30 bucket into a 0.85 one); fixed by #1936.

Related: #592/#606 (TS/JS), #476 (Perl), #1893 (Swift), #1276 (Python), #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