Skip to content

fix(go): a type in a signature or composite literal links a type, never a method of its name - #2416

Merged
colbymchenry merged 9 commits into
mainfrom
claude/focused-bose-a97a24
Oct 7, 2026
Merged

colbymchenry merged 9 commits into
mainfrom
claude/focused-bose-a97a24

Conversation

@colbymchenry

@colbymchenry colbymchenry commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

Summary

A Go type position now resolves only to a type (struct / interface / type_alias) of the package Go reads the name from. A type position here is a parameter or result type (a references ref from a function or method) or a composite literal's type (instantiates). Route handlers are references refs too, but they are values, so they are left alone.

  • Kind gate at the single seam. gateTargetKind (src/resolution/index.ts) hands every Go type position to a new goTypePositionTarget (src/resolution/name-matcher.ts, beside the other Go scoping helpers). So the gate applies to whatever strategy won: name matching, the instance-method single-candidate guess, or a framework resolver (before fix(frameworks): a framework resolves only references written in its own languages #2415, Express took etcd's *zap.Logger results for EtcdServer.Logger).
    • A method or function target moves to the type of that name in the reference's own package (bare name) or in the imported project package (pkg.T, or the dotted name a composite literal keeps).
    • When no such type exists there (an outside package, a qualifier that is none of the file's imports, a type parameter), the edge is dropped and the ref stays in unresolved_refs.
    • A bare name that found another package's type moves to its own package's type when the package declares one. Pattern 4 skips names with digits or _, so prometheus's prompb/codec.go had built write/v2's Histogram_CountInt.
  • Generic receivers declare type parameters. declaredTypeParameters (src/resolution/type-parameters.ts) now reads a Go method's receiver. T in func (p *Pool[T]) Get() T is the receiver's type parameter; prometheus linked it to the method Sample.T in another package.
  • Why a gate rather than widening Pattern 4 to unexported names: Pattern 4 only adds a 0.7 candidate. The method it competes with is found by exact match at 0.7–0.9, often in the same file, so it would still lose. The gate sees the final result.
  • Why composite literals too: they are type positions by the same rule, and main has the same bug there. etcd's &keyIndex{…} linked treeIndex.keyIndex, prometheus's 43 samples{…} linked sampleRing.samples, and 20 config.URL{…} (prometheus/common, an outside package) linked scrape.Target.URL.

Validation

Test first. __tests__/go-type-position-kinds.test.ts puts the shapes below in miniature.

  • 7 of its 8 tests fail on main and all 8 pass with the fix, on both the wasm and the native-kernel extraction paths. The eighth guards that the ti.keyIndex(keyi) call still links the method.
  • tsc --noEmit is clean.

A/B on real repos. Main 9d07864 (the commit before this squash) vs this branch, a full init -y per arm on the native-kernel path, diffed per reference site (source, line, col, refName):

repo edges retargeted dropped added
gin 7674 → 7674 0 0 0
etcd 63105 → 63105 19 0 0
prometheus 96837 → 96813 55 24 0

Every changed site, triaged:

  • etcd, retargeted (19), all from a method to its own package's type:
    • keyIndex ×12 parameter/result types and ×5 &keyIndex{…} → struct keyIndex (were treeIndex.keyIndex);
    • kvsToEvents(…, c contains, …) → interface contains (was its own method spec);
    • *leafPageElement → struct (was page.leafPageElement).
  • prometheus, retargeted (55):
    • samples{…} ×43 → defined type samples in storage/series.go (were the method sampleRing.samples);
    • floatBucketIterator ×2 → struct;
    • isHistogram_Count / isHistogram_ZeroCount ×4 → the interfaces (were methods of the oneof structs);
    • (ec2Client, error) → interface ec2Client;
    • AppenderV2 → interface (was fanout.AppenderV2);
    • &Histogram_CountInt{…} and its 3 siblings in prompb/codec.go → prompb's own structs (were write/v2's).
  • prometheus, dropped (24):
    • config_util.URL{…} ×10, common_config.URL{…} ×8 and config.URL{…} ×2, all github.com/prometheus/common/config → were scrape.Target.URL;
    • T ×4 in the generic receivers of util/zeropool, web/api/testhelpers and web/api/v1 → were Sample.T.

After the fix, both measuring scripts report 0 on all three repos: no type position on a method or function, and no composite literal on a non-type.

Tests on the merged tree (Go suites, #2410's go-import-package-names, kernel-tsjs-parity, sync, sync-import-retry, resolution, frameworks, cross-file-visibility, reference-target-kind, type-parameter-refs): 20 files, 653 tests, all pass.

Full suite (Windows, other sessions holding the CPU at 90–100%):

Not changed here

  • = aliases are not indexed. A bare Event in etcd's client/v3 (type Event = mvccpb.Event), or PrometheusVersion / Type in prometheus, keeps linking another package's type. The own package declares no type of that name, and for an alias that target is often the right one.

🤖 Generated with Claude Code

colbymchenry and others added 9 commits October 7, 2026 00:26
…er a method of its name

A Go type position -- a parameter or result type, a composite literal's
type -- names a type of the package Go reads it from. Name matching (and
the instance-method guess, and the Express resolver reading Go refs) took
whatever declaration shared the name: etcd's
`func (ti *treeIndex) KeyIndex(keyi *keyIndex) *keyIndex` linked both types
to the method `treeIndex.keyIndex`, prometheus's `samples{...}` literals to
`sampleRing.samples`, and `semver.Version` or `config.URL{...}` from
outside packages to project methods and functions of that name.

gateTargetKind now hands every Go type position to goTypePositionTarget:
a method or function target moves to the type of that name in the
reference's own package (bare) or the imported project package (`pkg.T`),
or is dropped when there is none; a bare name that found another
package's type moves to its own package's type when it declares one
(prometheus's `prompb` builds its own `Histogram_CountInt`). A generic
method's receiver (`func (p *Pool[T]) Get() T`) now declares type
parameters for gateTypeParameter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`semver.Version` reads as an outside package since #2410, so its links
to project methods are gone before this change; `config.URL{...}` still
went to `scrape.Target.URL`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant