mirror of https://github.com/knative/caching.git
upgrade to latest dependencies (#906)
bumping golang.org/x/tools 2ab3b51...4d2b19f: > 4d2b19f go.mod: update golang.org/x dependencies > 6368677 gopls/internal/golang: strength reduce ComputeImportFixEdits > 777f155 gopls/internal/golang: show package attributes on hover > 8a0e08f gopls/doc: add missing doc link > 61415be gopls/internal/cache: guard against malformed paths in port.matches > 9a89d3a internal/analysisinternal: avoid sub-token spans in TypeErrorEndPos > 1115af6 internal/expect: support named arguments f(a, b, c=d, e="f") > 0b9e499 go/{expect,packages/packagestest}: mention the tag+delete proposal > efcd2bd internal/packagestest: fork go/packages/packagestest > 0e9ed3d go/packages: do not mutate Config > ca2b41b x/tools: use internal/expect instead of go/expect > b22f1ad internal/expect: fork go/expect > f1ae722 gopls/internal/semtok: change types.Named to types.Basic for iota, true, and false > dba5486 gopls: update x/telemetry to pick up fix for countertest.ReadCounter > 9d40727 gopls/internal/server: don't interact with os.UserConfigDir from tests > 59933b6 go/packages: create fewer goroutines > f1f7c26 gopls/internal/cache: ensure GO111MODULE is unset in GOPATH tests > 4f98d3f gopls/internal/golang: run testcases as subtests > 6d27bba gopls/internal/golang: add testcase handling for func with error returns > e5417d7 gopls/internal/cache: log go env in TestZeroConfigAlgorithm > 691997a gopls/internal/golang: consolidate imports from both file in qualifier > 0c792f1 gopls/internal/golang: support generating test for functions > 27e1a3a go/packages: ensure TypesInfo is set when NeedTypesInfo is enabled > 817c7bc gopls/internal/test/integration/workspace: fix TestStdWorkspace > e36459f gopls/internal/golang: generate test name for selected function/method > 2998e9a go/analysis/passes/lostcancel: add WithCancelCause et al > f0379e0 go/packages: add BenchmarkNetHTTP > ad28b93 go/packages: minor cleanups to loader.parseFiles > cceaf96 internal/imports: carve out a Source interface for index integration > f4878ba gopls/internal/golang: use correct imports in HTML pkg doc links > 109c5fc gopls/internal/test: fix path to local go in integration tests > 99e8fee x/tools: fset.File(file.Pos()) -> fset.File(file.FileStart) > 5cd08e2 go/gcexportdata: document 2 releases + tip policy > ce03cd6 internal/modindex: parse changed time in local time zone > 386503d gopls/internal/golang: add source code action for add test > 9d6e1a6 Revert "gopls/internal/analysis: disable ssa/ir analyzers on range-over-func" > f153a42 gopls: update honnef.co/go/tools (staticcheck) to v0.5.1 > 59f79bc go/ssa: speed up TestTypeparamTest by loading tests once > 70e82e0 go/analysis/passes/asmdecl: Correct identify writeResult instructions > cbd92b1 gopls/internal: stubcalledfunction: improve logic of selecting insert position > 45a28e1 all: fix x/tools tests that fail with a go1.23.1 go.work file > b0f44d5 copyright: limit copyright checking to .go files > 73d6794 gopls/internal/template: fix completion token boundary conditions > 91421d7 gopls/internal/cache: share type checking with analysis > 36684df go/analysis/passes/unusedwrite: silence if unsafe is imported > 17213ba gopls/internal/cache/parsego: support lazy object resolution for Files > 7d1d070 go/ssa/interp: disable interp tests on wasm > ae56d93 internal/modindex: implement Lookups in the index > 044b16f internal/gcimporter: extend markBlack workaround go1.22 > dabba6a internal/modindex: new API for incremental update > 7d196fc go/ssa/interp: fix failing recover2.go test > f861377 go/ssa/interp: redirect interpreter std{out,err} to testing.T.Log > 9f3c646 gopls/internal/cache: memoize cache keys > 1f162c6 gopls/internal/cache: async pull diagnostics and joined analysis > dbb8252 gopls/release: remove unused(?) script > 6176384 go/types/objectpath: break cycles through interface methods > 9e6388a internal/gcimporter: work around go/types data race in 1.23. > c457787 gopls/internal/cache: avoid reporting bugs when go/packages has errors > 401eca0 gopls/internal/settings: remove "allowImplicitNetworkAccess" > 6618569 gopls/internal/cache: refine a bug report related to package metadata > 6381f0b gopls/internal/cache: refine bug reports > 63e4449 gopls/internal/telemetry/cmd/stacks: print double-claimed stacks > f003ff6 gopls/internal/test/marker: rename s/suggestedfix/quickfix/ > 8128bcf gopls/internal/cache: add tolerance for builtin test variants > a199121 gopls: allow for asynchronous request handling > 8ecf757 internal/gcimporter: remove test of unsupported "goroot" iimport > 7310c72 importgraph: correct typo in graph_test.go > ce4cb55 internal/modindex: fix two bugs > b3482cc internal/modindex/cmd: Command for maintaining module cache indexes > 454be60 x/tools: be defensive after types.Info.Types[expr] lookups > dec6bf1 internal/modindex: update module cache index > 6c6def2 gopls/internal/telemetry/cmd/stacks: fix bad tmpreaper interaction > 1a5fe83 gopls: remove cruft > 244a31e gopls/internal: CodeAction: quickfix to generate missing method > 87d6131 internal/typeparams: support parameterized aliases in Free > aa87dcf go/analysis/passes: execute gofmt > feffeaa go/packages: report an error from Load when GOROOT is misconfigured > 50179f2 Revert "internal/aliases: add a function to conditionally enable aliases" > 4f6e118 all: set gotypesalias=1 when using >=1.23 toolchain > 915132c internal/typesinternal: add NamedOrAlias type > bd86f8c gopls/internal/cache/analysis: lazily resolve the import map > a4e0a9e cmd/bundle: enable materialized aliases > db26c69 cmd/stringer: fix test on android > f08b5c1 gopls/internal/test/integration/bench: add a pull diagnostics benchmark > bbb979f go/callgraph/vta: use node IDs for type flow graph > cf8979b gopls/doc/features: add index of supported Code Actions > 8621919 go/ssa/ssautil: disable fmt imports on wasm tests > f439874 internal/modindex: add symbol information > f21a1dc gopls: add initial support for pull diagnostics > c19060b gopls/internal/cache: use packageHandles to hold an active package cache > a30b207 internal/versions: remove InitFileVersions > de11c55 gopls/doc/codelenses: fix link typo > 0b989c8 internal/versions: update test expectations > 89a5311 go/analysis/passes/asmdecl: allow syscall write registers implicitly > f8f3c13 internal/aliases: add a function to conditionally enable aliases bumping knative.dev/pkg 47a6f9f...215048a: > 215048a Bump golang.org/x/tools from 0.26.0 to 0.27.0 (# 3114) bumping golang.org/x/mod 46a3137...dec0365: > dec0365 sumdb: make data tiles by Server compatible with sum.golang.org > c8a7319 x/mod: fix handling of vendored packages with '/vendor' in non-top-level paths > 9cd0e4c x/mod: remove vendor/modules.txt from module download Signed-off-by: Knative Automation <automation@knative.team>
This commit is contained in:
parent
5522d1afbe
commit
cb95f78b72
6
go.mod
6
go.mod
|
@ -11,7 +11,7 @@ require (
|
|||
k8s.io/code-generator v0.30.3
|
||||
k8s.io/kube-openapi v0.0.0-20240808142205-8e686545bdb8
|
||||
knative.dev/hack v0.0.0-20241106013728-b7995315deb5
|
||||
knative.dev/pkg v0.0.0-20241112094440-47a6f9fc2e7d
|
||||
knative.dev/pkg v0.0.0-20241115112044-215048add14b
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -56,7 +56,7 @@ require (
|
|||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.31.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
|
@ -64,7 +64,7 @@ require (
|
|||
golang.org/x/term v0.26.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
golang.org/x/tools v0.26.0 // indirect
|
||||
golang.org/x/tools v0.27.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||
google.golang.org/api v0.183.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
|
|
12
go.sum
12
go.sum
|
@ -352,8 +352,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
|||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -516,8 +516,8 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
|
|||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
|
||||
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -664,8 +664,8 @@ k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1
|
|||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
knative.dev/hack v0.0.0-20241106013728-b7995315deb5 h1:CfU5+6B+ylBd7mSGpvRqpzZV8H5ZQLGUwVygFzbE+1o=
|
||||
knative.dev/hack v0.0.0-20241106013728-b7995315deb5/go.mod h1:R0ritgYtjLDO9527h5vb5X6gfvt5LCrJ55BNbVDsWiY=
|
||||
knative.dev/pkg v0.0.0-20241112094440-47a6f9fc2e7d h1:8ReAstK8D5GVoIl0Sw2+ILPLcvlUfajd8rrVDdbl3n4=
|
||||
knative.dev/pkg v0.0.0-20241112094440-47a6f9fc2e7d/go.mod h1:o3BtZGGqrjjlYjW4hSI9VlzOCNb6wu3oSADAFES9wVE=
|
||||
knative.dev/pkg v0.0.0-20241115112044-215048add14b h1:9z3v+YZCexJSbhJyJknIll/B0MZA8xVRbCSgV2mc4Qc=
|
||||
knative.dev/pkg v0.0.0-20241115112044-215048add14b/go.mod h1:T6mZp4fRJ0SYIOKQXwfWmIrUNwXCymtdiDK9caKC7Eg=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
|
|
@ -344,7 +344,12 @@ func RewriteImport(fset *token.FileSet, f *ast.File, oldPath, newPath string) (r
|
|||
}
|
||||
|
||||
// UsesImport reports whether a given import is used.
|
||||
// The provided File must have been parsed with syntactic object resolution
|
||||
// (not using go/parser.SkipObjectResolution).
|
||||
func UsesImport(f *ast.File, path string) (used bool) {
|
||||
if f.Scope == nil {
|
||||
panic("file f was not parsed with syntactic object resolution")
|
||||
}
|
||||
spec := importSpec(f, path)
|
||||
if spec == nil {
|
||||
return
|
||||
|
|
|
@ -2,22 +2,64 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package gcexportdata provides functions for locating, reading, and
|
||||
// writing export data files containing type information produced by the
|
||||
// gc compiler. This package supports go1.7 export data format and all
|
||||
// later versions.
|
||||
// Package gcexportdata provides functions for reading and writing
|
||||
// export data, which is a serialized description of the API of a Go
|
||||
// package including the names, kinds, types, and locations of all
|
||||
// exported declarations.
|
||||
//
|
||||
// Although it might seem convenient for this package to live alongside
|
||||
// go/types in the standard library, this would cause version skew
|
||||
// problems for developer tools that use it, since they must be able to
|
||||
// consume the outputs of the gc compiler both before and after a Go
|
||||
// update such as from Go 1.7 to Go 1.8. Because this package lives in
|
||||
// golang.org/x/tools, sites can update their version of this repo some
|
||||
// time before the Go 1.8 release and rebuild and redeploy their
|
||||
// developer tools, which will then be able to consume both Go 1.7 and
|
||||
// Go 1.8 export data files, so they will work before and after the
|
||||
// Go update. (See discussion at https://golang.org/issue/15651.)
|
||||
package gcexportdata // import "golang.org/x/tools/go/gcexportdata"
|
||||
// The standard Go compiler (cmd/compile) writes an export data file
|
||||
// for each package it compiles, which it later reads when compiling
|
||||
// packages that import the earlier one. The compiler must thus
|
||||
// contain logic to both write and read export data.
|
||||
// (See the "Export" section in the cmd/compile/README file.)
|
||||
//
|
||||
// The [Read] function in this package can read files produced by the
|
||||
// compiler, producing [go/types] data structures. As a matter of
|
||||
// policy, Read supports export data files produced by only the last
|
||||
// two Go releases plus tip; see https://go.dev/issue/68898. The
|
||||
// export data files produced by the compiler contain additional
|
||||
// details related to generics, inlining, and other optimizations that
|
||||
// cannot be decoded by the [Read] function.
|
||||
//
|
||||
// In files written by the compiler, the export data is not at the
|
||||
// start of the file. Before calling Read, use [NewReader] to locate
|
||||
// the desired portion of the file.
|
||||
//
|
||||
// The [Write] function in this package encodes the exported API of a
|
||||
// Go package ([types.Package]) as a file. Such files can be later
|
||||
// decoded by Read, but cannot be consumed by the compiler.
|
||||
//
|
||||
// # Future changes
|
||||
//
|
||||
// Although Read supports the formats written by both Write and the
|
||||
// compiler, the two are quite different, and there is an open
|
||||
// proposal (https://go.dev/issue/69491) to separate these APIs.
|
||||
//
|
||||
// Under that proposal, this package would ultimately provide only the
|
||||
// Read operation for compiler export data, which must be defined in
|
||||
// this module (golang.org/x/tools), not in the standard library, to
|
||||
// avoid version skew for developer tools that need to read compiler
|
||||
// export data both before and after a Go release, such as from Go
|
||||
// 1.23 to Go 1.24. Because this package lives in the tools module,
|
||||
// clients can update their version of the module some time before the
|
||||
// Go 1.24 release and rebuild and redeploy their tools, which will
|
||||
// then be able to consume both Go 1.23 and Go 1.24 export data files,
|
||||
// so they will work before and after the Go update. (See discussion
|
||||
// at https://go.dev/issue/15651.)
|
||||
//
|
||||
// The operations to import and export [go/types] data structures
|
||||
// would be defined in the go/types package as Import and Export.
|
||||
// [Write] would (eventually) delegate to Export,
|
||||
// and [Read], when it detects a file produced by Export,
|
||||
// would delegate to Import.
|
||||
//
|
||||
// # Deprecations
|
||||
//
|
||||
// The [NewImporter] and [Find] functions are deprecated and should
|
||||
// not be used in new code. The [WriteBundle] and [ReadBundle]
|
||||
// functions are experimental, and there is an open proposal to
|
||||
// deprecate them (https://go.dev/issue/69573).
|
||||
package gcexportdata
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
|
@ -100,6 +142,11 @@ func readAll(r io.Reader) ([]byte, error) {
|
|||
// Read reads export data from in, decodes it, and returns type
|
||||
// information for the package.
|
||||
//
|
||||
// Read is capable of reading export data produced by [Write] at the
|
||||
// same source code version, or by the last two Go releases (plus tip)
|
||||
// of the standard Go compiler. Reading files from older compilers may
|
||||
// produce an error.
|
||||
//
|
||||
// The package path (effectively its linker symbol prefix) is
|
||||
// specified by path, since unlike the package name, this information
|
||||
// may not be recorded in the export data.
|
||||
|
@ -128,14 +175,26 @@ func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package,
|
|||
// (from "version"). Select appropriate importer.
|
||||
if len(data) > 0 {
|
||||
switch data[0] {
|
||||
case 'v', 'c', 'd': // binary, till go1.10
|
||||
case 'v', 'c', 'd':
|
||||
// binary, produced by cmd/compile till go1.10
|
||||
return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0])
|
||||
|
||||
case 'i': // indexed, till go1.19
|
||||
case 'i':
|
||||
// indexed, produced by cmd/compile till go1.19,
|
||||
// and also by [Write].
|
||||
//
|
||||
// If proposal #69491 is accepted, go/types
|
||||
// serialization will be implemented by
|
||||
// types.Export, to which Write would eventually
|
||||
// delegate (explicitly dropping any pretence at
|
||||
// inter-version Write-Read compatibility).
|
||||
// This [Read] function would delegate to types.Import
|
||||
// when it detects that the file was produced by Export.
|
||||
_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
|
||||
return pkg, err
|
||||
|
||||
case 'u': // unified, from go1.20
|
||||
case 'u':
|
||||
// unified, produced by cmd/compile since go1.20
|
||||
_, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path)
|
||||
return pkg, err
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ type DriverResponse struct {
|
|||
|
||||
// driver is the type for functions that query the build system for the
|
||||
// packages named by the patterns.
|
||||
type driver func(cfg *Config, patterns ...string) (*DriverResponse, error)
|
||||
type driver func(cfg *Config, patterns []string) (*DriverResponse, error)
|
||||
|
||||
// findExternalDriver returns the file path of a tool that supplies
|
||||
// the build system package structure, or "" if not found.
|
||||
|
@ -103,7 +103,7 @@ func findExternalDriver(cfg *Config) driver {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
return func(cfg *Config, words ...string) (*DriverResponse, error) {
|
||||
return func(cfg *Config, patterns []string) (*DriverResponse, error) {
|
||||
req, err := json.Marshal(DriverRequest{
|
||||
Mode: cfg.Mode,
|
||||
Env: cfg.Env,
|
||||
|
@ -117,7 +117,7 @@ func findExternalDriver(cfg *Config) driver {
|
|||
|
||||
buf := new(bytes.Buffer)
|
||||
stderr := new(bytes.Buffer)
|
||||
cmd := exec.CommandContext(cfg.Context, tool, words...)
|
||||
cmd := exec.CommandContext(cfg.Context, tool, patterns...)
|
||||
cmd.Dir = cfg.Dir
|
||||
// The cwd gets resolved to the real path. On Darwin, where
|
||||
// /tmp is a symlink, this breaks anything that expects the
|
||||
|
|
|
@ -80,6 +80,12 @@ type golistState struct {
|
|||
cfg *Config
|
||||
ctx context.Context
|
||||
|
||||
runner *gocommand.Runner
|
||||
|
||||
// overlay is the JSON file that encodes the Config.Overlay
|
||||
// mapping, used by 'go list -overlay=...'.
|
||||
overlay string
|
||||
|
||||
envOnce sync.Once
|
||||
goEnvError error
|
||||
goEnv map[string]string
|
||||
|
@ -127,7 +133,10 @@ func (state *golistState) mustGetEnv() map[string]string {
|
|||
// goListDriver uses the go list command to interpret the patterns and produce
|
||||
// the build system package structure.
|
||||
// See driver for more details.
|
||||
func goListDriver(cfg *Config, patterns ...string) (_ *DriverResponse, err error) {
|
||||
//
|
||||
// overlay is the JSON file that encodes the cfg.Overlay
|
||||
// mapping, used by 'go list -overlay=...'
|
||||
func goListDriver(cfg *Config, runner *gocommand.Runner, overlay string, patterns []string) (_ *DriverResponse, err error) {
|
||||
// Make sure that any asynchronous go commands are killed when we return.
|
||||
parentCtx := cfg.Context
|
||||
if parentCtx == nil {
|
||||
|
@ -142,13 +151,15 @@ func goListDriver(cfg *Config, patterns ...string) (_ *DriverResponse, err error
|
|||
cfg: cfg,
|
||||
ctx: ctx,
|
||||
vendorDirs: map[string]bool{},
|
||||
overlay: overlay,
|
||||
runner: runner,
|
||||
}
|
||||
|
||||
// Fill in response.Sizes asynchronously if necessary.
|
||||
if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
|
||||
if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&(NeedTypes|NeedTypesInfo) != 0 {
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
compiler, arch, err := getSizesForArgs(ctx, state.cfgInvocation(), cfg.gocmdRunner)
|
||||
compiler, arch, err := getSizesForArgs(ctx, state.cfgInvocation(), runner)
|
||||
response.dr.Compiler = compiler
|
||||
response.dr.Arch = arch
|
||||
errCh <- err
|
||||
|
@ -681,7 +692,7 @@ func (state *golistState) shouldAddFilenameFromError(p *jsonPackage) bool {
|
|||
// getGoVersion returns the effective minor version of the go command.
|
||||
func (state *golistState) getGoVersion() (int, error) {
|
||||
state.goVersionOnce.Do(func() {
|
||||
state.goVersion, state.goVersionError = gocommand.GoVersion(state.ctx, state.cfgInvocation(), state.cfg.gocmdRunner)
|
||||
state.goVersion, state.goVersionError = gocommand.GoVersion(state.ctx, state.cfgInvocation(), state.runner)
|
||||
})
|
||||
return state.goVersion, state.goVersionError
|
||||
}
|
||||
|
@ -751,7 +762,7 @@ func jsonFlag(cfg *Config, goVersion int) string {
|
|||
}
|
||||
}
|
||||
addFields("Name", "ImportPath", "Error") // These fields are always needed
|
||||
if cfg.Mode&NeedFiles != 0 || cfg.Mode&NeedTypes != 0 {
|
||||
if cfg.Mode&NeedFiles != 0 || cfg.Mode&(NeedTypes|NeedTypesInfo) != 0 {
|
||||
addFields("Dir", "GoFiles", "IgnoredGoFiles", "IgnoredOtherFiles", "CFiles",
|
||||
"CgoFiles", "CXXFiles", "MFiles", "HFiles", "FFiles", "SFiles",
|
||||
"SwigFiles", "SwigCXXFiles", "SysoFiles")
|
||||
|
@ -759,7 +770,7 @@ func jsonFlag(cfg *Config, goVersion int) string {
|
|||
addFields("TestGoFiles", "XTestGoFiles")
|
||||
}
|
||||
}
|
||||
if cfg.Mode&NeedTypes != 0 {
|
||||
if cfg.Mode&(NeedTypes|NeedTypesInfo) != 0 {
|
||||
// CompiledGoFiles seems to be required for the test case TestCgoNoSyntax,
|
||||
// even when -compiled isn't passed in.
|
||||
// TODO(#52435): Should we make the test ask for -compiled, or automatically
|
||||
|
@ -840,7 +851,7 @@ func (state *golistState) cfgInvocation() gocommand.Invocation {
|
|||
Env: cfg.Env,
|
||||
Logf: cfg.Logf,
|
||||
WorkingDir: cfg.Dir,
|
||||
Overlay: cfg.goListOverlayFile,
|
||||
Overlay: state.overlay,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -851,11 +862,8 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer,
|
|||
inv := state.cfgInvocation()
|
||||
inv.Verb = verb
|
||||
inv.Args = args
|
||||
gocmdRunner := cfg.gocmdRunner
|
||||
if gocmdRunner == nil {
|
||||
gocmdRunner = &gocommand.Runner{}
|
||||
}
|
||||
stdout, stderr, friendlyErr, err := gocmdRunner.RunRaw(cfg.Context, inv)
|
||||
|
||||
stdout, stderr, friendlyErr, err := state.runner.RunRaw(cfg.Context, inv)
|
||||
if err != nil {
|
||||
// Check for 'go' executable not being found.
|
||||
if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
|
||||
|
@ -879,6 +887,12 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer,
|
|||
return nil, friendlyErr
|
||||
}
|
||||
|
||||
// Return an error if 'go list' failed due to missing tools in
|
||||
// $GOROOT/pkg/tool/$GOOS_$GOARCH (#69606).
|
||||
if len(stderr.String()) > 0 && strings.Contains(stderr.String(), `go: no such tool`) {
|
||||
return nil, friendlyErr
|
||||
}
|
||||
|
||||
// Is there an error running the C compiler in cgo? This will be reported in the "Error" field
|
||||
// and should be suppressed by go list -e.
|
||||
//
|
||||
|
|
|
@ -16,13 +16,13 @@ import (
|
|||
"go/scanner"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
@ -31,7 +31,6 @@ import (
|
|||
"golang.org/x/tools/internal/gocommand"
|
||||
"golang.org/x/tools/internal/packagesinternal"
|
||||
"golang.org/x/tools/internal/typesinternal"
|
||||
"golang.org/x/tools/internal/versions"
|
||||
)
|
||||
|
||||
// A LoadMode controls the amount of detail to return when loading.
|
||||
|
@ -56,7 +55,7 @@ const (
|
|||
// NeedName adds Name and PkgPath.
|
||||
NeedName LoadMode = 1 << iota
|
||||
|
||||
// NeedFiles adds GoFiles and OtherFiles.
|
||||
// NeedFiles adds GoFiles, OtherFiles, and IgnoredFiles
|
||||
NeedFiles
|
||||
|
||||
// NeedCompiledGoFiles adds CompiledGoFiles.
|
||||
|
@ -78,7 +77,7 @@ const (
|
|||
// NeedSyntax adds Syntax and Fset.
|
||||
NeedSyntax
|
||||
|
||||
// NeedTypesInfo adds TypesInfo.
|
||||
// NeedTypesInfo adds TypesInfo and Fset.
|
||||
NeedTypesInfo
|
||||
|
||||
// NeedTypesSizes adds TypesSizes.
|
||||
|
@ -145,13 +144,7 @@ const (
|
|||
// A Config specifies details about how packages should be loaded.
|
||||
// The zero value is a valid configuration.
|
||||
//
|
||||
// Calls to Load do not modify this struct.
|
||||
//
|
||||
// TODO(adonovan): #67702: this is currently false: in fact,
|
||||
// calls to [Load] do not modify the public fields of this struct, but
|
||||
// may modify hidden fields, so concurrent calls to [Load] must not
|
||||
// use the same Config. But perhaps we should reestablish the
|
||||
// documented invariant.
|
||||
// Calls to [Load] do not modify this struct.
|
||||
type Config struct {
|
||||
// Mode controls the level of information returned for each package.
|
||||
Mode LoadMode
|
||||
|
@ -182,19 +175,10 @@ type Config struct {
|
|||
//
|
||||
Env []string
|
||||
|
||||
// gocmdRunner guards go command calls from concurrency errors.
|
||||
gocmdRunner *gocommand.Runner
|
||||
|
||||
// BuildFlags is a list of command-line flags to be passed through to
|
||||
// the build system's query tool.
|
||||
BuildFlags []string
|
||||
|
||||
// modFile will be used for -modfile in go command invocations.
|
||||
modFile string
|
||||
|
||||
// modFlag will be used for -modfile in go command invocations.
|
||||
modFlag string
|
||||
|
||||
// Fset provides source position information for syntax trees and types.
|
||||
// If Fset is nil, Load will use a new fileset, but preserve Fset's value.
|
||||
Fset *token.FileSet
|
||||
|
@ -241,9 +225,13 @@ type Config struct {
|
|||
// drivers may vary in their level of support for overlays.
|
||||
Overlay map[string][]byte
|
||||
|
||||
// goListOverlayFile is the JSON file that encodes the Overlay
|
||||
// mapping, used by 'go list -overlay=...'
|
||||
goListOverlayFile string
|
||||
// -- Hidden configuration fields only for use in x/tools --
|
||||
|
||||
// modFile will be used for -modfile in go command invocations.
|
||||
modFile string
|
||||
|
||||
// modFlag will be used for -modfile in go command invocations.
|
||||
modFlag string
|
||||
}
|
||||
|
||||
// Load loads and returns the Go packages named by the given patterns.
|
||||
|
@ -334,21 +322,24 @@ func defaultDriver(cfg *Config, patterns ...string) (*DriverResponse, bool, erro
|
|||
} else if !response.NotHandled {
|
||||
return response, true, nil
|
||||
}
|
||||
// (fall through)
|
||||
// not handled: fall through
|
||||
}
|
||||
|
||||
// go list fallback
|
||||
//
|
||||
|
||||
// Write overlays once, as there are many calls
|
||||
// to 'go list' (one per chunk plus others too).
|
||||
overlay, cleanupOverlay, err := gocommand.WriteOverlays(cfg.Overlay)
|
||||
overlayFile, cleanupOverlay, err := gocommand.WriteOverlays(cfg.Overlay)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
defer cleanupOverlay()
|
||||
cfg.goListOverlayFile = overlay
|
||||
|
||||
response, err := callDriverOnChunks(goListDriver, cfg, chunks)
|
||||
var runner gocommand.Runner // (shared across many 'go list' calls)
|
||||
driver := func(cfg *Config, patterns []string) (*DriverResponse, error) {
|
||||
return goListDriver(cfg, &runner, overlayFile, patterns)
|
||||
}
|
||||
response, err := callDriverOnChunks(driver, cfg, chunks)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
@ -386,16 +377,14 @@ func splitIntoChunks(patterns []string, argMax int) ([][]string, error) {
|
|||
|
||||
func callDriverOnChunks(driver driver, cfg *Config, chunks [][]string) (*DriverResponse, error) {
|
||||
if len(chunks) == 0 {
|
||||
return driver(cfg)
|
||||
return driver(cfg, nil)
|
||||
}
|
||||
responses := make([]*DriverResponse, len(chunks))
|
||||
errNotHandled := errors.New("driver returned NotHandled")
|
||||
var g errgroup.Group
|
||||
for i, chunk := range chunks {
|
||||
i := i
|
||||
chunk := chunk
|
||||
g.Go(func() (err error) {
|
||||
responses[i], err = driver(cfg, chunk...)
|
||||
responses[i], err = driver(cfg, chunk)
|
||||
if responses[i] != nil && responses[i].NotHandled {
|
||||
err = errNotHandled
|
||||
}
|
||||
|
@ -692,18 +681,19 @@ func (p *Package) String() string { return p.ID }
|
|||
// loaderPackage augments Package with state used during the loading phase
|
||||
type loaderPackage struct {
|
||||
*Package
|
||||
importErrors map[string]error // maps each bad import to its error
|
||||
loadOnce sync.Once
|
||||
color uint8 // for cycle detection
|
||||
needsrc bool // load from source (Mode >= LoadTypes)
|
||||
needtypes bool // type information is either requested or depended on
|
||||
initial bool // package was matched by a pattern
|
||||
goVersion int // minor version number of go command on PATH
|
||||
importErrors map[string]error // maps each bad import to its error
|
||||
preds []*loaderPackage // packages that import this one
|
||||
unfinishedSuccs atomic.Int32 // number of direct imports not yet loaded
|
||||
color uint8 // for cycle detection
|
||||
needsrc bool // load from source (Mode >= LoadTypes)
|
||||
needtypes bool // type information is either requested or depended on
|
||||
initial bool // package was matched by a pattern
|
||||
goVersion int // minor version number of go command on PATH
|
||||
}
|
||||
|
||||
// loader holds the working state of a single call to load.
|
||||
type loader struct {
|
||||
pkgs map[string]*loaderPackage
|
||||
pkgs map[string]*loaderPackage // keyed by Package.ID
|
||||
Config
|
||||
sizes types.Sizes // non-nil if needed by mode
|
||||
parseCache map[string]*parseValue
|
||||
|
@ -749,9 +739,6 @@ func newLoader(cfg *Config) *loader {
|
|||
if ld.Config.Env == nil {
|
||||
ld.Config.Env = os.Environ()
|
||||
}
|
||||
if ld.Config.gocmdRunner == nil {
|
||||
ld.Config.gocmdRunner = &gocommand.Runner{}
|
||||
}
|
||||
if ld.Context == nil {
|
||||
ld.Context = context.Background()
|
||||
}
|
||||
|
@ -765,7 +752,7 @@ func newLoader(cfg *Config) *loader {
|
|||
ld.requestedMode = ld.Mode
|
||||
ld.Mode = impliedLoadMode(ld.Mode)
|
||||
|
||||
if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 {
|
||||
if ld.Mode&(NeedSyntax|NeedTypes|NeedTypesInfo) != 0 {
|
||||
if ld.Fset == nil {
|
||||
ld.Fset = token.NewFileSet()
|
||||
}
|
||||
|
@ -806,7 +793,7 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
|
|||
exportDataInvalid := len(ld.Overlay) > 0 || pkg.ExportFile == "" && pkg.PkgPath != "unsafe"
|
||||
// This package needs type information if the caller requested types and the package is
|
||||
// either a root, or it's a non-root and the user requested dependencies ...
|
||||
needtypes := (ld.Mode&NeedTypes|NeedTypesInfo != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0))
|
||||
needtypes := (ld.Mode&(NeedTypes|NeedTypesInfo) != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0))
|
||||
// This package needs source if the call requested source (or types info, which implies source)
|
||||
// and the package is either a root, or itas a non- root and the user requested dependencies...
|
||||
needsrc := ((ld.Mode&(NeedSyntax|NeedTypesInfo) != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0)) ||
|
||||
|
@ -831,9 +818,10 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if ld.Mode&NeedImports != 0 {
|
||||
// Materialize the import graph.
|
||||
|
||||
// Materialize the import graph if it is needed (NeedImports),
|
||||
// or if we'll be using loadPackages (Need{Syntax|Types|TypesInfo}).
|
||||
var leaves []*loaderPackage // packages with no unfinished successors
|
||||
if ld.Mode&(NeedImports|NeedSyntax|NeedTypes|NeedTypesInfo) != 0 {
|
||||
const (
|
||||
white = 0 // new
|
||||
grey = 1 // in progress
|
||||
|
@ -852,63 +840,76 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
|
|||
// dependency on a package that does. These are the only packages
|
||||
// for which we load source code.
|
||||
var stack []*loaderPackage
|
||||
var visit func(lpkg *loaderPackage) bool
|
||||
visit = func(lpkg *loaderPackage) bool {
|
||||
switch lpkg.color {
|
||||
case black:
|
||||
return lpkg.needsrc
|
||||
case grey:
|
||||
var visit func(from, lpkg *loaderPackage) bool
|
||||
visit = func(from, lpkg *loaderPackage) bool {
|
||||
if lpkg.color == grey {
|
||||
panic("internal error: grey node")
|
||||
}
|
||||
lpkg.color = grey
|
||||
stack = append(stack, lpkg) // push
|
||||
stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports
|
||||
lpkg.Imports = make(map[string]*Package, len(stubs))
|
||||
for importPath, ipkg := range stubs {
|
||||
var importErr error
|
||||
imp := ld.pkgs[ipkg.ID]
|
||||
if imp == nil {
|
||||
// (includes package "C" when DisableCgo)
|
||||
importErr = fmt.Errorf("missing package: %q", ipkg.ID)
|
||||
} else if imp.color == grey {
|
||||
importErr = fmt.Errorf("import cycle: %s", stack)
|
||||
}
|
||||
if importErr != nil {
|
||||
if lpkg.importErrors == nil {
|
||||
lpkg.importErrors = make(map[string]error)
|
||||
if lpkg.color == white {
|
||||
lpkg.color = grey
|
||||
stack = append(stack, lpkg) // push
|
||||
stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports
|
||||
lpkg.Imports = make(map[string]*Package, len(stubs))
|
||||
for importPath, ipkg := range stubs {
|
||||
var importErr error
|
||||
imp := ld.pkgs[ipkg.ID]
|
||||
if imp == nil {
|
||||
// (includes package "C" when DisableCgo)
|
||||
importErr = fmt.Errorf("missing package: %q", ipkg.ID)
|
||||
} else if imp.color == grey {
|
||||
importErr = fmt.Errorf("import cycle: %s", stack)
|
||||
}
|
||||
lpkg.importErrors[importPath] = importErr
|
||||
continue
|
||||
if importErr != nil {
|
||||
if lpkg.importErrors == nil {
|
||||
lpkg.importErrors = make(map[string]error)
|
||||
}
|
||||
lpkg.importErrors[importPath] = importErr
|
||||
continue
|
||||
}
|
||||
|
||||
if visit(lpkg, imp) {
|
||||
lpkg.needsrc = true
|
||||
}
|
||||
lpkg.Imports[importPath] = imp.Package
|
||||
}
|
||||
|
||||
if visit(imp) {
|
||||
lpkg.needsrc = true
|
||||
// -- postorder --
|
||||
|
||||
// Complete type information is required for the
|
||||
// immediate dependencies of each source package.
|
||||
if lpkg.needsrc && ld.Mode&NeedTypes != 0 {
|
||||
for _, ipkg := range lpkg.Imports {
|
||||
ld.pkgs[ipkg.ID].needtypes = true
|
||||
}
|
||||
}
|
||||
lpkg.Imports[importPath] = imp.Package
|
||||
|
||||
// NeedTypeSizes causes TypeSizes to be set even
|
||||
// on packages for which types aren't needed.
|
||||
if ld.Mode&NeedTypesSizes != 0 {
|
||||
lpkg.TypesSizes = ld.sizes
|
||||
}
|
||||
|
||||
// Add packages with no imports directly to the queue of leaves.
|
||||
if len(lpkg.Imports) == 0 {
|
||||
leaves = append(leaves, lpkg)
|
||||
}
|
||||
|
||||
stack = stack[:len(stack)-1] // pop
|
||||
lpkg.color = black
|
||||
}
|
||||
|
||||
// Complete type information is required for the
|
||||
// immediate dependencies of each source package.
|
||||
if lpkg.needsrc && ld.Mode&NeedTypes != 0 {
|
||||
for _, ipkg := range lpkg.Imports {
|
||||
ld.pkgs[ipkg.ID].needtypes = true
|
||||
}
|
||||
// Add edge from predecessor.
|
||||
if from != nil {
|
||||
from.unfinishedSuccs.Add(+1) // incref
|
||||
lpkg.preds = append(lpkg.preds, from)
|
||||
}
|
||||
|
||||
// NeedTypeSizes causes TypeSizes to be set even
|
||||
// on packages for which types aren't needed.
|
||||
if ld.Mode&NeedTypesSizes != 0 {
|
||||
lpkg.TypesSizes = ld.sizes
|
||||
}
|
||||
stack = stack[:len(stack)-1] // pop
|
||||
lpkg.color = black
|
||||
|
||||
return lpkg.needsrc
|
||||
}
|
||||
|
||||
// For each initial package, create its import DAG.
|
||||
for _, lpkg := range initial {
|
||||
visit(lpkg)
|
||||
visit(nil, lpkg)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -921,16 +922,45 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
|
|||
|
||||
// Load type data and syntax if needed, starting at
|
||||
// the initial packages (roots of the import DAG).
|
||||
if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 {
|
||||
var wg sync.WaitGroup
|
||||
for _, lpkg := range initial {
|
||||
wg.Add(1)
|
||||
go func(lpkg *loaderPackage) {
|
||||
ld.loadRecursive(lpkg)
|
||||
wg.Done()
|
||||
}(lpkg)
|
||||
if ld.Mode&(NeedSyntax|NeedTypes|NeedTypesInfo) != 0 {
|
||||
|
||||
// We avoid using g.SetLimit to limit concurrency as
|
||||
// it makes g.Go stop accepting work, which prevents
|
||||
// workers from enqeuing, and thus finishing, and thus
|
||||
// allowing the group to make progress: deadlock.
|
||||
//
|
||||
// Instead we use the ioLimit and cpuLimit semaphores.
|
||||
g, _ := errgroup.WithContext(ld.Context)
|
||||
|
||||
// enqueues adds a package to the type-checking queue.
|
||||
// It must have no unfinished successors.
|
||||
var enqueue func(*loaderPackage)
|
||||
enqueue = func(lpkg *loaderPackage) {
|
||||
g.Go(func() error {
|
||||
// Parse and type-check.
|
||||
ld.loadPackage(lpkg)
|
||||
|
||||
// Notify each waiting predecessor,
|
||||
// and enqueue it when it becomes a leaf.
|
||||
for _, pred := range lpkg.preds {
|
||||
if pred.unfinishedSuccs.Add(-1) == 0 { // decref
|
||||
enqueue(pred)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Load leaves first, adding new packages
|
||||
// to the queue as they become leaves.
|
||||
for _, leaf := range leaves {
|
||||
enqueue(leaf)
|
||||
}
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err // cancelled
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// If the context is done, return its error and
|
||||
|
@ -977,7 +1007,7 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
|
|||
if ld.requestedMode&NeedSyntax == 0 {
|
||||
ld.pkgs[i].Syntax = nil
|
||||
}
|
||||
if ld.requestedMode&NeedTypes == 0 && ld.requestedMode&NeedSyntax == 0 {
|
||||
if ld.requestedMode&(NeedSyntax|NeedTypes|NeedTypesInfo) == 0 {
|
||||
ld.pkgs[i].Fset = nil
|
||||
}
|
||||
if ld.requestedMode&NeedTypesInfo == 0 {
|
||||
|
@ -994,31 +1024,10 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
|
|||
return result, nil
|
||||
}
|
||||
|
||||
// loadRecursive loads the specified package and its dependencies,
|
||||
// recursively, in parallel, in topological order.
|
||||
// It is atomic and idempotent.
|
||||
// Precondition: ld.Mode&NeedTypes.
|
||||
func (ld *loader) loadRecursive(lpkg *loaderPackage) {
|
||||
lpkg.loadOnce.Do(func() {
|
||||
// Load the direct dependencies, in parallel.
|
||||
var wg sync.WaitGroup
|
||||
for _, ipkg := range lpkg.Imports {
|
||||
imp := ld.pkgs[ipkg.ID]
|
||||
wg.Add(1)
|
||||
go func(imp *loaderPackage) {
|
||||
ld.loadRecursive(imp)
|
||||
wg.Done()
|
||||
}(imp)
|
||||
}
|
||||
wg.Wait()
|
||||
ld.loadPackage(lpkg)
|
||||
})
|
||||
}
|
||||
|
||||
// loadPackage loads the specified package.
|
||||
// loadPackage loads/parses/typechecks the specified package.
|
||||
// It must be called only once per Package,
|
||||
// after immediate dependencies are loaded.
|
||||
// Precondition: ld.Mode & NeedTypes.
|
||||
// Precondition: ld.Mode&(NeedSyntax|NeedTypes|NeedTypesInfo) != 0.
|
||||
func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
||||
if lpkg.PkgPath == "unsafe" {
|
||||
// Fill in the blanks to avoid surprises.
|
||||
|
@ -1054,6 +1063,10 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
|||
if !lpkg.needtypes && !lpkg.needsrc {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(adonovan): this condition looks wrong:
|
||||
// I think it should be lpkg.needtypes && !lpg.needsrc,
|
||||
// so that NeedSyntax without NeedTypes can be satisfied by export data.
|
||||
if !lpkg.needsrc {
|
||||
if err := ld.loadFromExportData(lpkg); err != nil {
|
||||
lpkg.Errors = append(lpkg.Errors, Error{
|
||||
|
@ -1159,7 +1172,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
|||
}
|
||||
|
||||
lpkg.Syntax = files
|
||||
if ld.Config.Mode&NeedTypes == 0 {
|
||||
if ld.Config.Mode&(NeedTypes|NeedTypesInfo) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1170,16 +1183,20 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
|||
return
|
||||
}
|
||||
|
||||
lpkg.TypesInfo = &types.Info{
|
||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||
Defs: make(map[*ast.Ident]types.Object),
|
||||
Uses: make(map[*ast.Ident]types.Object),
|
||||
Implicits: make(map[ast.Node]types.Object),
|
||||
Instances: make(map[*ast.Ident]types.Instance),
|
||||
Scopes: make(map[ast.Node]*types.Scope),
|
||||
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
||||
// Populate TypesInfo only if needed, as it
|
||||
// causes the type checker to work much harder.
|
||||
if ld.Config.Mode&NeedTypesInfo != 0 {
|
||||
lpkg.TypesInfo = &types.Info{
|
||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||
Defs: make(map[*ast.Ident]types.Object),
|
||||
Uses: make(map[*ast.Ident]types.Object),
|
||||
Implicits: make(map[ast.Node]types.Object),
|
||||
Instances: make(map[*ast.Ident]types.Instance),
|
||||
Scopes: make(map[ast.Node]*types.Scope),
|
||||
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
||||
FileVersions: make(map[*ast.File]string),
|
||||
}
|
||||
}
|
||||
versions.InitFileVersions(lpkg.TypesInfo)
|
||||
lpkg.TypesSizes = ld.sizes
|
||||
|
||||
importer := importerFunc(func(path string) (*types.Package, error) {
|
||||
|
@ -1232,6 +1249,10 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
|||
}
|
||||
}
|
||||
|
||||
// Type-checking is CPU intensive.
|
||||
cpuLimit <- unit{} // acquire a token
|
||||
defer func() { <-cpuLimit }() // release a token
|
||||
|
||||
typErr := types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
|
||||
lpkg.importErrors = nil // no longer needed
|
||||
|
||||
|
@ -1296,8 +1317,11 @@ type importerFunc func(path string) (*types.Package, error)
|
|||
func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
|
||||
|
||||
// We use a counting semaphore to limit
|
||||
// the number of parallel I/O calls per process.
|
||||
var ioLimit = make(chan bool, 20)
|
||||
// the number of parallel I/O calls or CPU threads per process.
|
||||
var (
|
||||
ioLimit = make(chan unit, 20)
|
||||
cpuLimit = make(chan unit, runtime.GOMAXPROCS(0))
|
||||
)
|
||||
|
||||
func (ld *loader) parseFile(filename string) (*ast.File, error) {
|
||||
ld.parseCacheMu.Lock()
|
||||
|
@ -1314,20 +1338,28 @@ func (ld *loader) parseFile(filename string) (*ast.File, error) {
|
|||
|
||||
var src []byte
|
||||
for f, contents := range ld.Config.Overlay {
|
||||
// TODO(adonovan): Inefficient for large overlays.
|
||||
// Do an exact name-based map lookup
|
||||
// (for nonexistent files) followed by a
|
||||
// FileID-based map lookup (for existing ones).
|
||||
if sameFile(f, filename) {
|
||||
src = contents
|
||||
break
|
||||
}
|
||||
}
|
||||
var err error
|
||||
if src == nil {
|
||||
ioLimit <- true // wait
|
||||
ioLimit <- unit{} // acquire a token
|
||||
src, err = os.ReadFile(filename)
|
||||
<-ioLimit // signal
|
||||
<-ioLimit // release a token
|
||||
}
|
||||
if err != nil {
|
||||
v.err = err
|
||||
} else {
|
||||
// Parsing is CPU intensive.
|
||||
cpuLimit <- unit{} // acquire a token
|
||||
v.f, v.err = ld.ParseFile(ld.Fset, filename, src)
|
||||
<-cpuLimit // release a token
|
||||
}
|
||||
|
||||
close(v.ready)
|
||||
|
@ -1342,18 +1374,21 @@ func (ld *loader) parseFile(filename string) (*ast.File, error) {
|
|||
// Because files are scanned in parallel, the token.Pos
|
||||
// positions of the resulting ast.Files are not ordered.
|
||||
func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) {
|
||||
var wg sync.WaitGroup
|
||||
n := len(filenames)
|
||||
parsed := make([]*ast.File, n)
|
||||
errors := make([]error, n)
|
||||
for i, file := range filenames {
|
||||
wg.Add(1)
|
||||
go func(i int, filename string) {
|
||||
var (
|
||||
n = len(filenames)
|
||||
parsed = make([]*ast.File, n)
|
||||
errors = make([]error, n)
|
||||
)
|
||||
var g errgroup.Group
|
||||
for i, filename := range filenames {
|
||||
// This creates goroutines unnecessarily in the
|
||||
// cache-hit case, but that case is uncommon.
|
||||
g.Go(func() error {
|
||||
parsed[i], errors[i] = ld.parseFile(filename)
|
||||
wg.Done()
|
||||
}(i, file)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
wg.Wait()
|
||||
g.Wait()
|
||||
|
||||
// Eliminate nils, preserving order.
|
||||
var o int
|
||||
|
@ -1524,4 +1559,4 @@ func usesExportData(cfg *Config) bool {
|
|||
return cfg.Mode&NeedExportFile != 0 || cfg.Mode&NeedTypes != 0 && cfg.Mode&NeedDeps == 0
|
||||
}
|
||||
|
||||
var _ interface{} = io.Discard // assert build toolchain is go1.16 or later
|
||||
type unit struct{}
|
||||
|
|
|
@ -281,25 +281,25 @@ func (enc *Encoder) For(obj types.Object) (Path, error) {
|
|||
|
||||
T := o.Type()
|
||||
if alias, ok := T.(*types.Alias); ok {
|
||||
if r := findTypeParam(obj, aliases.TypeParams(alias), path, opTypeParam, nil); r != nil {
|
||||
if r := findTypeParam(obj, aliases.TypeParams(alias), path, opTypeParam); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
if r := find(obj, aliases.Rhs(alias), append(path, opRhs), nil); r != nil {
|
||||
if r := find(obj, aliases.Rhs(alias), append(path, opRhs)); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
|
||||
} else if tname.IsAlias() {
|
||||
// legacy alias
|
||||
if r := find(obj, T, path, nil); r != nil {
|
||||
if r := find(obj, T, path); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
|
||||
} else if named, ok := T.(*types.Named); ok {
|
||||
// defined (named) type
|
||||
if r := findTypeParam(obj, named.TypeParams(), path, opTypeParam, nil); r != nil {
|
||||
if r := findTypeParam(obj, named.TypeParams(), path, opTypeParam); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
if r := find(obj, named.Underlying(), append(path, opUnderlying), nil); r != nil {
|
||||
if r := find(obj, named.Underlying(), append(path, opUnderlying)); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
}
|
||||
|
@ -312,7 +312,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) {
|
|||
if _, ok := o.(*types.TypeName); !ok {
|
||||
if o.Exported() {
|
||||
// exported non-type (const, var, func)
|
||||
if r := find(obj, o.Type(), append(path, opType), nil); r != nil {
|
||||
if r := find(obj, o.Type(), append(path, opType)); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
}
|
||||
|
@ -332,7 +332,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) {
|
|||
if m == obj {
|
||||
return Path(path2), nil // found declared method
|
||||
}
|
||||
if r := find(obj, m.Type(), append(path2, opType), nil); r != nil {
|
||||
if r := find(obj, m.Type(), append(path2, opType)); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
}
|
||||
|
@ -447,46 +447,64 @@ func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
|
|||
//
|
||||
// The seen map is used to short circuit cycles through type parameters. If
|
||||
// nil, it will be allocated as necessary.
|
||||
func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte {
|
||||
//
|
||||
// The seenMethods map is used internally to short circuit cycles through
|
||||
// interface methods, such as occur in the following example:
|
||||
//
|
||||
// type I interface { f() interface{I} }
|
||||
//
|
||||
// See golang/go#68046 for details.
|
||||
func find(obj types.Object, T types.Type, path []byte) []byte {
|
||||
return (&finder{obj: obj}).find(T, path)
|
||||
}
|
||||
|
||||
// finder closes over search state for a call to find.
|
||||
type finder struct {
|
||||
obj types.Object // the sought object
|
||||
seenTParamNames map[*types.TypeName]bool // for cycle breaking through type parameters
|
||||
seenMethods map[*types.Func]bool // for cycle breaking through recursive interfaces
|
||||
}
|
||||
|
||||
func (f *finder) find(T types.Type, path []byte) []byte {
|
||||
switch T := T.(type) {
|
||||
case *types.Alias:
|
||||
return find(obj, types.Unalias(T), path, seen)
|
||||
return f.find(types.Unalias(T), path)
|
||||
case *types.Basic, *types.Named:
|
||||
// Named types belonging to pkg were handled already,
|
||||
// so T must belong to another package. No path.
|
||||
return nil
|
||||
case *types.Pointer:
|
||||
return find(obj, T.Elem(), append(path, opElem), seen)
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Slice:
|
||||
return find(obj, T.Elem(), append(path, opElem), seen)
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Array:
|
||||
return find(obj, T.Elem(), append(path, opElem), seen)
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Chan:
|
||||
return find(obj, T.Elem(), append(path, opElem), seen)
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Map:
|
||||
if r := find(obj, T.Key(), append(path, opKey), seen); r != nil {
|
||||
if r := f.find(T.Key(), append(path, opKey)); r != nil {
|
||||
return r
|
||||
}
|
||||
return find(obj, T.Elem(), append(path, opElem), seen)
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Signature:
|
||||
if r := findTypeParam(obj, T.RecvTypeParams(), path, opRecvTypeParam, nil); r != nil {
|
||||
if r := f.findTypeParam(T.RecvTypeParams(), path, opRecvTypeParam); r != nil {
|
||||
return r
|
||||
}
|
||||
if r := findTypeParam(obj, T.TypeParams(), path, opTypeParam, seen); r != nil {
|
||||
if r := f.findTypeParam(T.TypeParams(), path, opTypeParam); r != nil {
|
||||
return r
|
||||
}
|
||||
if r := find(obj, T.Params(), append(path, opParams), seen); r != nil {
|
||||
if r := f.find(T.Params(), append(path, opParams)); r != nil {
|
||||
return r
|
||||
}
|
||||
return find(obj, T.Results(), append(path, opResults), seen)
|
||||
return f.find(T.Results(), append(path, opResults))
|
||||
case *types.Struct:
|
||||
for i := 0; i < T.NumFields(); i++ {
|
||||
fld := T.Field(i)
|
||||
path2 := appendOpArg(path, opField, i)
|
||||
if fld == obj {
|
||||
if fld == f.obj {
|
||||
return path2 // found field var
|
||||
}
|
||||
if r := find(obj, fld.Type(), append(path2, opType), seen); r != nil {
|
||||
if r := f.find(fld.Type(), append(path2, opType)); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
@ -495,10 +513,10 @@ func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]
|
|||
for i := 0; i < T.Len(); i++ {
|
||||
v := T.At(i)
|
||||
path2 := appendOpArg(path, opAt, i)
|
||||
if v == obj {
|
||||
if v == f.obj {
|
||||
return path2 // found param/result var
|
||||
}
|
||||
if r := find(obj, v.Type(), append(path2, opType), seen); r != nil {
|
||||
if r := f.find(v.Type(), append(path2, opType)); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
@ -506,28 +524,35 @@ func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]
|
|||
case *types.Interface:
|
||||
for i := 0; i < T.NumMethods(); i++ {
|
||||
m := T.Method(i)
|
||||
if f.seenMethods[m] {
|
||||
return nil
|
||||
}
|
||||
path2 := appendOpArg(path, opMethod, i)
|
||||
if m == obj {
|
||||
if m == f.obj {
|
||||
return path2 // found interface method
|
||||
}
|
||||
if r := find(obj, m.Type(), append(path2, opType), seen); r != nil {
|
||||
if f.seenMethods == nil {
|
||||
f.seenMethods = make(map[*types.Func]bool)
|
||||
}
|
||||
f.seenMethods[m] = true
|
||||
if r := f.find(m.Type(), append(path2, opType)); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case *types.TypeParam:
|
||||
name := T.Obj()
|
||||
if name == obj {
|
||||
return append(path, opObj)
|
||||
}
|
||||
if seen[name] {
|
||||
if f.seenTParamNames[name] {
|
||||
return nil
|
||||
}
|
||||
if seen == nil {
|
||||
seen = make(map[*types.TypeName]bool)
|
||||
if name == f.obj {
|
||||
return append(path, opObj)
|
||||
}
|
||||
seen[name] = true
|
||||
if r := find(obj, T.Constraint(), append(path, opConstraint), seen); r != nil {
|
||||
if f.seenTParamNames == nil {
|
||||
f.seenTParamNames = make(map[*types.TypeName]bool)
|
||||
}
|
||||
f.seenTParamNames[name] = true
|
||||
if r := f.find(T.Constraint(), append(path, opConstraint)); r != nil {
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
|
@ -535,11 +560,15 @@ func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]
|
|||
panic(T)
|
||||
}
|
||||
|
||||
func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, op byte, seen map[*types.TypeName]bool) []byte {
|
||||
func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, op byte) []byte {
|
||||
return (&finder{obj: obj}).findTypeParam(list, path, op)
|
||||
}
|
||||
|
||||
func (f *finder) findTypeParam(list *types.TypeParamList, path []byte, op byte) []byte {
|
||||
for i := 0; i < list.Len(); i++ {
|
||||
tparam := list.At(i)
|
||||
path2 := appendOpArg(path, op, i)
|
||||
if r := find(obj, tparam, path2, seen); r != nil {
|
||||
if r := f.find(tparam, path2); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
|
|
@ -246,6 +246,26 @@ import (
|
|||
|
||||
// IExportShallow encodes "shallow" export data for the specified package.
|
||||
//
|
||||
// For types, we use "shallow" export data. Historically, the Go
|
||||
// compiler always produced a summary of the types for a given package
|
||||
// that included types from other packages that it indirectly
|
||||
// referenced: "deep" export data. This had the advantage that the
|
||||
// compiler (and analogous tools such as gopls) need only load one
|
||||
// file per direct import. However, it meant that the files tended to
|
||||
// get larger based on the level of the package in the import
|
||||
// graph. For example, higher-level packages in the kubernetes module
|
||||
// have over 1MB of "deep" export data, even when they have almost no
|
||||
// content of their own, merely because they mention a major type that
|
||||
// references many others. In pathological cases the export data was
|
||||
// 300x larger than the source for a package due to this quadratic
|
||||
// growth.
|
||||
//
|
||||
// "Shallow" export data means that the serialized types describe only
|
||||
// a single package. If those types mention types from other packages,
|
||||
// the type checker may need to request additional packages beyond
|
||||
// just the direct imports. Type information for the entire transitive
|
||||
// closure of imports is provided (lazily) by the DAG.
|
||||
//
|
||||
// No promises are made about the encoding other than that it can be decoded by
|
||||
// the same version of IIExportShallow. If you plan to save export data in the
|
||||
// file system, be sure to include a cryptographic digest of the executable in
|
||||
|
@ -268,8 +288,8 @@ func IExportShallow(fset *token.FileSet, pkg *types.Package, reportf ReportFunc)
|
|||
}
|
||||
|
||||
// IImportShallow decodes "shallow" types.Package data encoded by
|
||||
// IExportShallow in the same executable. This function cannot import data from
|
||||
// cmd/compile or gcexportdata.Write.
|
||||
// [IExportShallow] in the same executable. This function cannot import data
|
||||
// from cmd/compile or gcexportdata.Write.
|
||||
//
|
||||
// The importer calls getPackages to obtain package symbols for all
|
||||
// packages mentioned in the export data, including the one being
|
||||
|
|
|
@ -558,6 +558,14 @@ type importReader struct {
|
|||
prevColumn int64
|
||||
}
|
||||
|
||||
// markBlack is redefined in iimport_go123.go, to work around golang/go#69912.
|
||||
//
|
||||
// If TypeNames are not marked black (in the sense of go/types cycle
|
||||
// detection), they may be mutated when dot-imported. Fix this by punching a
|
||||
// hole through the type, when compiling with Go 1.23. (The bug has been fixed
|
||||
// for 1.24, but the fix was not worth back-porting).
|
||||
var markBlack = func(name *types.TypeName) {}
|
||||
|
||||
func (r *importReader) obj(name string) {
|
||||
tag := r.byte()
|
||||
pos := r.pos()
|
||||
|
@ -570,6 +578,7 @@ func (r *importReader) obj(name string) {
|
|||
}
|
||||
typ := r.typ()
|
||||
obj := aliases.NewAlias(r.p.aliases, pos, r.currPkg, name, typ, tparams)
|
||||
markBlack(obj) // workaround for golang/go#69912
|
||||
r.declare(obj)
|
||||
|
||||
case constTag:
|
||||
|
@ -590,6 +599,9 @@ func (r *importReader) obj(name string) {
|
|||
// declaration before recursing.
|
||||
obj := types.NewTypeName(pos, r.currPkg, name, nil)
|
||||
named := types.NewNamed(obj, nil, nil)
|
||||
|
||||
markBlack(obj) // workaround for golang/go#69912
|
||||
|
||||
// Declare obj before calling r.tparamList, so the new type name is recognized
|
||||
// if used in the constraint of one of its own typeparams (see #48280).
|
||||
r.declare(obj)
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.22 && !go1.24
|
||||
|
||||
package gcimporter
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// TODO(rfindley): delete this workaround once go1.24 is assured.
|
||||
|
||||
func init() {
|
||||
// Update markBlack so that it correctly sets the color
|
||||
// of imported TypeNames.
|
||||
//
|
||||
// See the doc comment for markBlack for details.
|
||||
|
||||
type color uint32
|
||||
const (
|
||||
white color = iota
|
||||
black
|
||||
grey
|
||||
)
|
||||
type object struct {
|
||||
_ *types.Scope
|
||||
_ token.Pos
|
||||
_ *types.Package
|
||||
_ string
|
||||
_ types.Type
|
||||
_ uint32
|
||||
color_ color
|
||||
_ token.Pos
|
||||
}
|
||||
type typeName struct {
|
||||
object
|
||||
}
|
||||
|
||||
// If the size of types.TypeName changes, this will fail to compile.
|
||||
const delta = int64(unsafe.Sizeof(typeName{})) - int64(unsafe.Sizeof(types.TypeName{}))
|
||||
var _ [-delta * delta]int
|
||||
|
||||
markBlack = func(obj *types.TypeName) {
|
||||
type uP = unsafe.Pointer
|
||||
var ptr *typeName
|
||||
*(*uP)(uP(&ptr)) = uP(obj)
|
||||
ptr.color_ = black
|
||||
}
|
||||
}
|
|
@ -27,7 +27,6 @@ import (
|
|||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
"golang.org/x/tools/internal/event"
|
||||
"golang.org/x/tools/internal/gocommand"
|
||||
|
@ -91,18 +90,6 @@ type ImportFix struct {
|
|||
Relevance float64 // see pkg
|
||||
}
|
||||
|
||||
// An ImportInfo represents a single import statement.
|
||||
type ImportInfo struct {
|
||||
ImportPath string // import path, e.g. "crypto/rand".
|
||||
Name string // import name, e.g. "crand", or "" if none.
|
||||
}
|
||||
|
||||
// A packageInfo represents what's known about a package.
|
||||
type packageInfo struct {
|
||||
name string // real package name, if known.
|
||||
exports map[string]bool // known exports.
|
||||
}
|
||||
|
||||
// parseOtherFiles parses all the Go files in srcDir except filename, including
|
||||
// test files if filename looks like a test.
|
||||
//
|
||||
|
@ -162,8 +149,8 @@ func addGlobals(f *ast.File, globals map[string]bool) {
|
|||
|
||||
// collectReferences builds a map of selector expressions, from
|
||||
// left hand side (X) to a set of right hand sides (Sel).
|
||||
func collectReferences(f *ast.File) references {
|
||||
refs := references{}
|
||||
func collectReferences(f *ast.File) References {
|
||||
refs := References{}
|
||||
|
||||
var visitor visitFn
|
||||
visitor = func(node ast.Node) ast.Visitor {
|
||||
|
@ -233,7 +220,7 @@ func (p *pass) findMissingImport(pkg string, syms map[string]bool) *ImportInfo {
|
|||
|
||||
allFound := true
|
||||
for right := range syms {
|
||||
if !pkgInfo.exports[right] {
|
||||
if !pkgInfo.Exports[right] {
|
||||
allFound = false
|
||||
break
|
||||
}
|
||||
|
@ -246,11 +233,6 @@ func (p *pass) findMissingImport(pkg string, syms map[string]bool) *ImportInfo {
|
|||
return nil
|
||||
}
|
||||
|
||||
// references is set of references found in a Go file. The first map key is the
|
||||
// left hand side of a selector expression, the second key is the right hand
|
||||
// side, and the value should always be true.
|
||||
type references map[string]map[string]bool
|
||||
|
||||
// A pass contains all the inputs and state necessary to fix a file's imports.
|
||||
// It can be modified in some ways during use; see comments below.
|
||||
type pass struct {
|
||||
|
@ -258,27 +240,29 @@ type pass struct {
|
|||
fset *token.FileSet // fset used to parse f and its siblings.
|
||||
f *ast.File // the file being fixed.
|
||||
srcDir string // the directory containing f.
|
||||
env *ProcessEnv // the environment to use for go commands, etc.
|
||||
loadRealPackageNames bool // if true, load package names from disk rather than guessing them.
|
||||
otherFiles []*ast.File // sibling files.
|
||||
logf func(string, ...any)
|
||||
source Source // the environment to use for go commands, etc.
|
||||
loadRealPackageNames bool // if true, load package names from disk rather than guessing them.
|
||||
otherFiles []*ast.File // sibling files.
|
||||
goroot string
|
||||
|
||||
// Intermediate state, generated by load.
|
||||
existingImports map[string][]*ImportInfo
|
||||
allRefs references
|
||||
missingRefs references
|
||||
allRefs References
|
||||
missingRefs References
|
||||
|
||||
// Inputs to fix. These can be augmented between successive fix calls.
|
||||
lastTry bool // indicates that this is the last call and fix should clean up as best it can.
|
||||
candidates []*ImportInfo // candidate imports in priority order.
|
||||
knownPackages map[string]*packageInfo // information about all known packages.
|
||||
knownPackages map[string]*PackageInfo // information about all known packages.
|
||||
}
|
||||
|
||||
// loadPackageNames saves the package names for everything referenced by imports.
|
||||
func (p *pass) loadPackageNames(imports []*ImportInfo) error {
|
||||
if p.env.Logf != nil {
|
||||
p.env.Logf("loading package names for %v packages", len(imports))
|
||||
func (p *pass) loadPackageNames(ctx context.Context, imports []*ImportInfo) error {
|
||||
if p.logf != nil {
|
||||
p.logf("loading package names for %v packages", len(imports))
|
||||
defer func() {
|
||||
p.env.Logf("done loading package names for %v packages", len(imports))
|
||||
p.logf("done loading package names for %v packages", len(imports))
|
||||
}()
|
||||
}
|
||||
var unknown []string
|
||||
|
@ -289,20 +273,17 @@ func (p *pass) loadPackageNames(imports []*ImportInfo) error {
|
|||
unknown = append(unknown, imp.ImportPath)
|
||||
}
|
||||
|
||||
resolver, err := p.env.GetResolver()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
names, err := resolver.loadPackageNames(unknown, p.srcDir)
|
||||
names, err := p.source.LoadPackageNames(ctx, p.srcDir, unknown)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(rfindley): revisit this. Why do we need to store known packages with
|
||||
// no exports? The inconsistent data is confusing.
|
||||
for path, name := range names {
|
||||
p.knownPackages[path] = &packageInfo{
|
||||
name: name,
|
||||
exports: map[string]bool{},
|
||||
p.knownPackages[path] = &PackageInfo{
|
||||
Name: name,
|
||||
Exports: map[string]bool{},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -330,8 +311,8 @@ func (p *pass) importIdentifier(imp *ImportInfo) string {
|
|||
return imp.Name
|
||||
}
|
||||
known := p.knownPackages[imp.ImportPath]
|
||||
if known != nil && known.name != "" {
|
||||
return withoutVersion(known.name)
|
||||
if known != nil && known.Name != "" {
|
||||
return withoutVersion(known.Name)
|
||||
}
|
||||
return ImportPathToAssumedName(imp.ImportPath)
|
||||
}
|
||||
|
@ -339,9 +320,9 @@ func (p *pass) importIdentifier(imp *ImportInfo) string {
|
|||
// load reads in everything necessary to run a pass, and reports whether the
|
||||
// file already has all the imports it needs. It fills in p.missingRefs with the
|
||||
// file's missing symbols, if any, or removes unused imports if not.
|
||||
func (p *pass) load() ([]*ImportFix, bool) {
|
||||
p.knownPackages = map[string]*packageInfo{}
|
||||
p.missingRefs = references{}
|
||||
func (p *pass) load(ctx context.Context) ([]*ImportFix, bool) {
|
||||
p.knownPackages = map[string]*PackageInfo{}
|
||||
p.missingRefs = References{}
|
||||
p.existingImports = map[string][]*ImportInfo{}
|
||||
|
||||
// Load basic information about the file in question.
|
||||
|
@ -364,9 +345,11 @@ func (p *pass) load() ([]*ImportFix, bool) {
|
|||
// f's imports by the identifier they introduce.
|
||||
imports := collectImports(p.f)
|
||||
if p.loadRealPackageNames {
|
||||
err := p.loadPackageNames(append(imports, p.candidates...))
|
||||
err := p.loadPackageNames(ctx, append(imports, p.candidates...))
|
||||
if err != nil {
|
||||
p.env.logf("loading package names: %v", err)
|
||||
if p.logf != nil {
|
||||
p.logf("loading package names: %v", err)
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
@ -535,9 +518,10 @@ func (p *pass) assumeSiblingImportsValid() {
|
|||
// We have the stdlib in memory; no need to guess.
|
||||
rights = symbolNameSet(m)
|
||||
}
|
||||
p.addCandidate(imp, &packageInfo{
|
||||
// TODO(rfindley): we should set package name here, for consistency.
|
||||
p.addCandidate(imp, &PackageInfo{
|
||||
// no name; we already know it.
|
||||
exports: rights,
|
||||
Exports: rights,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -546,14 +530,14 @@ func (p *pass) assumeSiblingImportsValid() {
|
|||
|
||||
// addCandidate adds a candidate import to p, and merges in the information
|
||||
// in pkg.
|
||||
func (p *pass) addCandidate(imp *ImportInfo, pkg *packageInfo) {
|
||||
func (p *pass) addCandidate(imp *ImportInfo, pkg *PackageInfo) {
|
||||
p.candidates = append(p.candidates, imp)
|
||||
if existing, ok := p.knownPackages[imp.ImportPath]; ok {
|
||||
if existing.name == "" {
|
||||
existing.name = pkg.name
|
||||
if existing.Name == "" {
|
||||
existing.Name = pkg.Name
|
||||
}
|
||||
for export := range pkg.exports {
|
||||
existing.exports[export] = true
|
||||
for export := range pkg.Exports {
|
||||
existing.Exports[export] = true
|
||||
}
|
||||
} else {
|
||||
p.knownPackages[imp.ImportPath] = pkg
|
||||
|
@ -581,19 +565,42 @@ func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string, env *P
|
|||
// getFixes gets the import fixes that need to be made to f in order to fix the imports.
|
||||
// It does not modify the ast.
|
||||
func getFixes(ctx context.Context, fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv) ([]*ImportFix, error) {
|
||||
source, err := NewProcessEnvSource(env, filename, f.Name.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
goEnv, err := env.goEnv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getFixesWithSource(ctx, fset, f, filename, goEnv["GOROOT"], env.logf, source)
|
||||
}
|
||||
|
||||
func getFixesWithSource(ctx context.Context, fset *token.FileSet, f *ast.File, filename string, goroot string, logf func(string, ...any), source Source) ([]*ImportFix, error) {
|
||||
// This logic is defensively duplicated from getFixes.
|
||||
abs, err := filepath.Abs(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
srcDir := filepath.Dir(abs)
|
||||
env.logf("fixImports(filename=%q), abs=%q, srcDir=%q ...", filename, abs, srcDir)
|
||||
|
||||
if logf != nil {
|
||||
logf("fixImports(filename=%q), srcDir=%q ...", filename, abs, srcDir)
|
||||
}
|
||||
|
||||
// First pass: looking only at f, and using the naive algorithm to
|
||||
// derive package names from import paths, see if the file is already
|
||||
// complete. We can't add any imports yet, because we don't know
|
||||
// if missing references are actually package vars.
|
||||
p := &pass{fset: fset, f: f, srcDir: srcDir, env: env}
|
||||
if fixes, done := p.load(); done {
|
||||
p := &pass{
|
||||
fset: fset,
|
||||
f: f,
|
||||
srcDir: srcDir,
|
||||
logf: logf,
|
||||
goroot: goroot,
|
||||
source: source,
|
||||
}
|
||||
if fixes, done := p.load(ctx); done {
|
||||
return fixes, nil
|
||||
}
|
||||
|
||||
|
@ -605,7 +612,7 @@ func getFixes(ctx context.Context, fset *token.FileSet, f *ast.File, filename st
|
|||
// Second pass: add information from other files in the same package,
|
||||
// like their package vars and imports.
|
||||
p.otherFiles = otherFiles
|
||||
if fixes, done := p.load(); done {
|
||||
if fixes, done := p.load(ctx); done {
|
||||
return fixes, nil
|
||||
}
|
||||
|
||||
|
@ -618,10 +625,17 @@ func getFixes(ctx context.Context, fset *token.FileSet, f *ast.File, filename st
|
|||
|
||||
// Third pass: get real package names where we had previously used
|
||||
// the naive algorithm.
|
||||
p = &pass{fset: fset, f: f, srcDir: srcDir, env: env}
|
||||
p = &pass{
|
||||
fset: fset,
|
||||
f: f,
|
||||
srcDir: srcDir,
|
||||
logf: logf,
|
||||
goroot: goroot,
|
||||
source: p.source, // safe to reuse, as it's just a wrapper around env
|
||||
}
|
||||
p.loadRealPackageNames = true
|
||||
p.otherFiles = otherFiles
|
||||
if fixes, done := p.load(); done {
|
||||
if fixes, done := p.load(ctx); done {
|
||||
return fixes, nil
|
||||
}
|
||||
|
||||
|
@ -835,7 +849,7 @@ func GetPackageExports(ctx context.Context, wrapped func(PackageExport), searchP
|
|||
return true
|
||||
},
|
||||
dirFound: func(pkg *pkg) bool {
|
||||
return pkgIsCandidate(filename, references{searchPkg: nil}, pkg)
|
||||
return pkgIsCandidate(filename, References{searchPkg: nil}, pkg)
|
||||
},
|
||||
packageNameLoaded: func(pkg *pkg) bool {
|
||||
return pkg.packageName == searchPkg
|
||||
|
@ -1086,11 +1100,7 @@ func (e *ProcessEnv) invokeGo(ctx context.Context, verb string, args ...string)
|
|||
return e.GocmdRunner.Run(ctx, inv)
|
||||
}
|
||||
|
||||
func addStdlibCandidates(pass *pass, refs references) error {
|
||||
goenv, err := pass.env.goEnv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func addStdlibCandidates(pass *pass, refs References) error {
|
||||
localbase := func(nm string) string {
|
||||
ans := path.Base(nm)
|
||||
if ans[0] == 'v' {
|
||||
|
@ -1105,13 +1115,13 @@ func addStdlibCandidates(pass *pass, refs references) error {
|
|||
}
|
||||
add := func(pkg string) {
|
||||
// Prevent self-imports.
|
||||
if path.Base(pkg) == pass.f.Name.Name && filepath.Join(goenv["GOROOT"], "src", pkg) == pass.srcDir {
|
||||
if path.Base(pkg) == pass.f.Name.Name && filepath.Join(pass.goroot, "src", pkg) == pass.srcDir {
|
||||
return
|
||||
}
|
||||
exports := symbolNameSet(stdlib.PackageSymbols[pkg])
|
||||
pass.addCandidate(
|
||||
&ImportInfo{ImportPath: pkg},
|
||||
&packageInfo{name: localbase(pkg), exports: exports})
|
||||
&PackageInfo{Name: localbase(pkg), Exports: exports})
|
||||
}
|
||||
for left := range refs {
|
||||
if left == "rand" {
|
||||
|
@ -1175,91 +1185,14 @@ type scanCallback struct {
|
|||
exportsLoaded func(pkg *pkg, exports []stdlib.Symbol)
|
||||
}
|
||||
|
||||
func addExternalCandidates(ctx context.Context, pass *pass, refs references, filename string) error {
|
||||
func addExternalCandidates(ctx context.Context, pass *pass, refs References, filename string) error {
|
||||
ctx, done := event.Start(ctx, "imports.addExternalCandidates")
|
||||
defer done()
|
||||
|
||||
var mu sync.Mutex
|
||||
found := make(map[string][]pkgDistance)
|
||||
callback := &scanCallback{
|
||||
rootFound: func(gopathwalk.Root) bool {
|
||||
return true // We want everything.
|
||||
},
|
||||
dirFound: func(pkg *pkg) bool {
|
||||
return pkgIsCandidate(filename, refs, pkg)
|
||||
},
|
||||
packageNameLoaded: func(pkg *pkg) bool {
|
||||
if _, want := refs[pkg.packageName]; !want {
|
||||
return false
|
||||
}
|
||||
if pkg.dir == pass.srcDir && pass.f.Name.Name == pkg.packageName {
|
||||
// The candidate is in the same directory and has the
|
||||
// same package name. Don't try to import ourselves.
|
||||
return false
|
||||
}
|
||||
if !canUse(filename, pkg.dir) {
|
||||
return false
|
||||
}
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
found[pkg.packageName] = append(found[pkg.packageName], pkgDistance{pkg, distance(pass.srcDir, pkg.dir)})
|
||||
return false // We'll do our own loading after we sort.
|
||||
},
|
||||
}
|
||||
resolver, err := pass.env.GetResolver()
|
||||
results, err := pass.source.ResolveReferences(ctx, filename, refs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = resolver.scan(ctx, callback); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Search for imports matching potential package references.
|
||||
type result struct {
|
||||
imp *ImportInfo
|
||||
pkg *packageInfo
|
||||
}
|
||||
results := make([]*result, len(refs))
|
||||
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
searcher := symbolSearcher{
|
||||
logf: pass.env.logf,
|
||||
srcDir: pass.srcDir,
|
||||
xtest: strings.HasSuffix(pass.f.Name.Name, "_test"),
|
||||
loadExports: resolver.loadExports,
|
||||
}
|
||||
|
||||
i := 0
|
||||
for pkgName, symbols := range refs {
|
||||
index := i // claim an index in results
|
||||
i++
|
||||
pkgName := pkgName
|
||||
symbols := symbols
|
||||
|
||||
g.Go(func() error {
|
||||
found, err := searcher.search(ctx, found[pkgName], pkgName, symbols)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found == nil {
|
||||
return nil // No matching package.
|
||||
}
|
||||
|
||||
imp := &ImportInfo{
|
||||
ImportPath: found.importPathShort,
|
||||
}
|
||||
pkg := &packageInfo{
|
||||
name: pkgName,
|
||||
exports: symbols,
|
||||
}
|
||||
results[index] = &result{imp, pkg}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if err := g.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, result := range results {
|
||||
if result == nil {
|
||||
|
@ -1267,7 +1200,7 @@ func addExternalCandidates(ctx context.Context, pass *pass, refs references, fil
|
|||
}
|
||||
// Don't offer completions that would shadow predeclared
|
||||
// names, such as github.com/coreos/etcd/error.
|
||||
if types.Universe.Lookup(result.pkg.name) != nil { // predeclared
|
||||
if types.Universe.Lookup(result.Package.Name) != nil { // predeclared
|
||||
// Ideally we would skip this candidate only
|
||||
// if the predeclared name is actually
|
||||
// referenced by the file, but that's a lot
|
||||
|
@ -1276,7 +1209,7 @@ func addExternalCandidates(ctx context.Context, pass *pass, refs references, fil
|
|||
// user before long.
|
||||
continue
|
||||
}
|
||||
pass.addCandidate(result.imp, result.pkg)
|
||||
pass.addCandidate(result.Import, result.Package)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1801,7 +1734,7 @@ func (s *symbolSearcher) searchOne(ctx context.Context, c pkgDistance, symbols m
|
|||
// filename is the file being formatted.
|
||||
// pkgIdent is the package being searched for, like "client" (if
|
||||
// searching for "client.New")
|
||||
func pkgIsCandidate(filename string, refs references, pkg *pkg) bool {
|
||||
func pkgIsCandidate(filename string, refs References, pkg *pkg) bool {
|
||||
// Check "internal" and "vendor" visibility:
|
||||
if !canUse(filename, pkg.dir) {
|
||||
return false
|
||||
|
|
|
@ -47,7 +47,14 @@ type Options struct {
|
|||
// Process implements golang.org/x/tools/imports.Process with explicit context in opt.Env.
|
||||
func Process(filename string, src []byte, opt *Options) (formatted []byte, err error) {
|
||||
fileSet := token.NewFileSet()
|
||||
file, adjust, err := parse(fileSet, filename, src, opt)
|
||||
var parserMode parser.Mode
|
||||
if opt.Comments {
|
||||
parserMode |= parser.ParseComments
|
||||
}
|
||||
if opt.AllErrors {
|
||||
parserMode |= parser.AllErrors
|
||||
}
|
||||
file, adjust, err := parse(fileSet, filename, src, parserMode, opt.Fragment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -66,17 +73,19 @@ func Process(filename string, src []byte, opt *Options) (formatted []byte, err e
|
|||
//
|
||||
// Note that filename's directory influences which imports can be chosen,
|
||||
// so it is important that filename be accurate.
|
||||
func FixImports(ctx context.Context, filename string, src []byte, opt *Options) (fixes []*ImportFix, err error) {
|
||||
func FixImports(ctx context.Context, filename string, src []byte, goroot string, logf func(string, ...any), source Source) (fixes []*ImportFix, err error) {
|
||||
ctx, done := event.Start(ctx, "imports.FixImports")
|
||||
defer done()
|
||||
|
||||
fileSet := token.NewFileSet()
|
||||
file, _, err := parse(fileSet, filename, src, opt)
|
||||
// TODO(rfindley): these default values for ParseComments and AllErrors were
|
||||
// extracted from gopls, but are they even needed?
|
||||
file, _, err := parse(fileSet, filename, src, parser.ParseComments|parser.AllErrors, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return getFixes(ctx, fileSet, file, filename, opt.Env)
|
||||
return getFixesWithSource(ctx, fileSet, file, filename, goroot, logf, source)
|
||||
}
|
||||
|
||||
// ApplyFixes applies all of the fixes to the file and formats it. extraMode
|
||||
|
@ -114,7 +123,7 @@ func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options, e
|
|||
// formatted file, and returns the postpocessed result.
|
||||
func formatFile(fset *token.FileSet, file *ast.File, src []byte, adjust func(orig []byte, src []byte) []byte, opt *Options) ([]byte, error) {
|
||||
mergeImports(file)
|
||||
sortImports(opt.LocalPrefix, fset.File(file.Pos()), file)
|
||||
sortImports(opt.LocalPrefix, fset.File(file.FileStart), file)
|
||||
var spacesBefore []string // import paths we need spaces before
|
||||
for _, impSection := range astutil.Imports(fset, file) {
|
||||
// Within each block of contiguous imports, see if any
|
||||
|
@ -164,13 +173,9 @@ func formatFile(fset *token.FileSet, file *ast.File, src []byte, adjust func(ori
|
|||
|
||||
// parse parses src, which was read from filename,
|
||||
// as a Go source file or statement list.
|
||||
func parse(fset *token.FileSet, filename string, src []byte, opt *Options) (*ast.File, func(orig, src []byte) []byte, error) {
|
||||
var parserMode parser.Mode // legacy ast.Object resolution is required here
|
||||
if opt.Comments {
|
||||
parserMode |= parser.ParseComments
|
||||
}
|
||||
if opt.AllErrors {
|
||||
parserMode |= parser.AllErrors
|
||||
func parse(fset *token.FileSet, filename string, src []byte, parserMode parser.Mode, fragment bool) (*ast.File, func(orig, src []byte) []byte, error) {
|
||||
if parserMode&parser.SkipObjectResolution != 0 {
|
||||
panic("legacy ast.Object resolution is required")
|
||||
}
|
||||
|
||||
// Try as whole source file.
|
||||
|
@ -181,7 +186,7 @@ func parse(fset *token.FileSet, filename string, src []byte, opt *Options) (*ast
|
|||
// If the error is that the source file didn't begin with a
|
||||
// package line and we accept fragmented input, fall through to
|
||||
// try as a source fragment. Stop and return on any other error.
|
||||
if !opt.Fragment || !strings.Contains(err.Error(), "expected 'package'") {
|
||||
if !fragment || !strings.Contains(err.Error(), "expected 'package'") {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package imports
|
||||
|
||||
import "context"
|
||||
|
||||
// These types document the APIs below.
|
||||
//
|
||||
// TODO(rfindley): consider making these defined types rather than aliases.
|
||||
type (
|
||||
ImportPath = string
|
||||
PackageName = string
|
||||
Symbol = string
|
||||
|
||||
// References is set of References found in a Go file. The first map key is the
|
||||
// left hand side of a selector expression, the second key is the right hand
|
||||
// side, and the value should always be true.
|
||||
References = map[PackageName]map[Symbol]bool
|
||||
)
|
||||
|
||||
// A Result satisfies a missing import.
|
||||
//
|
||||
// The Import field describes the missing import spec, and the Package field
|
||||
// summarizes the package exports.
|
||||
type Result struct {
|
||||
Import *ImportInfo
|
||||
Package *PackageInfo
|
||||
}
|
||||
|
||||
// An ImportInfo represents a single import statement.
|
||||
type ImportInfo struct {
|
||||
ImportPath string // import path, e.g. "crypto/rand".
|
||||
Name string // import name, e.g. "crand", or "" if none.
|
||||
}
|
||||
|
||||
// A PackageInfo represents what's known about a package.
|
||||
type PackageInfo struct {
|
||||
Name string // package name in the package declaration, if known
|
||||
Exports map[string]bool // set of names of known package level sortSymbols
|
||||
}
|
||||
|
||||
// A Source provides imports to satisfy unresolved references in the file being
|
||||
// fixed.
|
||||
type Source interface {
|
||||
// LoadPackageNames queries PackageName information for the requested import
|
||||
// paths, when operating from the provided srcDir.
|
||||
//
|
||||
// TODO(rfindley): try to refactor to remove this operation.
|
||||
LoadPackageNames(ctx context.Context, srcDir string, paths []ImportPath) (map[ImportPath]PackageName, error)
|
||||
|
||||
// ResolveReferences asks the Source for the best package name to satisfy
|
||||
// each of the missing references, in the context of fixing the given
|
||||
// filename.
|
||||
//
|
||||
// Returns a map from package name to a [Result] for that package name that
|
||||
// provides the required symbols. Keys may be omitted in the map if no
|
||||
// candidates satisfy all missing references for that package name. It is up
|
||||
// to each data source to select the best result for each entry in the
|
||||
// missing map.
|
||||
ResolveReferences(ctx context.Context, filename string, missing References) (map[PackageName]*Result, error)
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package imports
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/tools/internal/gopathwalk"
|
||||
)
|
||||
|
||||
// ProcessEnvSource implements the [Source] interface using the legacy
|
||||
// [ProcessEnv] abstraction.
|
||||
type ProcessEnvSource struct {
|
||||
env *ProcessEnv
|
||||
srcDir string
|
||||
filename string
|
||||
pkgName string
|
||||
}
|
||||
|
||||
// NewProcessEnvSource returns a [ProcessEnvSource] wrapping the given
|
||||
// env, to be used for fixing imports in the file with name filename in package
|
||||
// named pkgName.
|
||||
func NewProcessEnvSource(env *ProcessEnv, filename, pkgName string) (*ProcessEnvSource, error) {
|
||||
abs, err := filepath.Abs(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
srcDir := filepath.Dir(abs)
|
||||
return &ProcessEnvSource{
|
||||
env: env,
|
||||
srcDir: srcDir,
|
||||
filename: filename,
|
||||
pkgName: pkgName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ProcessEnvSource) LoadPackageNames(ctx context.Context, srcDir string, unknown []string) (map[string]string, error) {
|
||||
r, err := s.env.GetResolver()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.loadPackageNames(unknown, srcDir)
|
||||
}
|
||||
|
||||
func (s *ProcessEnvSource) ResolveReferences(ctx context.Context, filename string, refs map[string]map[string]bool) (map[string]*Result, error) {
|
||||
var mu sync.Mutex
|
||||
found := make(map[string][]pkgDistance)
|
||||
callback := &scanCallback{
|
||||
rootFound: func(gopathwalk.Root) bool {
|
||||
return true // We want everything.
|
||||
},
|
||||
dirFound: func(pkg *pkg) bool {
|
||||
return pkgIsCandidate(filename, refs, pkg)
|
||||
},
|
||||
packageNameLoaded: func(pkg *pkg) bool {
|
||||
if _, want := refs[pkg.packageName]; !want {
|
||||
return false
|
||||
}
|
||||
if pkg.dir == s.srcDir && s.pkgName == pkg.packageName {
|
||||
// The candidate is in the same directory and has the
|
||||
// same package name. Don't try to import ourselves.
|
||||
return false
|
||||
}
|
||||
if !canUse(filename, pkg.dir) {
|
||||
return false
|
||||
}
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
found[pkg.packageName] = append(found[pkg.packageName], pkgDistance{pkg, distance(s.srcDir, pkg.dir)})
|
||||
return false // We'll do our own loading after we sort.
|
||||
},
|
||||
}
|
||||
resolver, err := s.env.GetResolver()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := resolver.scan(ctx, callback); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
searcher := symbolSearcher{
|
||||
logf: s.env.logf,
|
||||
srcDir: s.srcDir,
|
||||
xtest: strings.HasSuffix(s.pkgName, "_test"),
|
||||
loadExports: resolver.loadExports,
|
||||
}
|
||||
|
||||
var resultMu sync.Mutex
|
||||
results := make(map[string]*Result, len(refs))
|
||||
for pkgName, symbols := range refs {
|
||||
g.Go(func() error {
|
||||
found, err := searcher.search(ctx, found[pkgName], pkgName, symbols)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found == nil {
|
||||
return nil // No matching package.
|
||||
}
|
||||
|
||||
imp := &ImportInfo{
|
||||
ImportPath: found.importPathShort,
|
||||
}
|
||||
pkg := &PackageInfo{
|
||||
Name: pkgName,
|
||||
Exports: symbols,
|
||||
}
|
||||
resultMu.Lock()
|
||||
results[pkgName] = &Result{Import: imp, Package: pkg}
|
||||
resultMu.Unlock()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if err := g.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return results, nil
|
||||
}
|
|
@ -6,6 +6,8 @@ package typeparams
|
|||
|
||||
import (
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/internal/aliases"
|
||||
)
|
||||
|
||||
// Free is a memoization of the set of free type parameters within a
|
||||
|
@ -36,6 +38,18 @@ func (w *Free) Has(typ types.Type) (res bool) {
|
|||
break
|
||||
|
||||
case *types.Alias:
|
||||
if aliases.TypeParams(t).Len() > aliases.TypeArgs(t).Len() {
|
||||
return true // This is an uninstantiated Alias.
|
||||
}
|
||||
// The expansion of an alias can have free type parameters,
|
||||
// whether or not the alias itself has type parameters:
|
||||
//
|
||||
// func _[K comparable]() {
|
||||
// type Set = map[K]bool // free(Set) = {K}
|
||||
// type MapTo[V] = map[K]V // free(Map[foo]) = {V}
|
||||
// }
|
||||
//
|
||||
// So, we must Unalias.
|
||||
return w.Has(types.Unalias(t))
|
||||
|
||||
case *types.Array:
|
||||
|
@ -96,9 +110,8 @@ func (w *Free) Has(typ types.Type) (res bool) {
|
|||
|
||||
case *types.Named:
|
||||
args := t.TypeArgs()
|
||||
// TODO(taking): this does not match go/types/infer.go. Check with rfindley.
|
||||
if params := t.TypeParams(); params.Len() > args.Len() {
|
||||
return true
|
||||
return true // this is an uninstantiated named type.
|
||||
}
|
||||
for i, n := 0, args.Len(); i < n; i++ {
|
||||
if w.Has(args.At(i)) {
|
||||
|
|
|
@ -11,6 +11,8 @@ import (
|
|||
"go/types"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/tools/internal/aliases"
|
||||
)
|
||||
|
||||
func SetUsesCgo(conf *types.Config) bool {
|
||||
|
@ -63,3 +65,57 @@ func NameRelativeTo(pkg *types.Package) types.Qualifier {
|
|||
return other.Name()
|
||||
}
|
||||
}
|
||||
|
||||
// A NamedOrAlias is a [types.Type] that is named (as
|
||||
// defined by the spec) and capable of bearing type parameters: it
|
||||
// abstracts aliases ([types.Alias]) and defined types
|
||||
// ([types.Named]).
|
||||
//
|
||||
// Every type declared by an explicit "type" declaration is a
|
||||
// NamedOrAlias. (Built-in type symbols may additionally
|
||||
// have type [types.Basic], which is not a NamedOrAlias,
|
||||
// though the spec regards them as "named".)
|
||||
//
|
||||
// NamedOrAlias cannot expose the Origin method, because
|
||||
// [types.Alias.Origin] and [types.Named.Origin] have different
|
||||
// (covariant) result types; use [Origin] instead.
|
||||
type NamedOrAlias interface {
|
||||
types.Type
|
||||
Obj() *types.TypeName
|
||||
}
|
||||
|
||||
// TypeParams is a light shim around t.TypeParams().
|
||||
// (go/types.Alias).TypeParams requires >= 1.23.
|
||||
func TypeParams(t NamedOrAlias) *types.TypeParamList {
|
||||
switch t := t.(type) {
|
||||
case *types.Alias:
|
||||
return aliases.TypeParams(t)
|
||||
case *types.Named:
|
||||
return t.TypeParams()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TypeArgs is a light shim around t.TypeArgs().
|
||||
// (go/types.Alias).TypeArgs requires >= 1.23.
|
||||
func TypeArgs(t NamedOrAlias) *types.TypeList {
|
||||
switch t := t.(type) {
|
||||
case *types.Alias:
|
||||
return aliases.TypeArgs(t)
|
||||
case *types.Named:
|
||||
return t.TypeArgs()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Origin returns the generic type of the Named or Alias type t if it
|
||||
// is instantiated, otherwise it returns t.
|
||||
func Origin(t NamedOrAlias) NamedOrAlias {
|
||||
switch t := t.(type) {
|
||||
case *types.Alias:
|
||||
return aliases.Origin(t)
|
||||
case *types.Named:
|
||||
return t.Origin()
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
|
|
@ -31,8 +31,3 @@ func FileVersion(info *types.Info, file *ast.File) string {
|
|||
// This would act as a max version on what a tool can support.
|
||||
return Future
|
||||
}
|
||||
|
||||
// InitFileVersions initializes info to record Go versions for Go files.
|
||||
func InitFileVersions(info *types.Info) {
|
||||
info.FileVersions = make(map[*ast.File]string)
|
||||
}
|
||||
|
|
|
@ -185,7 +185,7 @@ go.uber.org/zap/internal/exit
|
|||
go.uber.org/zap/internal/pool
|
||||
go.uber.org/zap/internal/stacktrace
|
||||
go.uber.org/zap/zapcore
|
||||
# golang.org/x/mod v0.21.0
|
||||
# golang.org/x/mod v0.22.0
|
||||
## explicit; go 1.22.0
|
||||
golang.org/x/mod/internal/lazyregexp
|
||||
golang.org/x/mod/module
|
||||
|
@ -224,7 +224,7 @@ golang.org/x/text/unicode/norm
|
|||
# golang.org/x/time v0.6.0
|
||||
## explicit; go 1.18
|
||||
golang.org/x/time/rate
|
||||
# golang.org/x/tools v0.26.0
|
||||
# golang.org/x/tools v0.27.0
|
||||
## explicit; go 1.22.0
|
||||
golang.org/x/tools/go/ast/astutil
|
||||
golang.org/x/tools/go/gcexportdata
|
||||
|
@ -707,7 +707,7 @@ k8s.io/utils/trace
|
|||
# knative.dev/hack v0.0.0-20241106013728-b7995315deb5
|
||||
## explicit; go 1.21
|
||||
knative.dev/hack
|
||||
# knative.dev/pkg v0.0.0-20241112094440-47a6f9fc2e7d
|
||||
# knative.dev/pkg v0.0.0-20241115112044-215048add14b
|
||||
## explicit; go 1.22.7
|
||||
knative.dev/pkg/apis
|
||||
knative.dev/pkg/apis/duck
|
||||
|
|
Loading…
Reference in New Issue