Repository navigation
fix(go): type receivers from package-qualified parameters and call results; no guess for outside types - #1954
danusha2345 wants to merge 1 commit into
Conversation
eacc1d1 to
0afc052
Compare
Ports colbymchenry#1954's goImportedPackageDirs / goIsExternalImport onto colbymchenry#2324's per-import module lookup (getGoModuleForImport), with a small getGoModuleOfFile so a file outside every module still treats only the standard library as certainly external. EXTRACTION_VERSION 40: Go edges change for modules below the project root. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
I ran this branch ( Type aliases. mattermost/mattermost ( Embedded package-qualified struct. goharbor/harbor ( Separately, in etcd-io/etcd ( The full comparison table is in #2325 (comment). |
… structs, conversions)
54a2adf to
96d8906
Compare
|
Rebased onto main and redone as one commit on top of #2361. That PR covers a large part of what this one did, so those parts and their tests are dropped here:
Two scoping rules from the earlier version are also left out, because they need the receiver scan to run without its per-line memo and they drop edges main gets today: a What this PR still adds on main, all with tests that fail there:
One rule is narrower than before: a project function that returns an outside type by value, such as Re-measured on harbor 🤖 Generated with Claude Code |
96d8906 to
1107ed0
Compare
|
Rebased onto main at ed199e6 and reduced again to what main still lacks. One commit; the description above is rewritten to match. Dropped, because main covers it
Kept, still failing on main (16 of the 21 remaining test cases fail on ed199e6; the other 5 are controls)
#2408, #2414 and #2416 change how a type reference and an interface call resolve; none of them types a receiver, and none of the 16 cases moved. Ported onto #2417 rather than dropped An alias of an outside type ( Re-measured on goharbor/harbor |
…o guess for outside types Three receiver shapes still got no type, and a receiver typed outside the project still got a guess by method name: - A variable named like a standard-library package (`ring`, `token`, `parser`). Every `ring.X()` was skipped as a call into that package, whether or not the file imports it. It is skipped now only when the file imports the package; otherwise the receiver is typed like any other, and one whose type is unknown still gets no edge. - A parameter or receiver of a package-qualified type (`s *store.Store`). - A receiver bound to a call: `r := newRing()`, `s, err := store.NewStore()`, `if r := find(id); r != nil`, a call spread over several lines, a conversion (`list := model.BotList(bots)`). The type is the callee's first result as its signature spells it, read in the callee's file. Only when the call is the whole right-hand side, and not when it takes the receiver itself. A receiver whose declared type comes from a package outside the project's modules (`conn net.Conn`, `ctx context.Context`, `req *http.Request`, an alias of one, the result of an outside function) gets no edge: `ctx.Done()` went to the one project type declaring a `Done`. A project function's result declared as an outside type by value (`http.RoundTripper`) is left as it was, since that is usually an interface a project type implements. Aliases of project types are followed by colbymchenry#2417, which gave them nodes; the alias reader this change carried is gone, and an alias of an outside type is recognized from that node's declaration. Resolver only: no extraction change, the receiver scan keeps its per-line memo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1107ed0 to
3b1a538
Compare
Rebased onto main at ed199e6 and reduced again to what main still lacks. #2417 gave type aliases nodes and resolves a method through them, so the alias reader this PR carried is gone with its tests. #2410 registers the name goimports assumes for an import, so the standard-library check no longer matches on path segments. What is left is one commit.
Problem
Three receiver shapes still get no type on main, and a receiver typed outside the project still gets a guess by method name:
ring,token,parser,userandurlare on the standard-library package list, so everyring.X()is skipped as a call into that package, whether or not the file imports it.func flush(ring *ringLog) { ring.Write(b) }never resolves.s *store.Store.r := newRing(),e, err := NewEngine(),s := store.NewStore(),if r := find(id); r != nil, and a conversion such aslist := model.BotList(bots).ctx.Done()on acontext.Contextgoes to the one project type that declares aDone, andreq.WithContext(…)on an*http.Requestto a projectWithContext.Fix
Resolver only, with no extraction change. The receiver scan keeps its per-line memo.
pkg.Xreference is skipped as a standard-library call only when the file imports a package under that name. Otherwise the receiver is typed like any other. When its type can't be read it still gets no edge, as before.name *pkg.Type. The package is found through the file's imports.x[, err] := f(…)is typed from the first result in the callee's signature, read in the callee's file. The callee must be a function of the same package or of an imported project package, or a conversion to a project type. Two guards:r := newRing().Reader()is not typed bynewRing;e := wrap(e.Run())) is skipped, because on that lineeis still the outer variable.conn net.Conn,ctx context.Context,req *http.Request, an alias of one, or the result of an outside function such asnet.Dial(…). "Outside" is the same test main already applies to a type reference written through an import.type Ctx = context.Context) is read from the alias node fix(go): a type aliastype A = Bis indexed and links to the type it names #2417 added. fix(go): a type aliastype A = Bis indexed and links to the type it names #2417 leaves that case to a lookup by name; on mainctx.Done()on aCtxthen goes toCtx::Doneof another package when one declares a structCtx. The right-hand side of the declaration is read by a small helper split out ofgoAliasTarget, which is otherwise unchanged.func NewRateLimitedTransport(…) http.RoundTripper) is left as it is on main. That is usually an interface a project type implements. A pointer result (*os.File) is that outside type.Measured
goharbor/harbor at
f25e9da,src/only (1,595 Go files), indexed from scratch with builds of mained199e60and of this branch, native kernel. Counts are Gocallsedges. A call site is a file, line, column and callee name. Both builds index it identically run to run.Index time was 22–54 s for either build on a shared machine, with no difference I could separate from the noise.
Added. Typed through a call result (
mgr := config.GetCfgManager(ctx),cx, exist := security.FromContext(d.Ctx),st := job.Status(ack.Status)), a qualified parameter (ctx job.Context,userMgr pkguser.Manager,resources types.ResourceList) or a variable named like a standard-library package (parser := &license{…},context := NewSecurityContext(…); 28 of the 478).Retargeted. A guess by name moved to the method of the declared type. Examples:
logger := log.GetLogger(ctx)from a replicationLogger::Warningftolib/log's;ctx := NewSecurityContext(nil)from the v2token context to the local one;err := New(cause)from an allowlist error tolib/errors;client := registryclient.NewClient(…)from the unexportedclient::BlobExistto theClientinterface the constructor returns.Dropped. Every one of the 155 has a receiver declared with a type of a package outside the module, or bound to a call into one. The script reads the receiver's declaration and the file's imports independently of the resolver; it files 146 that way, and the other 9 were read by hand (an
io.ReadCloser, adistribution.Manifest×4, anhttp.Handler×2, and two project functions returning*redis.Pooland*sdktrace.TracerProvider). By what main linked them to:req.WithContext(…)on an*http.RequestWorker::WithContextw.WriteHeader(…)/w.Header()on anhttp.ResponseWriter, anhttptestrecorder, atar.Writerman.Payload()/man.References()on adistribution.Manifesttesting/handler.ServeHTTP(…)on anhttp.Handlerfile.Close()on an*os.File/io.ReadCloserCloseclient.Do/Get/Post/Headon an*http.Clientand similarcommon/http'sClientctx.Done()on acontext.Context,wg.Done()on async.WaitGroupserver.Client()on anhttptest.Server,err.Error(),rootRouter.Path(…)on a gorilla router, …)114 of them are concrete outside types. The other 41 are outside interfaces (
http.ResponseWriter,http.Handler,distribution.Manifest,context.Context,health.Checker,io.ReadCloser,error) that main linked by name to one project type declaring the method. Two of those are arguable:checker.Check()on ahealth.Checkerparameter (an interface ofdocker/distribution) went to the project'supdater::Check, which is what a registered checker can be at run time. The declared type is an outside interface, so the call gets no edge, the same asctx.Done().Outside
calls, 13referencesedges are gone and nothing else changed. All 13 were read: each is a field read through a receiver that is now typed (opts.IgnoreLimitation,params.WithTag,model.AuthMode,u.ReferenceID), which main linked to a package-level function of the same name.With #2368 merged in as well (the two touch the same condition; only
CHANGELOG.mdconflicts), harbor gets exactly the sum of the two changes: 25,538 → 28,537callsedges, with the same 155 dropped and 237 retargeted sites.Known limits
x := v.Method()) and a range variable are not typed.:=binding whose type can't be read does not hide an outer declaration of the same name, and a name bound in anif/forheader or a function literal is not scoped to its block. Both are as on main.req := suite.NewRequest(…)in a method with receiversuite, besideimport "github.com/stretchr/testify/suite") is read as a call into that package, so its result counts as an outside type and gets no edge. That is 12 of the dropped sites on harbor, all*http.Requestordistribution.Manifestvalues. fix(go): an unaliased import is also known by the name goimports assumes for its package #2410 lists the same shadowing for qualified references.go.modfiles. A project indexed below itsgo.mod, or with none, treats every non-relative import as outside, as main already does for type references.Tests
__tests__/go-receiver-type.test.ts, 21 cases, 16 of which fail on main at ed199e6:ifheader, a constructor of another package;*os.File;ctx.Done()on acontext.Context;ctx.Done()on an alias ofcontext.Context, with a struct of the alias's name in another package.The five controls pass on main and here:
type A = Bis indexed and links to the type it names #2417 made it.The three alias cases the previous version had (
type Context = web.Context, a grouped pointer alias, an alias embedded in a struct) pass on main since #2417 and were removed.The file passes with the native kernel and with
CODEGRAPH_KERNEL=0, together with the Go suites (go-*,goframe,rust-go-call-shape,gin-middleware-chain),resolution,function-ref,member-function-ref-owner,frameworks-integration,dead-codeand the receiver suites around them (27 files, 557 tests native; 18 files, 386 tests WASM).🤖 Generated with Claude Code