Repository navigation
fix(go): an unaliased import is also known by the name goimports assumes for its package - #2410
Merged
Merged
Conversation
…mes for its package An unaliased Go import was named by its path's last element, so the qualifier in yaml.Node (go.yaml.in/yaml/v3), klog.V (k8s.io/klog/v2) or sqlite3.Error (github.com/mattn/go-sqlite3) matched none of the file's imports. The reference then resolved by its bare name to whatever project symbol shared it. An unaliased import now also takes the name goimports' ImportPathToAssumedName gives its path (the last element that isn't a major version, without a go- prefix, cut at the first non-identifier character), unless another import of the file is bound to that name or a second import assumes it too. The last element stays registered: k8s.io/api/core/v1 really is package v1. The import scan now reads only the import section, without comments: the last word of a comment had become the next import's alias, and imports spelled in doc comments or string literals were read as real ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 7, 2026
colbymchenry
added a commit
that referenced
this pull request
Oct 7, 2026
`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>
colbymchenry
added a commit
that referenced
this pull request
Oct 7, 2026
…hich #2410 keeps unknown #2410 makes an unaliased go.yaml.in/yaml/v3 known as yaml, so the test's yaml.Node alias no longer reached the rule. An unaliased go.etcd.io/etcd/client/v3 is known as v3 or client, never clientv3; with the rule disabled, this case fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 7, 2026
This was referenced Oct 7, 2026
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.
Problem
extractGoImportsnames an unaliased Go import by its path's last element. For a versioned orgo-path, that element is not the name the code uses:go.yaml.in/yaml/v3v3yaml.Unmarshal(…),yaml.Nodegopkg.in/yaml.v3yaml.v3yaml.Xgithub.com/mattn/go-sqlite3go-sqlite3sqlite3.Errork8s.io/klog/v2v2klog.V(2)So the qualifier matched none of the file's imports, and everything that reads one treated the name as unqualified:
goRefQualifier/isGoExternalQualified/isInGoQualifierPackage(Go: cross-package calls are not resolved whengo.modis not at the project root #2322) saw no outside package to reject;resolveGoCrossPackageReferencecouldn't follow a project one.The name then resolved by itself to whatever project symbol shared it. #2397 guarded supertypes only (
isGoUnknownQualified); calls and type references were still open. On kubernetes, 1,561klog.V(…)calls went to theVmethod of a logging wrapper inapiserver/pkg/storage/etcd3/logger.go. 1,431klog.Loggertypes went to theLoggerinterfaces of two test servers.What changed
src/resolution/import-resolver.ts, resolver only (extraction and the kernel are untouched):ImportPathToAssumedName): the last element that isn't a major version (/vN→ the element before), with ago-prefix trimmed, cut at the first character an identifier can't hold. The last-element name stays registered, sincek8s.io/api/core/v1really is packagev1. This is what keeps harbor'sv1.Artifact{…}edge, which made fix(go): a comment in an import declaration is not part of an import #2374 drop a/vNrule that replaced the name.pkg/apis/abac/v0(assumedabac) andpkg/apis/abac.func/type/var/constthat isn't inside a block comment."github.com/golang/protobuf/proto" //nolint:staticcheck // TODO: remove for a supported versionmade etcd's followingprotoreflectimportversion.import "…"in a doc comment or a string literal as an import (a code generator's template, a test fixture). Overlap with fix(go): a comment in an import declaration is not part of an import #2374 is described below.The doc comments of
isGoUnknownQualifiedand itsgateTargetKindcall no longer citeyaml.Node. That supertype is now rejected as an outside package's. The guard's remaining case is a package named neither way, likeclientv3under a barego.etcd.io/etcd/client/v3.Validation
Tests
__tests__/go-import-package-names.test.ts, 6 tests:/v2module (kit.New()), a k8s-stylev1package beside acoreone, and an import a comment used to rename.__tests__/go-interface-embedding.test.ts(fix(go): an interface links the interfaces it embeds #2397):yaml.Nodeis now kept out as an outside package;clientv3.KVembedding under a barego.etcd.io/etcd/client/v3was added, soisGoUnknownQualifiedstays covered. Without the guard it yieldskvPrefix implements KV@other/other.go.__tests__/go-import-comments.test.tspasses unchanged against this branch, 10/10 with the kernel and withCODEGRAPH_KERNEL=0.tscpasses. On the rebased branch (main@31c3328d, after fix(go): satisfying an interface counts the methods embedding brings in #2402, fix(go): a type written bare links its own package's, not a namesake struct elsewhere #2408 and fix(kernel): a Go value likewm.neworraft.Nonekeeps its function ref #2401's Go kernel change, kernel rebuilt), 24 Go / resolution / framework / import / parity / extraction / function-ref / sync suites pass withCODEGRAPH_KERNEL_EXPECT=1, 1,420 of 1,421 tests. The exception isfunction-ref's Caller/impact graph misses methods passed as first-class references (callbacks), e.g. executor.submit(obj.method, …) #1820 Python case. It has its own 60 s timeout, and under the same load it fails on main too (62.5 s against 60.8 s on this branch).git-index-currency,sync,mcp-stale-refusal,mcp-status-freshness,oversize-file-not-read,worktree-detection,awaited-receiver,grammar-load-failureandlaravel-route-paths, none with a Go fixture, and one Python case infunction-ref. All pass rerun serially with a longer timeout. Thefunction-refcase takes 33 s on main and 40 s on this branch when run alone.Before / after (full index, kernel path,
main@31c3328d vs this branch, same kernel; natural-key edge diff;vendor/excluded by default)gin, prometheus, etcd and harbor give the same diff on fe95183, before #2401. The same A/B on the first base, 24501fa (before #2402 and #2408), changed the same sites. Some of the removed false bindings had pointed elsewhere there: the Go framework heuristic that #2408 narrowed had picked etcd's
*raft.Config→ cache'sConfigand harbor'sredis.Client→ the dockerhub adapter'sClient. Every changed edge was triaged at its site. The script read the qualifier written there and the import it names under each arm's naming, and checked that a qualified target lives in that import's package directory (module path stripped), or nowhere in the project for an outside import.validator/v10,go-toml/v2,go-json, …) had no false bindings.mmap.Map(…)had been linked toLabels::Map;kingpin.Application(×2) andgovultr.Instanceto discovery/eureka's structs;kingpin.Valueto promql/parser'sValue;compute.Serviceto discovery/kubernetes'sService;instance.Serverto stackit'sServer.semver/v3refs:*semver.Versionhad gone to client/v3'sVersionfunction (37 → 20 incoming edges), wal'sVersioninterface (31 → 6) andCluster::Version(17 → 1).raft/v3refs:raft.ReadState{…}had gone to a test double'sReadStatemethod,raft.Loggertoserver::Logger,*raft.ConfigtoServerHealth::Config.protoreflect/protorefs from the comment fix.jwt/v5calls.jwt.Parsehad gone tojwtOptions::Parse. The other 3 were correct:jwt.assign/jwt.infoinserver/auth/jwt_test.goare calls on a localjwt, err := newTokenProviderJWT(…)that shadows the import. The outside-package check rejects every candidate, methods included. That rule already applied to imports named by their last element. These are the only correct edges lost across the five repos (see limits).digest.Digest→Suite::Digest(23), go-redis/v9'sredis.Client→ harbor's ownClientstructs incommon/httpandpkg/clients/core(5),jwt.Claims→ harbor's ownclaims/v2.Claims(2),gock.Request→notification.Request(1).config.X()calls incore/main.go,metadata.Instance()and amodels.PostGreSQL{…}literal;config.Loadretarget, fromConfigStore::Loadtolib/config'sLoad;import0.9.This matches fix(go): a comment in an import declaration is not part of an import #2374's harbor numbers (16 gained, 1 retargeted).
&v1.Artifact{…}inpreheat_test.go, an unaliasedpkg/scan/rest/v1import, keeps its edge on both arms.klog/v24,574,ginkgo/v21,904,go-restful/v347,semver/v430,dbus/v514,diskv/v37,blackfriday/v23, 1 each fromgo-socks5,go-jose/v4andcbor/v2. By how they had been resolved: 5,010instance-methodcalls, 1,551exact-matchtype refs, 21 other. Effects:Errorfdrops from 389 incoming edges to 5;Loggerinterface drops from 1,310 to 17;Framework::BeforeEachdrops from 384 to 1 (ginkgo.BeforeEach).None was flagged as a local variable shadowing the import.
logs.AddFlagshad gone to kubectl'sLogsOptions::AddFlagsand now reaches component-base/logs'AddFlags, andmount.Interfacehad gone to fsquota'sInterfaceand now reaches mount-utils'Interface.import0.9.k8s.io/mount-utils, a staging module whose package ismount;b.mounter.IsLikelyNotMountPoint), because a field declaredmount.Interfacenow has a known package.extendsedge and 3 synthesized from it:removeall_test.go'sfakeMounterembedsmount.FakeMounter. The 3 are an overrideinterface-implfromFakeMounter, one frommount.Interface, andfakeMounter implements mount.Interfacethrough the promoted methods fix(go): satisfying an interface counts the methods embedding brings in #2402 counts.err.Error→notRegisteredErr::Error, wrong). It was removed for an incidental reason, noted below.X.Name, and a second spelling makes it give up: harbor's&distribution.Descriptor{Digest: digest.Digest(dig)}(9), kubernetes'klog.Infof("Log using Infof, …")andklog.Errorf(…)in a logs example, andcmp.Diffon a line that prints"Diff:".klog.Logger→ the same file'sTContext::Loggermethod (3). On main it produced 944 Go edges on kubernetes and 7 on etcd.Both are follow-ups, below.
Performance (measured on the first base)
.gofile in one process: prometheus 18 → 28 ms, kubernetes 213 → 546 ms.All within run-to-run noise.
Overlap
import "…"in a later string literal isn't read as an import (6 kubernetes files).stripCommentsForRegexline collapse into a rebase.localName. The two compose; for example, a project…/log/v2imported bare will be recognized aslog. No textual overlap.Limits and follow-ups (not in this PR)
jwt, err := …besideimport "github.com/golang-jwt/jwt/v5".isGoExternalQualifiedrejects method candidates too, sojwt.assign(…)loses its edge (3 on etcd). The rule predates this change; this change only makes more qualifiers recognizable. A fix would check whether the qualifier is a parameter or local at the site before treating it as the package.k8s.io/api/core/v1assumescorebut isv1; goimports adds an explicit name in such cases, so it mostly shows in hand-written imports. It is registered unless another import holdscore. No added or moved edge landed outside its import's package. For a project package, the declaredpackageclause would give the real name. That would also makeclientv3.KVunder a barego.etcd.io/etcd/client/v3resolve, which still goes throughisGoUnknownQualified.X.Name, so it reads the wrong qualifier when anotherX.Nameprecedes (klog.Error(err.Error())givesklogforerr.Error; this fired once on kubernetes and removed a wrong guess). It reads none when the name is spelled twice (the 12 residual sites above). Taking the receiver from the ref name would fix both.languages.logger.V(…)onlogger := klog.FromContext(ctx)still goes to the etcd3 wrapper'sV(2,292 edges, a receiver-name guess for an outside type);config.URL{…}becomeinstantiatesedges to aTarget::URLmethod (20).🤖 Generated with Claude Code