fix(pipeline): a Go import can resolve to a function or method — the name-guess fallbacks are language-blind
Status: fix open — PR #1938 (both name-guess fallbacks — Strategy 3 symbol-name and Strategy 1b sibling-file — gated on the importing file's language via cbm_import_symbol_fallback_allowed, false for Go). Measured: Go IMPORTS targets Method 85 → 0, Function 4 → 0, Folder 2206 unchanged. Generalises #1926/#1931 (import "C" was the special case).
What happens
A Go import path names a package — never a function, method, field, or a Makefile target. Strategy 1 in cbm_pipeline_resolve_import_node() resolves every correct Go import (module path → package Folder). When it misses — the normal case for stdlib/third-party — the correct result is no edge, but two fallbacks fire instead:
- Strategy 3 (
pass_pkgmap.c): takes the last path segment, cbm_gbuf_find_by_names it project-wide, filters by the permissive import_targetable_label() (admits Function/Method/…, right for Python/Java where imports can name members), returns the lexicographically smallest survivor. import "os/exec" → any project symbol named exec.
- Strategy 1b (sibling-file resolution) admits symbol labels through the same filter, re-creating the bug one directory closer (found by field census after gating Strategy 3 alone: one surviving
os/exec → a same-package exec() method).
Damage measured (~1150-file Go repo)
| Go IMPORTS resolved to |
count |
Folder (correct) |
2206 |
Method/Function (false) |
89 (3.9%) |
Breakdown: os/exec → a test harness's exec method (52); "C" → a test helper's C (27, = #1926); .../wait → a rate limiter's method (6); two imports → a Function extracted from a Makefile (4). Blast radius is confined to the IMPORTS relation (calls in those files resolve via LSP first; only 1 knock-on call edge) — but IMPORTS is what get_architecture and dependency views are built from, where a Go file "depends on" a test harness or a Makefile.
Minimal reproduction
// app/run.go
package app
import "os/exec"
func Run() error { return exec.Command("true").Run() }
// helper/harness.go
package helper
type harness struct{ n int }
func (h *harness) exec(cmd string) error { return nil }
go.mod. Expected: os/exec is external → no IMPORTS edge. Actual: edge to helper.harness.exec.
Detection recipe
SELECT t.label, count(*) FROM edges e
JOIN nodes s ON s.id = e.source_id JOIN nodes t ON t.id = e.target_id
WHERE e.type = 'IMPORTS' AND s.file_path LIKE '%.go'
GROUP BY 1 ORDER BY 2 DESC;
For Go, any row that is not Folder/Module/File is false.
Fix (as landed)
The issue's "cheaper first cut": skip both name-guess fallbacks when the importing file is Go — the measurement found no correct Go import that needed either. Member-importing languages (Python from m import f, Java, Kotlin, Rust use crate::ops::helper) keep Strategy 3; build/markup grammars (SCSS partials, Meson subdir, Pony use) keep Strategy 1b. Reproduce-first exact-count probe (ei_edge_count_is — a floor can't catch a fabricated extra edge) with cross-package and same-package decoys.
Related: #1926/#1931, #725, #787; tracked in #1932.
fix(pipeline): a Go
importcan resolve to a function or method — the name-guess fallbacks are language-blindStatus: fix open — PR #1938 (both name-guess fallbacks — Strategy 3 symbol-name and Strategy 1b sibling-file — gated on the importing file's language via
cbm_import_symbol_fallback_allowed, false for Go). Measured: Go IMPORTS targetsMethod85 → 0,Function4 → 0,Folder2206 unchanged. Generalises #1926/#1931 (import "C"was the special case).What happens
A Go import path names a package — never a function, method, field, or a Makefile target. Strategy 1 in
cbm_pipeline_resolve_import_node()resolves every correct Go import (module path → packageFolder). When it misses — the normal case for stdlib/third-party — the correct result is no edge, but two fallbacks fire instead:pass_pkgmap.c): takes the last path segment,cbm_gbuf_find_by_names it project-wide, filters by the permissiveimport_targetable_label()(admitsFunction/Method/…, right for Python/Java where imports can name members), returns the lexicographically smallest survivor.import "os/exec"→ any project symbol namedexec.os/exec→ a same-packageexec()method).Damage measured (~1150-file Go repo)
Folder(correct)Method/Function(false)Breakdown:
os/exec→ a test harness'sexecmethod (52);"C"→ a test helper'sC(27, = #1926);.../wait→ a rate limiter's method (6); two imports → aFunctionextracted from a Makefile (4). Blast radius is confined to the IMPORTS relation (calls in those files resolve via LSP first; only 1 knock-on call edge) — but IMPORTS is whatget_architectureand dependency views are built from, where a Go file "depends on" a test harness or a Makefile.Minimal reproduction
go.mod. Expected:os/execis external → no IMPORTS edge. Actual: edge tohelper.harness.exec.Detection recipe
For Go, any row that is not
Folder/Module/Fileis false.Fix (as landed)
The issue's "cheaper first cut": skip both name-guess fallbacks when the importing file is Go — the measurement found no correct Go import that needed either. Member-importing languages (Python
from m import f, Java, Kotlin, Rustuse crate::ops::helper) keep Strategy 3; build/markup grammars (SCSS partials, Meson subdir, Ponyuse) keep Strategy 1b. Reproduce-first exact-count probe (ei_edge_count_is— a floor can't catch a fabricated extra edge) with cross-package and same-package decoys.Related: #1926/#1931, #725, #787; tracked in #1932.