Skip to content

fix(go): a method a type declares in a file the index couldn't read is not its embedded type's - #2757

Merged
colbymchenry merged 1 commit into
mainfrom
claude/go-unread-promoted-methods
Oct 21, 2026
Merged

colbymchenry merged 1 commit into
mainfrom
claude/go-unread-promoted-methods

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Follow-up to #2755.

Problem

kubernetes' staging/src/k8s.io/api/core/v1/generated.pb.go is 1.7 MB, over the index's 1 MB limit, so it is stored without being read. Every core/v1 API type's own String, Marshal, Unmarshal, Size, MarshalToSizedBuffer and Reset lives there, so none of them is in the graph.

Resolving a typed Go receiver, resolveGoMethodInPackage looks for the type's own method first and, not finding it, walks the types it embeds. For a core/v1 type that walk always succeeds: Pod embeds metav1.TypeMeta and metav1.ObjectMeta, which have those methods. So on main:

  • pod.String() on a *v1.Pod linked TypeMeta.String;
  • p.Unmarshal(e.Pod) after var p v1.Pod linked TypeMeta.Unmarshal;
  • m.Template.Size() and m.Template.MarshalToSizedBuffer(…) in the apps, batch and extensions generated.pb.go (a v1.PodTemplateSpec) linked ObjectMeta's.

None of those is the method the call runs. #2755 fixed this for calls on indexed elements; every other typed receiver (a parameter, a local, a field, an assertion, a package-level value) took the same path.

Fix

  • resolveGoMethodInPackage reads the method declarations of the files of the type's package that the index read nothing from (func (m *Pod) String(), from their text, and returns null when the type declares the method there. Such a file is one with no node of its own (getNodesInFileNamed(f, basename), one indexed probe per file); test files don't count. A file over 32 MB isn't read, and counts as declaring it.
  • matchMethodCall's typed branch and matchGoPackageValueCall keep that null. They fall through to name matching when a declared project type lacks a method (for types defined over an interface), and name matching then found the same promoted method.
  • fix(go): a call on an indexed element reaches its element type's method #2755's copy of the check for indexed elements is removed: it goes through the shared path now.

A method the type really gets from an embedded type, like GetName from ObjectMeta, still links: only a method the unread file declares for the type stops the walk.

Measured

Base = main a5fe6e1, fix = this branch, the same kernel, a full index per arm.

repo edges removed added other changes
kubernetes 4c05e667 63 0 none (nodes, every other edge, metadata identical)
etcd, prometheus 0 0 none
express, flask (controls) 0 0 byte-identical

All 63 were read. Each is a typed Go call that had reached a protobuf method of TypeMeta (21) or ObjectMeta (42) promoted into a core/v1 type that declares its own in the unread file:

  • m.Template.Size(), .MarshalToSizedBuffer(…) and .Unmarshal(…) in apps v1/v1beta1/v1beta2, batch v1 and extensions v1beta1 generated.pb.go (42, Template is a v1.PodTemplateSpec);
  • pod.Unmarshal(…), p.Unmarshal(…) on var p v1.Pod in test/e2e_node (12);
  • obj.Marshal(), obj.Reset(), out.Unmarshal(…), validate.Unmarshal(…) on &v1.Pod{} in pkg/api/testing (6);
  • pod.String() on a *v1.Pod in pkg/volume (3).

kubernetes has three Go files the index reads nothing from: this one, pkg/generated/openapi/zz_generated.openapi.go (4 MB, functions only) and a test file.

Cost: a CPU-profiled kubernetes index per arm. The unread-file helpers take 76 ms of self time, against 22 ms on main, where #2755 used them for indexed elements only. The methods they sit in (resolveGoMethodInPackage, resolveMethodOnType, matchMethodCall, matchGoPackageValueCall) are within noise: 844 ms on this branch, 916 ms on main. Busy CPU was 389.7 s and 396.4 s, wall time 116 s and 118 s.

Tests

__tests__/go-unread-file-methods.test.ts, 7 cases, 5 of which fail on main a5fe6e1 (each linking TypeMeta's String or Unmarshal): a parameter, a local bound to a composite literal, a field (h.pod.String()), a type assertion, a package-level variable. The other two are the fixture's facts: the 1 MB+ file is indexed without a node, and a method that really is promoted (GetName) still links. Each of the three checks was mutation-checked: dropping it fails at least one case. __tests__/go-indexed-receiver.test.ts (#2755) passes unchanged through the shared path.

Full suite on this commit (main a5fe6e1, kernel built, CODEGRAPH_KERNEL_EXPECT=1, --maxWorkers=4 --minWorkers=1): 697 files, 8,254 tests passed, 82 skipped, 0 failed. tsc --noEmit is clean.

🤖 Generated with Claude Code

…s not its embedded type's

kubernetes' core/v1 generated.pb.go is 1.7 MB, over the index's 1 MB
limit, so every API type's own String, Marshal, Unmarshal, Size and
MarshalToSizedBuffer is missing from the graph. Resolving `pod.String()` on
a `*v1.Pod`, or `m.Template.Size()` on a `v1.PodTemplateSpec`, the walk over
embedded types found the method the embedded TypeMeta or ObjectMeta has and
linked that, a method the call never runs.

resolveGoMethodInPackage, which every typed Go receiver goes through
(parameters, locals, fields, assertions, package-level values, indexed
elements), now reads the method declarations of the files of the type's
package that the index couldn't read, from their text, and links nothing
when the type declares the method there. matchMethodCall's typed branch and
matchGoPackageValueCall keep that null instead of falling through to name
matching, which found the same promoted method. A method the type really
gets from an embedded type, like GetName from ObjectMeta, still links. The
check that #2755 added for indexed elements moves into the shared path.

On kubernetes 4c05e667, 63 calls lose that link and nothing else changes;
etcd, prometheus, express and flask are unchanged.

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