Repository navigation
fix(go): implementing an interface needs matching parameter and result counts - #2447
Merged
Merged
Conversation
…t counts goImplementsEdges matched method names only, so a struct whose same-named method takes or returns a different number of values, which Go can never accept, was linked as an implementation (etcd's watch cache as a peerGetter), and enough such look-alikes filled the per-interface cap before the real implementer (grpc-go's xDS TransportBuilder). Each wanted method now also needs a declaration with its parameter and result counts, read off the stored signature text; a signature that doesn't read rules nothing out. A gRPC client interface (every method takes `opts ...grpc.CallOption` last) also accepts its RPCs' server arities, so `KVClient.Range -> kvServer.Range`, the bridge explore follows from a client call to its handler, stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…9d1da4 # Conflicts: # CHANGELOG.md # src/resolution/callback-synthesizer.ts
…9d1da4 # Conflicts: # CHANGELOG.md
…9d1da4 # Conflicts: # CHANGELOG.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
goImplementsEdges(Go implicit interface satisfaction, #584) matched method names only. Go wants every method's signature identical, so a type whose same-named method takes or returns a different number of values can never satisfy the interface. Name matching linked it anyway:Cache(Get(ctx, key string, opts ...OpOption) (*GetResponse, error)) "implemented" rafthttp'speerGetter(Get(id types.ID) Peer), along with 34 otherGets;Closer { Close() }collected 34 types whoseClosereturns an error;Buildtakes other arguments filled the 40-slot cap of xDS'sTransportBuilder(Build(ServerIdentifier) (Transport, error)) before the realgrpctransport.Builderwas reached, socodegraph_explorecould not follow the xDS client into the transport it builds.Change
signaturetext ((params) results). Parameters are the top-level comma-separated items (a, b intis two); results are none, one bare type, or a parenthesized list. Nested brackets (Pair[K, V],func(a, b int)), string literals (struct tags) and comments are skipped. A signature that doesn't read rules nothing out; on the five repos below every Go method signature read.opts ...grpc.CallOptionlast) also accepts its service's server arity: one parameter fewer with the same results for a unary RPC, one parameter fewer returningerrorfor a streaming one (the client's first result is not a pointer). SoKVClient.Range → kvServer.Range, which is no Go implementation but is the bridge a client's call crosses to reach its handler, stays. The generated client stub and real client wrappers keep matching by the client arity as before.Why keep the gRPC client → server pairs
They act as RPC bridges:
codegraph_explore's Flow section follows a client call through the client interface into the server handler. With a plain arity gate those flows break, which is the partial-coverage case AGENTS.md warns about:kv.Do KVClient.Range kvServer.Range EtcdServer.RangeDo → Do → KVClient.Range → kvServer.RangeKVClient.Rangelessor.Grant LeaseClient.LeaseGrant LeaseServer.LeaseGrant EtcdServer.LeaseGrantEtcdServer.LeaseGrantls2lcadapterwatchGRPCStream.openWatchClient WatchClient.Watch watchServer.Watch(bidi)→ watchServer.WatchrunRecordRoute RouteGuideClient.RecordRoute routeGuideServer.RecordRoute(client stream)→ routeGuideServer.RecordRouterequest_Query_Balance_0 QueryClient.Balance BaseKeeper.Balance(REST gateway)→ BaseKeeper.BalanceLRSClient.getOrCreateLRSStream TransportBuilder.Build Builder.Build→ grpctransport.Builder.Build→ grpctransport.Builder.BuildXDSClient.getOrCreateChannel TransportBuilder.Build Builder.Build(The plain-gate column ran on main 31c3328; main and this PR's columns were re-run on current main ed199e6 with identical Flow sections.) The dedicated
goGrpcStubImplEdgespass doesn't cover this: it linksUnimplemented*Serverstub methods to implementations, not client interfaces to servers.A first, looser exception (accept
params - 1with any result count) let anySet(a, b) errorstruct match cosmos-sdk's one-methodKeyValueClient; deriving the server arity per RPC kind fixed that. Requiring the interface to live in a.pb.gofile changed only 3 grpc-go edges (one is the server of checked-in generated code under another name), so it isn't required.Validation
Kit: gin (
43fe48e), prometheus (39c878f), etcd (76d58e3), grpc-go (6c0c296) and cosmos-sdk (2ad20ad), each indexed by current main (ed199e6) and by this branch, same kernel. The graphs differ only ingo-implementsedges and theinterface-implcalls that hang off them; nodes, unresolved refs and files are identical.go-implementsinterface-implpeerGetter.Get(35 on etcd),Compactable.Compact(the generatedkVClient.Compactwith call options),WatchClient.Watchvs clientv3'sWatch(ctx, key, opts...) WatchChan, printers'MemberAdd(resp), backend transactions'Lock()vsLockClient.Lock(ctx, in, opts...), prometheusCloser/AppenderV2/TBRun, grpc-go balancer builders underTransportBuilder/methodLoggerBuilder/ChannelCredentials, cosmos-sdk gomock recorders(arg0, arg1 any) *gomock.Calland the protobuffastReflection_*types underParamStore,HasValue,KeyValueServer. The 58 etcd gRPC-interface removals are none of them servers (the 5 keptWatchClientimplementers are exactly the generated client, the stub,watchServer,watchProxyand thews2wcadapter).grpctransport.Builderand fake transport underTransportBuilder, the xDS credential builders underChannelCredentials, prometheus discoverers underDiscoverer, and some same-count/other-type matches the cap used to hide (e.g.ProducerBuilder).exact/loose= signature text equal, up to package qualifiers):The 16 left are a struct whose own method hides a promoted one of the same name but another shape (etcd's test
integrationClientdeclaresAuthEnable(ctx) errorand embeds*clientv3.Client); the gate counts any declaration in the method set, as the name check already did.QueryBuildercalls (counted per method on etcd, grpc-go and cosmos-sdk); the arity work only runs for pairs whose names already match. Alternating both builds in one process over the same stripped graph (15 rounds), the pass medians were etcd 253 → 266 ms, grpc-go 540 → 545, prometheus 308 → 290, cosmos-sdk 2865 → 2737 on main 31c3328, and grpc-go 513 → 516, cosmos-sdk 4209 → 4414 on current main with a test run going: within this box's noise.Agent A/B
Not run. The gRPC client→server flows come out byte-identical to main; the flows that changed are the two grpc-go xDS ones that now connect, plus side links in the "Dynamic-dispatch links" list (etcd's
Compactable.Compactnow shows a test fake instead of the client wrapperretryKVClient). A Windows port ofab-new-vs-baseline.sh(no rsync/jq here, globalcodegraphdropped from the arm PATH, node CLI-block hook) is ready, but everyclaude -prun on this machine fails with "OAuth session expired and could not be refreshed".Tests
__tests__/go-implements-arity.test.ts(22 cases): etcd/prometheus/grpc-go shapes (aGetwith other parameters, aClosewith a result, promoted methods, grouped names, multi-line signatures with comments, generics and struct tags, the cap-starvedTransportBuilder), a generated gRPC service with unary, server-streaming and bidi RPCs reaching the client stub, the unimplemented server and the real server (not a same-named wrapper, not via a mixed interface), andgoSignatureArityunit cases. All 22 fail on main and pass here, on the kernel and the wasm extraction paths.go-bare-call-no-method) failed once in the parallel run and passed alone, on this branch and on main.function-ref.test.tsCaller/impact graph misses methods passed as first-class references (callbacks), e.g. executor.submit(obj.method, …) #1820 case at its own 60 s in-file limit, which passes alone here and on main.Overlap
Merged current main (#2414 promoted dispatch, #2419 defined types, #2417 type aliases) into this branch: the gate applies to defined-type implementers too, and #2414's promoted links follow whatever
go-implementsedges remain.Merged on top of
Main moved five times during the merge (#2430, #2442, #2444, #2433, #2449, #2434, #2448, #2437, #2431, #2441, #2436). Only #2430 touches this area: it drops Go struct-to-struct
interface-impledges, a set disjoint from the interface-based ones this PR changes. Re-indexing gin and etcd with the merged build differed from the earlier fix arm by exactly #2430's own removals (1 and 60 edges), so the tables above stand. #2441 addsreferencesedges from defined types, which this pass never reads. On the merged tree (kernel rebuilt for #2441), the Go suites, including every incoming Go PR's tests, pass on both extraction paths (11 files, 168 tests).Follow-ups (not here)
goGrpcStubImplEdgesmatches by name only as well: on etcd it linksUnimplementedKVServer.RangetoretryKVClient.Rangeandkvs2kvc.Range, client wrappers.🤖 Generated with Claude Code