vendor: golang.org/x/tools v0.16.0, golang.org/x/mod v0.14.0

removes dependency on golang.org/x/sys/execabs

full diff:

- https://github.com/golang/tools/compare/v0.10.0...v0.16.0
- https://github.com/golang/mod/compare/v0.11.0...v0.14.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2024-01-08 11:02:26 +01:00
parent efae960e5a
commit 9db56ea2f6
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
33 changed files with 1535 additions and 939 deletions

View File

@ -80,10 +80,10 @@ require (
go.opentelemetry.io/otel/metric v1.19.0 // indirect go.opentelemetry.io/otel/metric v1.19.0 // indirect
go.opentelemetry.io/otel/trace v1.19.0 // indirect go.opentelemetry.io/otel/trace v1.19.0 // indirect
golang.org/x/crypto v0.17.0 // indirect golang.org/x/crypto v0.17.0 // indirect
golang.org/x/mod v0.11.0 // indirect golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.19.0 // indirect golang.org/x/net v0.19.0 // indirect
golang.org/x/time v0.3.0 // indirect golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.10.0 // indirect golang.org/x/tools v0.16.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/grpc v1.58.3 // indirect google.golang.org/grpc v1.58.3 // indirect
google.golang.org/protobuf v1.31.0 // indirect google.golang.org/protobuf v1.31.0 // indirect

View File

@ -299,8 +299,8 @@ golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -347,8 +347,8 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -140,7 +140,7 @@ func Compare(v, w string) int {
// Max canonicalizes its arguments and then returns the version string // Max canonicalizes its arguments and then returns the version string
// that compares greater. // that compares greater.
// //
// Deprecated: use Compare instead. In most cases, returning a canonicalized // Deprecated: use [Compare] instead. In most cases, returning a canonicalized
// version is not expected or desired. // version is not expected or desired.
func Max(v, w string) string { func Max(v, w string) string {
v = Canonical(v) v = Canonical(v)
@ -151,7 +151,7 @@ func Max(v, w string) string {
return w return w
} }
// ByVersion implements sort.Interface for sorting semantic version strings. // ByVersion implements [sort.Interface] for sorting semantic version strings.
type ByVersion []string type ByVersion []string
func (vs ByVersion) Len() int { return len(vs) } func (vs ByVersion) Len() int { return len(vs) }
@ -164,7 +164,7 @@ func (vs ByVersion) Less(i, j int) bool {
return vs[i] < vs[j] return vs[i] < vs[j]
} }
// Sort sorts a list of semantic version strings using ByVersion. // Sort sorts a list of semantic version strings using [ByVersion].
func Sort(list []string) { func Sort(list []string) {
sort.Sort(ByVersion(list)) sort.Sort(ByVersion(list))
} }

View File

@ -1,102 +0,0 @@
// Copyright 2020 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 execabs is a drop-in replacement for os/exec
// that requires PATH lookups to find absolute paths.
// That is, execabs.Command("cmd") runs the same PATH lookup
// as exec.Command("cmd"), but if the result is a path
// which is relative, the Run and Start methods will report
// an error instead of running the executable.
//
// See https://blog.golang.org/path-security for more information
// about when it may be necessary or appropriate to use this package.
package execabs
import (
"context"
"fmt"
"os/exec"
"path/filepath"
"reflect"
"unsafe"
)
// ErrNotFound is the error resulting if a path search failed to find an executable file.
// It is an alias for exec.ErrNotFound.
var ErrNotFound = exec.ErrNotFound
// Cmd represents an external command being prepared or run.
// It is an alias for exec.Cmd.
type Cmd = exec.Cmd
// Error is returned by LookPath when it fails to classify a file as an executable.
// It is an alias for exec.Error.
type Error = exec.Error
// An ExitError reports an unsuccessful exit by a command.
// It is an alias for exec.ExitError.
type ExitError = exec.ExitError
func relError(file, path string) error {
return fmt.Errorf("%s resolves to executable in current directory (.%c%s)", file, filepath.Separator, path)
}
// LookPath searches for an executable named file in the directories
// named by the PATH environment variable. If file contains a slash,
// it is tried directly and the PATH is not consulted. The result will be
// an absolute path.
//
// LookPath differs from exec.LookPath in its handling of PATH lookups,
// which are used for file names without slashes. If exec.LookPath's
// PATH lookup would have returned an executable from the current directory,
// LookPath instead returns an error.
func LookPath(file string) (string, error) {
path, err := exec.LookPath(file)
if err != nil && !isGo119ErrDot(err) {
return "", err
}
if filepath.Base(file) == file && !filepath.IsAbs(path) {
return "", relError(file, path)
}
return path, nil
}
func fixCmd(name string, cmd *exec.Cmd) {
if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) && !isGo119ErrFieldSet(cmd) {
// exec.Command was called with a bare binary name and
// exec.LookPath returned a path which is not absolute.
// Set cmd.lookPathErr and clear cmd.Path so that it
// cannot be run.
lookPathErr := (*error)(unsafe.Pointer(reflect.ValueOf(cmd).Elem().FieldByName("lookPathErr").Addr().Pointer()))
if *lookPathErr == nil {
*lookPathErr = relError(name, cmd.Path)
}
cmd.Path = ""
}
}
// CommandContext is like Command but includes a context.
//
// The provided context is used to kill the process (by calling os.Process.Kill)
// if the context becomes done before the command completes on its own.
func CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd {
cmd := exec.CommandContext(ctx, name, arg...)
fixCmd(name, cmd)
return cmd
}
// Command returns the Cmd struct to execute the named program with the given arguments.
// See exec.Command for most details.
//
// Command differs from exec.Command in its handling of PATH lookups,
// which are used when the program name contains no slashes.
// If exec.Command would have returned an exec.Cmd configured to run an
// executable from the current directory, Command instead
// returns an exec.Cmd that will return an error from Start or Run.
func Command(name string, arg ...string) *exec.Cmd {
cmd := exec.Command(name, arg...)
fixCmd(name, cmd)
return cmd
}

View File

@ -1,17 +0,0 @@
// Copyright 2022 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.19
package execabs
import "os/exec"
func isGo119ErrDot(err error) bool {
return false
}
func isGo119ErrFieldSet(cmd *exec.Cmd) bool {
return false
}

View File

@ -1,20 +0,0 @@
// Copyright 2022 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.19
package execabs
import (
"errors"
"os/exec"
)
func isGo119ErrDot(err error) bool {
return errors.Is(err, exec.ErrDot)
}
func isGo119ErrFieldSet(cmd *exec.Cmd) bool {
return cmd.Err != nil
}

View File

@ -188,6 +188,8 @@ type Generator struct {
trimPrefix string trimPrefix string
lineComment bool lineComment bool
logf func(format string, args ...interface{}) // test logging hook; nil when not testing
} }
func (g *Generator) Printf(format string, args ...interface{}) { func (g *Generator) Printf(format string, args ...interface{}) {
@ -221,13 +223,14 @@ func (g *Generator) parsePackage(patterns []string, tags []string) {
// in a separate pass? For later. // in a separate pass? For later.
Tests: false, Tests: false,
BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))}, BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))},
Logf: g.logf,
} }
pkgs, err := packages.Load(cfg, patterns...) pkgs, err := packages.Load(cfg, patterns...)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
if len(pkgs) != 1 { if len(pkgs) != 1 {
log.Fatalf("error: %d packages found", len(pkgs)) log.Fatalf("error: %d packages matching %v", len(pkgs), strings.Join(patterns, " "))
} }
g.addPackage(pkgs[0]) g.addPackage(pkgs[0])
} }

View File

@ -8,42 +8,46 @@ package packagesdriver
import ( import (
"context" "context"
"fmt" "fmt"
"go/types"
"strings" "strings"
"golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/gocommand"
) )
var debug = false func GetSizesForArgsGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (string, string, error) {
func GetSizesGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (types.Sizes, error) {
inv.Verb = "list" inv.Verb = "list"
inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"} inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"}
stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv) stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv)
var goarch, compiler string var goarch, compiler string
if rawErr != nil { if rawErr != nil {
if rawErrMsg := rawErr.Error(); strings.Contains(rawErrMsg, "cannot find main module") || strings.Contains(rawErrMsg, "go.mod file not found") { rawErrMsg := rawErr.Error()
// User's running outside of a module. All bets are off. Get GOARCH and guess compiler is gc. if strings.Contains(rawErrMsg, "cannot find main module") ||
strings.Contains(rawErrMsg, "go.mod file not found") {
// User's running outside of a module.
// All bets are off. Get GOARCH and guess compiler is gc.
// TODO(matloob): Is this a problem in practice? // TODO(matloob): Is this a problem in practice?
inv.Verb = "env" inv.Verb = "env"
inv.Args = []string{"GOARCH"} inv.Args = []string{"GOARCH"}
envout, enverr := gocmdRunner.Run(ctx, inv) envout, enverr := gocmdRunner.Run(ctx, inv)
if enverr != nil { if enverr != nil {
return nil, enverr return "", "", enverr
} }
goarch = strings.TrimSpace(envout.String()) goarch = strings.TrimSpace(envout.String())
compiler = "gc" compiler = "gc"
} else if friendlyErr != nil {
return "", "", friendlyErr
} else { } else {
return nil, friendlyErr // This should be unreachable, but be defensive
// in case RunRaw's error results are inconsistent.
return "", "", rawErr
} }
} else { } else {
fields := strings.Fields(stdout.String()) fields := strings.Fields(stdout.String())
if len(fields) < 2 { if len(fields) < 2 {
return nil, fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>", return "", "", fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>",
stdout.String(), stderr.String()) stdout.String(), stderr.String())
} }
goarch = fields[0] goarch = fields[0]
compiler = fields[1] compiler = fields[1]
} }
return types.SizesFor(compiler, goarch), nil return compiler, goarch, nil
} }

View File

@ -35,7 +35,7 @@ The Package struct provides basic information about the package, including
- Imports, a map from source import strings to the Packages they name; - Imports, a map from source import strings to the Packages they name;
- Types, the type information for the package's exported symbols; - Types, the type information for the package's exported symbols;
- Syntax, the parsed syntax trees for the package's source code; and - Syntax, the parsed syntax trees for the package's source code; and
- TypeInfo, the result of a complete type-check of the package syntax trees. - TypesInfo, the result of a complete type-check of the package syntax trees.
(See the documentation for type Package for the complete list of fields (See the documentation for type Package for the complete list of fields
and more detailed descriptions.) and more detailed descriptions.)

View File

@ -12,8 +12,8 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
exec "golang.org/x/sys/execabs"
"os" "os"
"os/exec"
"strings" "strings"
) )

View File

@ -9,10 +9,9 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"go/types"
"io/ioutil"
"log" "log"
"os" "os"
"os/exec"
"path" "path"
"path/filepath" "path/filepath"
"reflect" "reflect"
@ -22,7 +21,6 @@ import (
"sync" "sync"
"unicode" "unicode"
exec "golang.org/x/sys/execabs"
"golang.org/x/tools/go/internal/packagesdriver" "golang.org/x/tools/go/internal/packagesdriver"
"golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/gocommand"
"golang.org/x/tools/internal/packagesinternal" "golang.org/x/tools/internal/packagesinternal"
@ -153,10 +151,10 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 { if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
sizeswg.Add(1) sizeswg.Add(1)
go func() { go func() {
var sizes types.Sizes compiler, arch, err := packagesdriver.GetSizesForArgsGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner)
sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner) sizeserr = err
// types.SizesFor always returns nil or a *types.StdSizes. response.dr.Compiler = compiler
response.dr.Sizes, _ = sizes.(*types.StdSizes) response.dr.Arch = arch
sizeswg.Done() sizeswg.Done()
}() }()
} }
@ -210,62 +208,6 @@ extractQueries:
} }
} }
// Only use go/packages' overlay processing if we're using a Go version
// below 1.16. Otherwise, go list handles it.
if goVersion, err := state.getGoVersion(); err == nil && goVersion < 16 {
modifiedPkgs, needPkgs, err := state.processGolistOverlay(response)
if err != nil {
return nil, err
}
var containsCandidates []string
if len(containFiles) > 0 {
containsCandidates = append(containsCandidates, modifiedPkgs...)
containsCandidates = append(containsCandidates, needPkgs...)
}
if err := state.addNeededOverlayPackages(response, needPkgs); err != nil {
return nil, err
}
// Check candidate packages for containFiles.
if len(containFiles) > 0 {
for _, id := range containsCandidates {
pkg, ok := response.seenPackages[id]
if !ok {
response.addPackage(&Package{
ID: id,
Errors: []Error{{
Kind: ListError,
Msg: fmt.Sprintf("package %s expected but not seen", id),
}},
})
continue
}
for _, f := range containFiles {
for _, g := range pkg.GoFiles {
if sameFile(f, g) {
response.addRoot(id)
}
}
}
}
}
// Add root for any package that matches a pattern. This applies only to
// packages that are modified by overlays, since they are not added as
// roots automatically.
for _, pattern := range restPatterns {
match := matchPattern(pattern)
for _, pkgID := range modifiedPkgs {
pkg, ok := response.seenPackages[pkgID]
if !ok {
continue
}
if match(pkg.PkgPath) {
response.addRoot(pkg.ID)
}
}
}
}
sizeswg.Wait() sizeswg.Wait()
if sizeserr != nil { if sizeserr != nil {
return nil, sizeserr return nil, sizeserr
@ -273,24 +215,6 @@ extractQueries:
return response.dr, nil return response.dr, nil
} }
func (state *golistState) addNeededOverlayPackages(response *responseDeduper, pkgs []string) error {
if len(pkgs) == 0 {
return nil
}
dr, err := state.createDriverResponse(pkgs...)
if err != nil {
return err
}
for _, pkg := range dr.Packages {
response.addPackage(pkg)
}
_, needPkgs, err := state.processGolistOverlay(response)
if err != nil {
return err
}
return state.addNeededOverlayPackages(response, needPkgs)
}
func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error { func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error {
for _, query := range queries { for _, query := range queries {
// TODO(matloob): Do only one query per directory. // TODO(matloob): Do only one query per directory.
@ -671,6 +595,9 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
// Temporary work-around for golang/go#39986. Parse filenames out of // Temporary work-around for golang/go#39986. Parse filenames out of
// error messages. This happens if there are unrecoverable syntax // error messages. This happens if there are unrecoverable syntax
// errors in the source, so we can't match on a specific error message. // errors in the source, so we can't match on a specific error message.
//
// TODO(rfindley): remove this heuristic, in favor of considering
// InvalidGoFiles from the list driver.
if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) { if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) {
addFilenameFromPos := func(pos string) bool { addFilenameFromPos := func(pos string) bool {
split := strings.Split(pos, ":") split := strings.Split(pos, ":")
@ -1107,7 +1034,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err
if len(state.cfg.Overlay) == 0 { if len(state.cfg.Overlay) == 0 {
return "", func() {}, nil return "", func() {}, nil
} }
dir, err := ioutil.TempDir("", "gopackages-*") dir, err := os.MkdirTemp("", "gopackages-*")
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
@ -1126,7 +1053,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err
// Create a unique filename for the overlaid files, to avoid // Create a unique filename for the overlaid files, to avoid
// creating nested directories. // creating nested directories.
noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "") noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "")
f, err := ioutil.TempFile(dir, fmt.Sprintf("*-%s", noSeparator)) f, err := os.CreateTemp(dir, fmt.Sprintf("*-%s", noSeparator))
if err != nil { if err != nil {
return "", func() {}, err return "", func() {}, err
} }
@ -1144,7 +1071,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err
} }
// Write out the overlay file that contains the filepath mappings. // Write out the overlay file that contains the filepath mappings.
filename = filepath.Join(dir, "overlay.json") filename = filepath.Join(dir, "overlay.json")
if err := ioutil.WriteFile(filename, b, 0665); err != nil { if err := os.WriteFile(filename, b, 0665); err != nil {
return "", func() {}, err return "", func() {}, err
} }
return filename, cleanup, nil return filename, cleanup, nil

View File

@ -6,314 +6,11 @@ package packages
import ( import (
"encoding/json" "encoding/json"
"fmt"
"go/parser"
"go/token"
"os"
"path/filepath" "path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/gocommand"
) )
// processGolistOverlay provides rudimentary support for adding
// files that don't exist on disk to an overlay. The results can be
// sometimes incorrect.
// TODO(matloob): Handle unsupported cases, including the following:
// - determining the correct package to add given a new import path
func (state *golistState) processGolistOverlay(response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) {
havePkgs := make(map[string]string) // importPath -> non-test package ID
needPkgsSet := make(map[string]bool)
modifiedPkgsSet := make(map[string]bool)
pkgOfDir := make(map[string][]*Package)
for _, pkg := range response.dr.Packages {
// This is an approximation of import path to id. This can be
// wrong for tests, vendored packages, and a number of other cases.
havePkgs[pkg.PkgPath] = pkg.ID
dir, err := commonDir(pkg.GoFiles)
if err != nil {
return nil, nil, err
}
if dir != "" {
pkgOfDir[dir] = append(pkgOfDir[dir], pkg)
}
}
// If no new imports are added, it is safe to avoid loading any needPkgs.
// Otherwise, it's hard to tell which package is actually being loaded
// (due to vendoring) and whether any modified package will show up
// in the transitive set of dependencies (because new imports are added,
// potentially modifying the transitive set of dependencies).
var overlayAddsImports bool
// If both a package and its test package are created by the overlay, we
// need the real package first. Process all non-test files before test
// files, and make the whole process deterministic while we're at it.
var overlayFiles []string
for opath := range state.cfg.Overlay {
overlayFiles = append(overlayFiles, opath)
}
sort.Slice(overlayFiles, func(i, j int) bool {
iTest := strings.HasSuffix(overlayFiles[i], "_test.go")
jTest := strings.HasSuffix(overlayFiles[j], "_test.go")
if iTest != jTest {
return !iTest // non-tests are before tests.
}
return overlayFiles[i] < overlayFiles[j]
})
for _, opath := range overlayFiles {
contents := state.cfg.Overlay[opath]
base := filepath.Base(opath)
dir := filepath.Dir(opath)
var pkg *Package // if opath belongs to both a package and its test variant, this will be the test variant
var testVariantOf *Package // if opath is a test file, this is the package it is testing
var fileExists bool
isTestFile := strings.HasSuffix(opath, "_test.go")
pkgName, ok := extractPackageName(opath, contents)
if !ok {
// Don't bother adding a file that doesn't even have a parsable package statement
// to the overlay.
continue
}
// If all the overlay files belong to a different package, change the
// package name to that package.
maybeFixPackageName(pkgName, isTestFile, pkgOfDir[dir])
nextPackage:
for _, p := range response.dr.Packages {
if pkgName != p.Name && p.ID != "command-line-arguments" {
continue
}
for _, f := range p.GoFiles {
if !sameFile(filepath.Dir(f), dir) {
continue
}
// Make sure to capture information on the package's test variant, if needed.
if isTestFile && !hasTestFiles(p) {
// TODO(matloob): Are there packages other than the 'production' variant
// of a package that this can match? This shouldn't match the test main package
// because the file is generated in another directory.
testVariantOf = p
continue nextPackage
} else if !isTestFile && hasTestFiles(p) {
// We're examining a test variant, but the overlaid file is
// a non-test file. Because the overlay implementation
// (currently) only adds a file to one package, skip this
// package, so that we can add the file to the production
// variant of the package. (https://golang.org/issue/36857
// tracks handling overlays on both the production and test
// variant of a package).
continue nextPackage
}
if pkg != nil && p != pkg && pkg.PkgPath == p.PkgPath {
// We have already seen the production version of the
// for which p is a test variant.
if hasTestFiles(p) {
testVariantOf = pkg
}
}
pkg = p
if filepath.Base(f) == base {
fileExists = true
}
}
}
// The overlay could have included an entirely new package or an
// ad-hoc package. An ad-hoc package is one that we have manually
// constructed from inadequate `go list` results for a file= query.
// It will have the ID command-line-arguments.
if pkg == nil || pkg.ID == "command-line-arguments" {
// Try to find the module or gopath dir the file is contained in.
// Then for modules, add the module opath to the beginning.
pkgPath, ok, err := state.getPkgPath(dir)
if err != nil {
return nil, nil, err
}
if !ok {
break
}
var forTest string // only set for x tests
isXTest := strings.HasSuffix(pkgName, "_test")
if isXTest {
forTest = pkgPath
pkgPath += "_test"
}
id := pkgPath
if isTestFile {
if isXTest {
id = fmt.Sprintf("%s [%s.test]", pkgPath, forTest)
} else {
id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath)
}
}
if pkg != nil {
// TODO(rstambler): We should change the package's path and ID
// here. The only issue is that this messes with the roots.
} else {
// Try to reclaim a package with the same ID, if it exists in the response.
for _, p := range response.dr.Packages {
if reclaimPackage(p, id, opath, contents) {
pkg = p
break
}
}
// Otherwise, create a new package.
if pkg == nil {
pkg = &Package{
PkgPath: pkgPath,
ID: id,
Name: pkgName,
Imports: make(map[string]*Package),
}
response.addPackage(pkg)
havePkgs[pkg.PkgPath] = id
// Add the production package's sources for a test variant.
if isTestFile && !isXTest && testVariantOf != nil {
pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...)
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...)
// Add the package under test and its imports to the test variant.
pkg.forTest = testVariantOf.PkgPath
for k, v := range testVariantOf.Imports {
pkg.Imports[k] = &Package{ID: v.ID}
}
}
if isXTest {
pkg.forTest = forTest
}
}
}
}
if !fileExists {
pkg.GoFiles = append(pkg.GoFiles, opath)
// TODO(matloob): Adding the file to CompiledGoFiles can exhibit the wrong behavior
// if the file will be ignored due to its build tags.
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, opath)
modifiedPkgsSet[pkg.ID] = true
}
imports, err := extractImports(opath, contents)
if err != nil {
// Let the parser or type checker report errors later.
continue
}
for _, imp := range imports {
// TODO(rstambler): If the package is an x test and the import has
// a test variant, make sure to replace it.
if _, found := pkg.Imports[imp]; found {
continue
}
overlayAddsImports = true
id, ok := havePkgs[imp]
if !ok {
var err error
id, err = state.resolveImport(dir, imp)
if err != nil {
return nil, nil, err
}
}
pkg.Imports[imp] = &Package{ID: id}
// Add dependencies to the non-test variant version of this package as well.
if testVariantOf != nil {
testVariantOf.Imports[imp] = &Package{ID: id}
}
}
}
// toPkgPath guesses the package path given the id.
toPkgPath := func(sourceDir, id string) (string, error) {
if i := strings.IndexByte(id, ' '); i >= 0 {
return state.resolveImport(sourceDir, id[:i])
}
return state.resolveImport(sourceDir, id)
}
// Now that new packages have been created, do another pass to determine
// the new set of missing packages.
for _, pkg := range response.dr.Packages {
for _, imp := range pkg.Imports {
if len(pkg.GoFiles) == 0 {
return nil, nil, fmt.Errorf("cannot resolve imports for package %q with no Go files", pkg.PkgPath)
}
pkgPath, err := toPkgPath(filepath.Dir(pkg.GoFiles[0]), imp.ID)
if err != nil {
return nil, nil, err
}
if _, ok := havePkgs[pkgPath]; !ok {
needPkgsSet[pkgPath] = true
}
}
}
if overlayAddsImports {
needPkgs = make([]string, 0, len(needPkgsSet))
for pkg := range needPkgsSet {
needPkgs = append(needPkgs, pkg)
}
}
modifiedPkgs = make([]string, 0, len(modifiedPkgsSet))
for pkg := range modifiedPkgsSet {
modifiedPkgs = append(modifiedPkgs, pkg)
}
return modifiedPkgs, needPkgs, err
}
// resolveImport finds the ID of a package given its import path.
// In particular, it will find the right vendored copy when in GOPATH mode.
func (state *golistState) resolveImport(sourceDir, importPath string) (string, error) {
env, err := state.getEnv()
if err != nil {
return "", err
}
if env["GOMOD"] != "" {
return importPath, nil
}
searchDir := sourceDir
for {
vendorDir := filepath.Join(searchDir, "vendor")
exists, ok := state.vendorDirs[vendorDir]
if !ok {
info, err := os.Stat(vendorDir)
exists = err == nil && info.IsDir()
state.vendorDirs[vendorDir] = exists
}
if exists {
vendoredPath := filepath.Join(vendorDir, importPath)
if info, err := os.Stat(vendoredPath); err == nil && info.IsDir() {
// We should probably check for .go files here, but shame on anyone who fools us.
path, ok, err := state.getPkgPath(vendoredPath)
if err != nil {
return "", err
}
if ok {
return path, nil
}
}
}
// We know we've hit the top of the filesystem when we Dir / and get /,
// or C:\ and get C:\, etc.
next := filepath.Dir(searchDir)
if next == searchDir {
break
}
searchDir = next
}
return importPath, nil
}
func hasTestFiles(p *Package) bool {
for _, f := range p.GoFiles {
if strings.HasSuffix(f, "_test.go") {
return true
}
}
return false
}
// determineRootDirs returns a mapping from absolute directories that could // determineRootDirs returns a mapping from absolute directories that could
// contain code to their corresponding import path prefixes. // contain code to their corresponding import path prefixes.
func (state *golistState) determineRootDirs() (map[string]string, error) { func (state *golistState) determineRootDirs() (map[string]string, error) {
@ -384,192 +81,3 @@ func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) {
} }
return m, nil return m, nil
} }
func extractImports(filename string, contents []byte) ([]string, error) {
f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.ImportsOnly) // TODO(matloob): reuse fileset?
if err != nil {
return nil, err
}
var res []string
for _, imp := range f.Imports {
quotedPath := imp.Path.Value
path, err := strconv.Unquote(quotedPath)
if err != nil {
return nil, err
}
res = append(res, path)
}
return res, nil
}
// reclaimPackage attempts to reuse a package that failed to load in an overlay.
//
// If the package has errors and has no Name, GoFiles, or Imports,
// then it's possible that it doesn't yet exist on disk.
func reclaimPackage(pkg *Package, id string, filename string, contents []byte) bool {
// TODO(rstambler): Check the message of the actual error?
// It differs between $GOPATH and module mode.
if pkg.ID != id {
return false
}
if len(pkg.Errors) != 1 {
return false
}
if pkg.Name != "" || pkg.ExportFile != "" {
return false
}
if len(pkg.GoFiles) > 0 || len(pkg.CompiledGoFiles) > 0 || len(pkg.OtherFiles) > 0 {
return false
}
if len(pkg.Imports) > 0 {
return false
}
pkgName, ok := extractPackageName(filename, contents)
if !ok {
return false
}
pkg.Name = pkgName
pkg.Errors = nil
return true
}
func extractPackageName(filename string, contents []byte) (string, bool) {
// TODO(rstambler): Check the message of the actual error?
// It differs between $GOPATH and module mode.
f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.PackageClauseOnly) // TODO(matloob): reuse fileset?
if err != nil {
return "", false
}
return f.Name.Name, true
}
// commonDir returns the directory that all files are in, "" if files is empty,
// or an error if they aren't in the same directory.
func commonDir(files []string) (string, error) {
seen := make(map[string]bool)
for _, f := range files {
seen[filepath.Dir(f)] = true
}
if len(seen) > 1 {
return "", fmt.Errorf("files (%v) are in more than one directory: %v", files, seen)
}
for k := range seen {
// seen has only one element; return it.
return k, nil
}
return "", nil // no files
}
// It is possible that the files in the disk directory dir have a different package
// name from newName, which is deduced from the overlays. If they all have a different
// package name, and they all have the same package name, then that name becomes
// the package name.
// It returns true if it changes the package name, false otherwise.
func maybeFixPackageName(newName string, isTestFile bool, pkgsOfDir []*Package) {
names := make(map[string]int)
for _, p := range pkgsOfDir {
names[p.Name]++
}
if len(names) != 1 {
// some files are in different packages
return
}
var oldName string
for k := range names {
oldName = k
}
if newName == oldName {
return
}
// We might have a case where all of the package names in the directory are
// the same, but the overlay file is for an x test, which belongs to its
// own package. If the x test does not yet exist on disk, we may not yet
// have its package name on disk, but we should not rename the packages.
//
// We use a heuristic to determine if this file belongs to an x test:
// The test file should have a package name whose package name has a _test
// suffix or looks like "newName_test".
maybeXTest := strings.HasPrefix(oldName+"_test", newName) || strings.HasSuffix(newName, "_test")
if isTestFile && maybeXTest {
return
}
for _, p := range pkgsOfDir {
p.Name = newName
}
}
// This function is copy-pasted from
// https://github.com/golang/go/blob/9706f510a5e2754595d716bd64be8375997311fb/src/cmd/go/internal/search/search.go#L360.
// It should be deleted when we remove support for overlays from go/packages.
//
// NOTE: This does not handle any ./... or ./ style queries, as this function
// doesn't know the working directory.
//
// matchPattern(pattern)(name) reports whether
// name matches pattern. Pattern is a limited glob
// pattern in which '...' means 'any string' and there
// is no other special syntax.
// Unfortunately, there are two special cases. Quoting "go help packages":
//
// First, /... at the end of the pattern can match an empty string,
// so that net/... matches both net and packages in its subdirectories, like net/http.
// Second, any slash-separated pattern element containing a wildcard never
// participates in a match of the "vendor" element in the path of a vendored
// package, so that ./... does not match packages in subdirectories of
// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
// Note, however, that a directory named vendor that itself contains code
// is not a vendored package: cmd/vendor would be a command named vendor,
// and the pattern cmd/... matches it.
func matchPattern(pattern string) func(name string) bool {
// Convert pattern to regular expression.
// The strategy for the trailing /... is to nest it in an explicit ? expression.
// The strategy for the vendor exclusion is to change the unmatchable
// vendor strings to a disallowed code point (vendorChar) and to use
// "(anything but that codepoint)*" as the implementation of the ... wildcard.
// This is a bit complicated but the obvious alternative,
// namely a hand-written search like in most shell glob matchers,
// is too easy to make accidentally exponential.
// Using package regexp guarantees linear-time matching.
const vendorChar = "\x00"
if strings.Contains(pattern, vendorChar) {
return func(name string) bool { return false }
}
re := regexp.QuoteMeta(pattern)
re = replaceVendor(re, vendorChar)
switch {
case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`):
re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)`
case re == vendorChar+`/\.\.\.`:
re = `(/vendor|/` + vendorChar + `/\.\.\.)`
case strings.HasSuffix(re, `/\.\.\.`):
re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?`
}
re = strings.ReplaceAll(re, `\.\.\.`, `[^`+vendorChar+`]*`)
reg := regexp.MustCompile(`^` + re + `$`)
return func(name string) bool {
if strings.Contains(name, vendorChar) {
return false
}
return reg.MatchString(replaceVendor(name, vendorChar))
}
}
// replaceVendor returns the result of replacing
// non-trailing vendor path elements in x with repl.
func replaceVendor(x, repl string) string {
if !strings.Contains(x, "vendor") {
return x
}
elem := strings.Split(x, "/")
for i := 0; i < len(elem)-1; i++ {
if elem[i] == "vendor" {
elem[i] = repl
}
}
return strings.Join(elem, "/")
}

View File

@ -16,7 +16,6 @@ import (
"go/token" "go/token"
"go/types" "go/types"
"io" "io"
"io/ioutil"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@ -30,6 +29,7 @@ import (
"golang.org/x/tools/internal/packagesinternal" "golang.org/x/tools/internal/packagesinternal"
"golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/typesinternal"
"golang.org/x/tools/internal/versions"
) )
// A LoadMode controls the amount of detail to return when loading. // A LoadMode controls the amount of detail to return when loading.
@ -220,8 +220,10 @@ type driverResponse struct {
// lists of multiple drivers, go/packages will fall back to the next driver. // lists of multiple drivers, go/packages will fall back to the next driver.
NotHandled bool NotHandled bool
// Sizes, if not nil, is the types.Sizes to use when type checking. // Compiler and Arch are the arguments pass of types.SizesFor
Sizes *types.StdSizes // to get a types.Sizes to use when type checking.
Compiler string
Arch string
// Roots is the set of package IDs that make up the root packages. // Roots is the set of package IDs that make up the root packages.
// We have to encode this separately because when we encode a single package // We have to encode this separately because when we encode a single package
@ -257,31 +259,52 @@ type driverResponse struct {
// proceeding with further analysis. The PrintErrors function is // proceeding with further analysis. The PrintErrors function is
// provided for convenient display of all errors. // provided for convenient display of all errors.
func Load(cfg *Config, patterns ...string) ([]*Package, error) { func Load(cfg *Config, patterns ...string) ([]*Package, error) {
l := newLoader(cfg) ld := newLoader(cfg)
response, err := defaultDriver(&l.Config, patterns...) response, external, err := defaultDriver(&ld.Config, patterns...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
l.sizes = response.Sizes
return l.refine(response) ld.sizes = types.SizesFor(response.Compiler, response.Arch)
if ld.sizes == nil && ld.Config.Mode&(NeedTypes|NeedTypesSizes|NeedTypesInfo) != 0 {
// Type size information is needed but unavailable.
if external {
// An external driver may fail to populate the Compiler/GOARCH fields,
// especially since they are relatively new (see #63700).
// Provide a sensible fallback in this case.
ld.sizes = types.SizesFor("gc", runtime.GOARCH)
if ld.sizes == nil { // gccgo-only arch
ld.sizes = types.SizesFor("gc", "amd64")
}
} else {
// Go list should never fail to deliver accurate size information.
// Reject the whole Load since the error is the same for every package.
return nil, fmt.Errorf("can't determine type sizes for compiler %q on GOARCH %q",
response.Compiler, response.Arch)
}
}
return ld.refine(response)
} }
// defaultDriver is a driver that implements go/packages' fallback behavior. // defaultDriver is a driver that implements go/packages' fallback behavior.
// It will try to request to an external driver, if one exists. If there's // It will try to request to an external driver, if one exists. If there's
// no external driver, or the driver returns a response with NotHandled set, // no external driver, or the driver returns a response with NotHandled set,
// defaultDriver will fall back to the go list driver. // defaultDriver will fall back to the go list driver.
func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) { // The boolean result indicates that an external driver handled the request.
driver := findExternalDriver(cfg) func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, bool, error) {
if driver == nil { if driver := findExternalDriver(cfg); driver != nil {
driver = goListDriver response, err := driver(cfg, patterns...)
if err != nil {
return nil, false, err
} else if !response.NotHandled {
return response, true, nil
}
// (fall through)
} }
response, err := driver(cfg, patterns...)
if err != nil { response, err := goListDriver(cfg, patterns...)
return response, err return response, false, err
} else if response.NotHandled {
return goListDriver(cfg, patterns...)
}
return response, nil
} }
// A Package describes a loaded Go package. // A Package describes a loaded Go package.
@ -410,12 +433,6 @@ func init() {
packagesinternal.GetDepsErrors = func(p interface{}) []*packagesinternal.PackageError { packagesinternal.GetDepsErrors = func(p interface{}) []*packagesinternal.PackageError {
return p.(*Package).depsErrors return p.(*Package).depsErrors
} }
packagesinternal.GetGoCmdRunner = func(config interface{}) *gocommand.Runner {
return config.(*Config).gocmdRunner
}
packagesinternal.SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {
config.(*Config).gocmdRunner = runner
}
packagesinternal.SetModFile = func(config interface{}, value string) { packagesinternal.SetModFile = func(config interface{}, value string) {
config.(*Config).modFile = value config.(*Config).modFile = value
} }
@ -552,7 +569,7 @@ type loaderPackage struct {
type loader struct { type loader struct {
pkgs map[string]*loaderPackage pkgs map[string]*loaderPackage
Config Config
sizes types.Sizes sizes types.Sizes // non-nil if needed by mode
parseCache map[string]*parseValue parseCache map[string]*parseValue
parseCacheMu sync.Mutex parseCacheMu sync.Mutex
exportMu sync.Mutex // enforces mutual exclusion of exportdata operations exportMu sync.Mutex // enforces mutual exclusion of exportdata operations
@ -630,7 +647,7 @@ func newLoader(cfg *Config) *loader {
return ld return ld
} }
// refine connects the supplied packages into a graph and then adds type and // refine connects the supplied packages into a graph and then adds type
// and syntax information as requested by the LoadMode. // and syntax information as requested by the LoadMode.
func (ld *loader) refine(response *driverResponse) ([]*Package, error) { func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
roots := response.Roots roots := response.Roots
@ -677,39 +694,38 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
} }
} }
// Materialize the import graph. if ld.Mode&NeedImports != 0 {
// Materialize the import graph.
const ( const (
white = 0 // new white = 0 // new
grey = 1 // in progress grey = 1 // in progress
black = 2 // complete black = 2 // complete
) )
// visit traverses the import graph, depth-first, // visit traverses the import graph, depth-first,
// and materializes the graph as Packages.Imports. // and materializes the graph as Packages.Imports.
// //
// Valid imports are saved in the Packages.Import map. // Valid imports are saved in the Packages.Import map.
// Invalid imports (cycles and missing nodes) are saved in the importErrors map. // Invalid imports (cycles and missing nodes) are saved in the importErrors map.
// Thus, even in the presence of both kinds of errors, the Import graph remains a DAG. // Thus, even in the presence of both kinds of errors,
// // the Import graph remains a DAG.
// visit returns whether the package needs src or has a transitive //
// dependency on a package that does. These are the only packages // visit returns whether the package needs src or has a transitive
// for which we load source code. // dependency on a package that does. These are the only packages
var stack []*loaderPackage // for which we load source code.
var visit func(lpkg *loaderPackage) bool var stack []*loaderPackage
var srcPkgs []*loaderPackage var visit func(lpkg *loaderPackage) bool
visit = func(lpkg *loaderPackage) bool { visit = func(lpkg *loaderPackage) bool {
switch lpkg.color { switch lpkg.color {
case black: case black:
return lpkg.needsrc return lpkg.needsrc
case grey: case grey:
panic("internal error: grey node") panic("internal error: grey node")
} }
lpkg.color = grey lpkg.color = grey
stack = append(stack, lpkg) // push stack = append(stack, lpkg) // push
stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports
// If NeedImports isn't set, the imports fields will all be zeroed out.
if ld.Mode&NeedImports != 0 {
lpkg.Imports = make(map[string]*Package, len(stubs)) lpkg.Imports = make(map[string]*Package, len(stubs))
for importPath, ipkg := range stubs { for importPath, ipkg := range stubs {
var importErr error var importErr error
@ -733,40 +749,39 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
} }
lpkg.Imports[importPath] = imp.Package lpkg.Imports[importPath] = imp.Package
} }
}
if lpkg.needsrc {
srcPkgs = append(srcPkgs, lpkg)
}
if ld.Mode&NeedTypesSizes != 0 {
lpkg.TypesSizes = ld.sizes
}
stack = stack[:len(stack)-1] // pop
lpkg.color = black
return lpkg.needsrc // 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
}
}
if ld.Mode&NeedImports == 0 { // NeedTypeSizes causes TypeSizes to be set even
// We do this to drop the stub import packages that we are not even going to try to resolve. // on packages for which types aren't needed.
for _, lpkg := range initial { if ld.Mode&NeedTypesSizes != 0 {
lpkg.Imports = nil lpkg.TypesSizes = ld.sizes
}
stack = stack[:len(stack)-1] // pop
lpkg.color = black
return lpkg.needsrc
} }
} else {
// For each initial package, create its import DAG. // For each initial package, create its import DAG.
for _, lpkg := range initial { for _, lpkg := range initial {
visit(lpkg) visit(lpkg)
} }
}
if ld.Mode&NeedImports != 0 && ld.Mode&NeedTypes != 0 { } else {
for _, lpkg := range srcPkgs { // !NeedImports: drop the stub (ID-only) import packages
// Complete type information is required for the // that we are not even going to try to resolve.
// immediate dependencies of each source package. for _, lpkg := range initial {
for _, ipkg := range lpkg.Imports { lpkg.Imports = nil
imp := ld.pkgs[ipkg.ID]
imp.needtypes = true
}
} }
} }
// Load type data and syntax if needed, starting at // Load type data and syntax if needed, starting at
// the initial packages (roots of the import DAG). // the initial packages (roots of the import DAG).
if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 { if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 {
@ -1004,6 +1019,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
Selections: make(map[*ast.SelectorExpr]*types.Selection), Selections: make(map[*ast.SelectorExpr]*types.Selection),
} }
typeparams.InitInstanceInfo(lpkg.TypesInfo) typeparams.InitInstanceInfo(lpkg.TypesInfo)
versions.InitFileVersions(lpkg.TypesInfo)
lpkg.TypesSizes = ld.sizes lpkg.TypesSizes = ld.sizes
importer := importerFunc(func(path string) (*types.Package, error) { importer := importerFunc(func(path string) (*types.Package, error) {
@ -1041,7 +1057,10 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
IgnoreFuncBodies: ld.Mode&NeedDeps == 0 && !lpkg.initial, IgnoreFuncBodies: ld.Mode&NeedDeps == 0 && !lpkg.initial,
Error: appendError, Error: appendError,
Sizes: ld.sizes, Sizes: ld.sizes, // may be nil
}
if lpkg.Module != nil && lpkg.Module.GoVersion != "" {
typesinternal.SetGoVersion(tc, "go"+lpkg.Module.GoVersion)
} }
if (ld.Mode & typecheckCgo) != 0 { if (ld.Mode & typecheckCgo) != 0 {
if !typesinternal.SetUsesCgo(tc) { if !typesinternal.SetUsesCgo(tc) {
@ -1122,7 +1141,7 @@ func (ld *loader) parseFile(filename string) (*ast.File, error) {
var err error var err error
if src == nil { if src == nil {
ioLimit <- true // wait ioLimit <- true // wait
src, err = ioutil.ReadFile(filename) src, err = os.ReadFile(filename)
<-ioLimit // signal <-ioLimit // signal
} }
if err != nil { if err != nil {

View File

@ -0,0 +1,752 @@
// Copyright 2018 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 objectpath defines a naming scheme for types.Objects
// (that is, named entities in Go programs) relative to their enclosing
// package.
//
// Type-checker objects are canonical, so they are usually identified by
// their address in memory (a pointer), but a pointer has meaning only
// within one address space. By contrast, objectpath names allow the
// identity of an object to be sent from one program to another,
// establishing a correspondence between types.Object variables that are
// distinct but logically equivalent.
//
// A single object may have multiple paths. In this example,
//
// type A struct{ X int }
// type B A
//
// the field X has two paths due to its membership of both A and B.
// The For(obj) function always returns one of these paths, arbitrarily
// but consistently.
package objectpath
import (
"fmt"
"go/types"
"strconv"
"strings"
"golang.org/x/tools/internal/typeparams"
)
// A Path is an opaque name that identifies a types.Object
// relative to its package. Conceptually, the name consists of a
// sequence of destructuring operations applied to the package scope
// to obtain the original object.
// The name does not include the package itself.
type Path string
// Encoding
//
// An object path is a textual and (with training) human-readable encoding
// of a sequence of destructuring operators, starting from a types.Package.
// The sequences represent a path through the package/object/type graph.
// We classify these operators by their type:
//
// PO package->object Package.Scope.Lookup
// OT object->type Object.Type
// TT type->type Type.{Elem,Key,Params,Results,Underlying} [EKPRU]
// TO type->object Type.{At,Field,Method,Obj} [AFMO]
//
// All valid paths start with a package and end at an object
// and thus may be defined by the regular language:
//
// objectpath = PO (OT TT* TO)*
//
// The concrete encoding follows directly:
// - The only PO operator is Package.Scope.Lookup, which requires an identifier.
// - The only OT operator is Object.Type,
// which we encode as '.' because dot cannot appear in an identifier.
// - The TT operators are encoded as [EKPRUTC];
// one of these (TypeParam) requires an integer operand,
// which is encoded as a string of decimal digits.
// - The TO operators are encoded as [AFMO];
// three of these (At,Field,Method) require an integer operand,
// which is encoded as a string of decimal digits.
// These indices are stable across different representations
// of the same package, even source and export data.
// The indices used are implementation specific and may not correspond to
// the argument to the go/types function.
//
// In the example below,
//
// package p
//
// type T interface {
// f() (a string, b struct{ X int })
// }
//
// field X has the path "T.UM0.RA1.F0",
// representing the following sequence of operations:
//
// p.Lookup("T") T
// .Type().Underlying().Method(0). f
// .Type().Results().At(1) b
// .Type().Field(0) X
//
// The encoding is not maximally compact---every R or P is
// followed by an A, for example---but this simplifies the
// encoder and decoder.
const (
// object->type operators
opType = '.' // .Type() (Object)
// type->type operators
opElem = 'E' // .Elem() (Pointer, Slice, Array, Chan, Map)
opKey = 'K' // .Key() (Map)
opParams = 'P' // .Params() (Signature)
opResults = 'R' // .Results() (Signature)
opUnderlying = 'U' // .Underlying() (Named)
opTypeParam = 'T' // .TypeParams.At(i) (Named, Signature)
opConstraint = 'C' // .Constraint() (TypeParam)
// type->object operators
opAt = 'A' // .At(i) (Tuple)
opField = 'F' // .Field(i) (Struct)
opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored)
opObj = 'O' // .Obj() (Named, TypeParam)
)
// For is equivalent to new(Encoder).For(obj).
//
// It may be more efficient to reuse a single Encoder across several calls.
func For(obj types.Object) (Path, error) {
return new(Encoder).For(obj)
}
// An Encoder amortizes the cost of encoding the paths of multiple objects.
// The zero value of an Encoder is ready to use.
type Encoder struct {
scopeMemo map[*types.Scope][]types.Object // memoization of scopeObjects
}
// For returns the path to an object relative to its package,
// or an error if the object is not accessible from the package's Scope.
//
// The For function guarantees to return a path only for the following objects:
// - package-level types
// - exported package-level non-types
// - methods
// - parameter and result variables
// - struct fields
// These objects are sufficient to define the API of their package.
// The objects described by a package's export data are drawn from this set.
//
// The set of objects accessible from a package's Scope depends on
// whether the package was produced by type-checking syntax, or
// reading export data; the latter may have a smaller Scope since
// export data trims objects that are not reachable from an exported
// declaration. For example, the For function will return a path for
// an exported method of an unexported type that is not reachable
// from any public declaration; this path will cause the Object
// function to fail if called on a package loaded from export data.
// TODO(adonovan): is this a bug or feature? Should this package
// compute accessibility in the same way?
//
// For does not return a path for predeclared names, imported package
// names, local names, and unexported package-level names (except
// types).
//
// Example: given this definition,
//
// package p
//
// type T interface {
// f() (a string, b struct{ X int })
// }
//
// For(X) would return a path that denotes the following sequence of operations:
//
// p.Scope().Lookup("T") (TypeName T)
// .Type().Underlying().Method(0). (method Func f)
// .Type().Results().At(1) (field Var b)
// .Type().Field(0) (field Var X)
//
// where p is the package (*types.Package) to which X belongs.
func (enc *Encoder) For(obj types.Object) (Path, error) {
pkg := obj.Pkg()
// This table lists the cases of interest.
//
// Object Action
// ------ ------
// nil reject
// builtin reject
// pkgname reject
// label reject
// var
// package-level accept
// func param/result accept
// local reject
// struct field accept
// const
// package-level accept
// local reject
// func
// package-level accept
// init functions reject
// concrete method accept
// interface method accept
// type
// package-level accept
// local reject
//
// The only accessible package-level objects are members of pkg itself.
//
// The cases are handled in four steps:
//
// 1. reject nil and builtin
// 2. accept package-level objects
// 3. reject obviously invalid objects
// 4. search the API for the path to the param/result/field/method.
// 1. reference to nil or builtin?
if pkg == nil {
return "", fmt.Errorf("predeclared %s has no path", obj)
}
scope := pkg.Scope()
// 2. package-level object?
if scope.Lookup(obj.Name()) == obj {
// Only exported objects (and non-exported types) have a path.
// Non-exported types may be referenced by other objects.
if _, ok := obj.(*types.TypeName); !ok && !obj.Exported() {
return "", fmt.Errorf("no path for non-exported %v", obj)
}
return Path(obj.Name()), nil
}
// 3. Not a package-level object.
// Reject obviously non-viable cases.
switch obj := obj.(type) {
case *types.TypeName:
if _, ok := obj.Type().(*typeparams.TypeParam); !ok {
// With the exception of type parameters, only package-level type names
// have a path.
return "", fmt.Errorf("no path for %v", obj)
}
case *types.Const, // Only package-level constants have a path.
*types.Label, // Labels are function-local.
*types.PkgName: // PkgNames are file-local.
return "", fmt.Errorf("no path for %v", obj)
case *types.Var:
// Could be:
// - a field (obj.IsField())
// - a func parameter or result
// - a local var.
// Sadly there is no way to distinguish
// a param/result from a local
// so we must proceed to the find.
case *types.Func:
// A func, if not package-level, must be a method.
if recv := obj.Type().(*types.Signature).Recv(); recv == nil {
return "", fmt.Errorf("func is not a method: %v", obj)
}
if path, ok := enc.concreteMethod(obj); ok {
// Fast path for concrete methods that avoids looping over scope.
return path, nil
}
default:
panic(obj)
}
// 4. Search the API for the path to the var (field/param/result) or method.
// First inspect package-level named types.
// In the presence of path aliases, these give
// the best paths because non-types may
// refer to types, but not the reverse.
empty := make([]byte, 0, 48) // initial space
objs := enc.scopeObjects(scope)
for _, o := range objs {
tname, ok := o.(*types.TypeName)
if !ok {
continue // handle non-types in second pass
}
path := append(empty, o.Name()...)
path = append(path, opType)
T := o.Type()
if tname.IsAlias() {
// type alias
if r := find(obj, T, path, nil); r != nil {
return Path(r), nil
}
} else {
if named, _ := T.(*types.Named); named != nil {
if r := findTypeParam(obj, typeparams.ForNamed(named), path, nil); r != nil {
// generic named type
return Path(r), nil
}
}
// defined (named) type
if r := find(obj, T.Underlying(), append(path, opUnderlying), nil); r != nil {
return Path(r), nil
}
}
}
// Then inspect everything else:
// non-types, and declared methods of defined types.
for _, o := range objs {
path := append(empty, o.Name()...)
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 {
return Path(r), nil
}
}
continue
}
// Inspect declared methods of defined types.
if T, ok := o.Type().(*types.Named); ok {
path = append(path, opType)
// The method index here is always with respect
// to the underlying go/types data structures,
// which ultimately derives from source order
// and must be preserved by export data.
for i := 0; i < T.NumMethods(); i++ {
m := T.Method(i)
path2 := appendOpArg(path, opMethod, i)
if m == obj {
return Path(path2), nil // found declared method
}
if r := find(obj, m.Type(), append(path2, opType), nil); r != nil {
return Path(r), nil
}
}
}
}
return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path())
}
func appendOpArg(path []byte, op byte, arg int) []byte {
path = append(path, op)
path = strconv.AppendInt(path, int64(arg), 10)
return path
}
// concreteMethod returns the path for meth, which must have a non-nil receiver.
// The second return value indicates success and may be false if the method is
// an interface method or if it is an instantiated method.
//
// This function is just an optimization that avoids the general scope walking
// approach. You are expected to fall back to the general approach if this
// function fails.
func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
// Concrete methods can only be declared on package-scoped named types. For
// that reason we can skip the expensive walk over the package scope: the
// path will always be package -> named type -> method. We can trivially get
// the type name from the receiver, and only have to look over the type's
// methods to find the method index.
//
// Methods on generic types require special consideration, however. Consider
// the following package:
//
// L1: type S[T any] struct{}
// L2: func (recv S[A]) Foo() { recv.Bar() }
// L3: func (recv S[B]) Bar() { }
// L4: type Alias = S[int]
// L5: func _[T any]() { var s S[int]; s.Foo() }
//
// The receivers of methods on generic types are instantiations. L2 and L3
// instantiate S with the type-parameters A and B, which are scoped to the
// respective methods. L4 and L5 each instantiate S with int. Each of these
// instantiations has its own method set, full of methods (and thus objects)
// with receivers whose types are the respective instantiations. In other
// words, we have
//
// S[A].Foo, S[A].Bar
// S[B].Foo, S[B].Bar
// S[int].Foo, S[int].Bar
//
// We may thus be trying to produce object paths for any of these objects.
//
// S[A].Foo and S[B].Bar are the origin methods, and their paths are S.Foo
// and S.Bar, which are the paths that this function naturally produces.
//
// S[A].Bar, S[B].Foo, and both methods on S[int] are instantiations that
// don't correspond to the origin methods. For S[int], this is significant.
// The most precise object path for S[int].Foo, for example, is Alias.Foo,
// not S.Foo. Our function, however, would produce S.Foo, which would
// resolve to a different object.
//
// For S[A].Bar and S[B].Foo it could be argued that S.Bar and S.Foo are
// still the correct paths, since only the origin methods have meaningful
// paths. But this is likely only true for trivial cases and has edge cases.
// Since this function is only an optimization, we err on the side of giving
// up, deferring to the slower but definitely correct algorithm. Most users
// of objectpath will only be giving us origin methods, anyway, as referring
// to instantiated methods is usually not useful.
if typeparams.OriginMethod(meth) != meth {
return "", false
}
recvT := meth.Type().(*types.Signature).Recv().Type()
if ptr, ok := recvT.(*types.Pointer); ok {
recvT = ptr.Elem()
}
named, ok := recvT.(*types.Named)
if !ok {
return "", false
}
if types.IsInterface(named) {
// Named interfaces don't have to be package-scoped
//
// TODO(dominikh): opt: if scope.Lookup(name) == named, then we can apply this optimization to interface
// methods, too, I think.
return "", false
}
// Preallocate space for the name, opType, opMethod, and some digits.
name := named.Obj().Name()
path := make([]byte, 0, len(name)+8)
path = append(path, name...)
path = append(path, opType)
// Method indices are w.r.t. the go/types data structures,
// ultimately deriving from source order,
// which is preserved by export data.
for i := 0; i < named.NumMethods(); i++ {
if named.Method(i) == meth {
path = appendOpArg(path, opMethod, i)
return Path(path), true
}
}
// Due to golang/go#59944, go/types fails to associate the receiver with
// certain methods on cgo types.
//
// TODO(rfindley): replace this panic once golang/go#59944 is fixed in all Go
// versions gopls supports.
return "", false
// panic(fmt.Sprintf("couldn't find method %s on type %s; methods: %#v", meth, named, enc.namedMethods(named)))
}
// find finds obj within type T, returning the path to it, or nil if not found.
//
// 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 {
switch T := T.(type) {
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)
case *types.Slice:
return find(obj, T.Elem(), append(path, opElem), seen)
case *types.Array:
return find(obj, T.Elem(), append(path, opElem), seen)
case *types.Chan:
return find(obj, T.Elem(), append(path, opElem), seen)
case *types.Map:
if r := find(obj, T.Key(), append(path, opKey), seen); r != nil {
return r
}
return find(obj, T.Elem(), append(path, opElem), seen)
case *types.Signature:
if r := findTypeParam(obj, typeparams.ForSignature(T), path, seen); r != nil {
return r
}
if r := find(obj, T.Params(), append(path, opParams), seen); r != nil {
return r
}
return find(obj, T.Results(), append(path, opResults), seen)
case *types.Struct:
for i := 0; i < T.NumFields(); i++ {
fld := T.Field(i)
path2 := appendOpArg(path, opField, i)
if fld == obj {
return path2 // found field var
}
if r := find(obj, fld.Type(), append(path2, opType), seen); r != nil {
return r
}
}
return nil
case *types.Tuple:
for i := 0; i < T.Len(); i++ {
v := T.At(i)
path2 := appendOpArg(path, opAt, i)
if v == obj {
return path2 // found param/result var
}
if r := find(obj, v.Type(), append(path2, opType), seen); r != nil {
return r
}
}
return nil
case *types.Interface:
for i := 0; i < T.NumMethods(); i++ {
m := T.Method(i)
path2 := appendOpArg(path, opMethod, i)
if m == obj {
return path2 // found interface method
}
if r := find(obj, m.Type(), append(path2, opType), seen); r != nil {
return r
}
}
return nil
case *typeparams.TypeParam:
name := T.Obj()
if name == obj {
return append(path, opObj)
}
if seen[name] {
return nil
}
if seen == nil {
seen = make(map[*types.TypeName]bool)
}
seen[name] = true
if r := find(obj, T.Constraint(), append(path, opConstraint), seen); r != nil {
return r
}
return nil
}
panic(T)
}
func findTypeParam(obj types.Object, list *typeparams.TypeParamList, path []byte, seen map[*types.TypeName]bool) []byte {
for i := 0; i < list.Len(); i++ {
tparam := list.At(i)
path2 := appendOpArg(path, opTypeParam, i)
if r := find(obj, tparam, path2, seen); r != nil {
return r
}
}
return nil
}
// Object returns the object denoted by path p within the package pkg.
func Object(pkg *types.Package, p Path) (types.Object, error) {
pathstr := string(p)
if pathstr == "" {
return nil, fmt.Errorf("empty path")
}
var pkgobj, suffix string
if dot := strings.IndexByte(pathstr, opType); dot < 0 {
pkgobj = pathstr
} else {
pkgobj = pathstr[:dot]
suffix = pathstr[dot:] // suffix starts with "."
}
obj := pkg.Scope().Lookup(pkgobj)
if obj == nil {
return nil, fmt.Errorf("package %s does not contain %q", pkg.Path(), pkgobj)
}
// abstraction of *types.{Pointer,Slice,Array,Chan,Map}
type hasElem interface {
Elem() types.Type
}
// abstraction of *types.{Named,Signature}
type hasTypeParams interface {
TypeParams() *typeparams.TypeParamList
}
// abstraction of *types.{Named,TypeParam}
type hasObj interface {
Obj() *types.TypeName
}
// The loop state is the pair (t, obj),
// exactly one of which is non-nil, initially obj.
// All suffixes start with '.' (the only object->type operation),
// followed by optional type->type operations,
// then a type->object operation.
// The cycle then repeats.
var t types.Type
for suffix != "" {
code := suffix[0]
suffix = suffix[1:]
// Codes [AFM] have an integer operand.
var index int
switch code {
case opAt, opField, opMethod, opTypeParam:
rest := strings.TrimLeft(suffix, "0123456789")
numerals := suffix[:len(suffix)-len(rest)]
suffix = rest
i, err := strconv.Atoi(numerals)
if err != nil {
return nil, fmt.Errorf("invalid path: bad numeric operand %q for code %q", numerals, code)
}
index = int(i)
case opObj:
// no operand
default:
// The suffix must end with a type->object operation.
if suffix == "" {
return nil, fmt.Errorf("invalid path: ends with %q, want [AFMO]", code)
}
}
if code == opType {
if t != nil {
return nil, fmt.Errorf("invalid path: unexpected %q in type context", opType)
}
t = obj.Type()
obj = nil
continue
}
if t == nil {
return nil, fmt.Errorf("invalid path: code %q in object context", code)
}
// Inv: t != nil, obj == nil
switch code {
case opElem:
hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want pointer, slice, array, chan or map)", code, t, t)
}
t = hasElem.Elem()
case opKey:
mapType, ok := t.(*types.Map)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want map)", code, t, t)
}
t = mapType.Key()
case opParams:
sig, ok := t.(*types.Signature)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
}
t = sig.Params()
case opResults:
sig, ok := t.(*types.Signature)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
}
t = sig.Results()
case opUnderlying:
named, ok := t.(*types.Named)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named)", code, t, t)
}
t = named.Underlying()
case opTypeParam:
hasTypeParams, ok := t.(hasTypeParams) // Named, Signature
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or signature)", code, t, t)
}
tparams := hasTypeParams.TypeParams()
if n := tparams.Len(); index >= n {
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
}
t = tparams.At(index)
case opConstraint:
tparam, ok := t.(*typeparams.TypeParam)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want type parameter)", code, t, t)
}
t = tparam.Constraint()
case opAt:
tuple, ok := t.(*types.Tuple)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want tuple)", code, t, t)
}
if n := tuple.Len(); index >= n {
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
}
obj = tuple.At(index)
t = nil
case opField:
structType, ok := t.(*types.Struct)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want struct)", code, t, t)
}
if n := structType.NumFields(); index >= n {
return nil, fmt.Errorf("field index %d out of range [0-%d)", index, n)
}
obj = structType.Field(index)
t = nil
case opMethod:
switch t := t.(type) {
case *types.Interface:
if index >= t.NumMethods() {
return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods())
}
obj = t.Method(index) // Id-ordered
case *types.Named:
if index >= t.NumMethods() {
return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods())
}
obj = t.Method(index)
default:
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want interface or named)", code, t, t)
}
t = nil
case opObj:
hasObj, ok := t.(hasObj)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or type param)", code, t, t)
}
obj = hasObj.Obj()
t = nil
default:
return nil, fmt.Errorf("invalid path: unknown code %q", code)
}
}
if obj.Pkg() != pkg {
return nil, fmt.Errorf("path denotes %s, which belongs to a different package", obj)
}
return obj, nil // success
}
// scopeObjects is a memoization of scope objects.
// Callers must not modify the result.
func (enc *Encoder) scopeObjects(scope *types.Scope) []types.Object {
m := enc.scopeMemo
if m == nil {
m = make(map[*types.Scope][]types.Object)
enc.scopeMemo = m
}
objs, ok := m[scope]
if !ok {
names := scope.Names() // allocates and sorts
objs = make([]types.Object, len(names))
for i, name := range names {
objs[i] = scope.Lookup(name)
}
m[scope] = objs
}
return objs
}

View File

@ -19,7 +19,7 @@ var (
File = keys.NewString("file", "") File = keys.NewString("file", "")
Directory = keys.New("directory", "") Directory = keys.New("directory", "")
URI = keys.New("URI", "") URI = keys.New("URI", "")
Package = keys.NewString("package", "") // Package ID Package = keys.NewString("package", "") // sorted comma-separated list of Package IDs
PackagePath = keys.NewString("package_path", "") PackagePath = keys.NewString("package_path", "")
Query = keys.New("query", "") Query = keys.New("query", "")
Snapshot = keys.NewUInt64("snapshot", "") Snapshot = keys.NewUInt64("snapshot", "")

View File

@ -29,7 +29,6 @@ import (
"go/token" "go/token"
"go/types" "go/types"
"io" "io"
"io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -221,7 +220,7 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func
switch hdr { switch hdr {
case "$$B\n": case "$$B\n":
var data []byte var data []byte
data, err = ioutil.ReadAll(buf) data, err = io.ReadAll(buf)
if err != nil { if err != nil {
break break
} }

View File

@ -22,17 +22,23 @@ import (
"strconv" "strconv"
"strings" "strings"
"golang.org/x/tools/go/types/objectpath"
"golang.org/x/tools/internal/tokeninternal" "golang.org/x/tools/internal/tokeninternal"
"golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/typeparams"
) )
// IExportShallow encodes "shallow" export data for the specified package. // IExportShallow encodes "shallow" export data for the specified package.
// //
// No promises are made about the encoding other than that it can be // No promises are made about the encoding other than that it can be decoded by
// decoded by the same version of IIExportShallow. If you plan to save // the same version of IIExportShallow. If you plan to save export data in the
// export data in the file system, be sure to include a cryptographic // file system, be sure to include a cryptographic digest of the executable in
// digest of the executable in the key to avoid version skew. // the key to avoid version skew.
func IExportShallow(fset *token.FileSet, pkg *types.Package) ([]byte, error) { //
// If the provided reportf func is non-nil, it will be used for reporting bugs
// encountered during export.
// TODO(rfindley): remove reportf when we are confident enough in the new
// objectpath encoding.
func IExportShallow(fset *token.FileSet, pkg *types.Package, reportf ReportFunc) ([]byte, error) {
// In principle this operation can only fail if out.Write fails, // In principle this operation can only fail if out.Write fails,
// but that's impossible for bytes.Buffer---and as a matter of // but that's impossible for bytes.Buffer---and as a matter of
// fact iexportCommon doesn't even check for I/O errors. // fact iexportCommon doesn't even check for I/O errors.
@ -47,19 +53,27 @@ func IExportShallow(fset *token.FileSet, pkg *types.Package) ([]byte, error) {
// IImportShallow decodes "shallow" types.Package data encoded by // IImportShallow decodes "shallow" types.Package data encoded by
// IExportShallow in the same executable. This function cannot import data from // IExportShallow in the same executable. This function cannot import data from
// cmd/compile or gcexportdata.Write. // cmd/compile or gcexportdata.Write.
func IImportShallow(fset *token.FileSet, getPackage GetPackageFunc, data []byte, path string, insert InsertType) (*types.Package, error) { //
// The importer calls getPackages to obtain package symbols for all
// packages mentioned in the export data, including the one being
// decoded.
//
// If the provided reportf func is non-nil, it will be used for reporting bugs
// encountered during import.
// TODO(rfindley): remove reportf when we are confident enough in the new
// objectpath encoding.
func IImportShallow(fset *token.FileSet, getPackages GetPackagesFunc, data []byte, path string, reportf ReportFunc) (*types.Package, error) {
const bundle = false const bundle = false
pkgs, err := iimportCommon(fset, getPackage, data, bundle, path, insert) const shallow = true
pkgs, err := iimportCommon(fset, getPackages, data, bundle, path, shallow, reportf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return pkgs[0], nil return pkgs[0], nil
} }
// InsertType is the type of a function that creates a types.TypeName // ReportFunc is the type of a function used to report formatted bugs.
// object for a named type and inserts it into the scope of the type ReportFunc = func(string, ...interface{})
// specified Package.
type InsertType = func(pkg *types.Package, name string)
// Current bundled export format version. Increase with each format change. // Current bundled export format version. Increase with each format change.
// 0: initial implementation // 0: initial implementation
@ -313,8 +327,9 @@ type iexporter struct {
out *bytes.Buffer out *bytes.Buffer
version int version int
shallow bool // don't put types from other packages in the index shallow bool // don't put types from other packages in the index
localpkg *types.Package // (nil in bundle mode) objEncoder *objectpath.Encoder // encodes objects from other packages in shallow mode; lazily allocated
localpkg *types.Package // (nil in bundle mode)
// allPkgs tracks all packages that have been referenced by // allPkgs tracks all packages that have been referenced by
// the export data, so we can ensure to include them in the // the export data, so we can ensure to include them in the
@ -354,6 +369,17 @@ func (p *iexporter) trace(format string, args ...interface{}) {
fmt.Printf(strings.Repeat("..", p.indent)+format+"\n", args...) fmt.Printf(strings.Repeat("..", p.indent)+format+"\n", args...)
} }
// objectpathEncoder returns the lazily allocated objectpath.Encoder to use
// when encoding objects in other packages during shallow export.
//
// Using a shared Encoder amortizes some of cost of objectpath search.
func (p *iexporter) objectpathEncoder() *objectpath.Encoder {
if p.objEncoder == nil {
p.objEncoder = new(objectpath.Encoder)
}
return p.objEncoder
}
// stringOff returns the offset of s within the string section. // stringOff returns the offset of s within the string section.
// If not already present, it's added to the end. // If not already present, it's added to the end.
func (p *iexporter) stringOff(s string) uint64 { func (p *iexporter) stringOff(s string) uint64 {
@ -413,7 +439,6 @@ type exportWriter struct {
p *iexporter p *iexporter
data intWriter data intWriter
currPkg *types.Package
prevFile string prevFile string
prevLine int64 prevLine int64
prevColumn int64 prevColumn int64
@ -436,7 +461,6 @@ func (p *iexporter) doDecl(obj types.Object) {
}() }()
} }
w := p.newWriter() w := p.newWriter()
w.setPkg(obj.Pkg(), false)
switch obj := obj.(type) { switch obj := obj.(type) {
case *types.Var: case *types.Var:
@ -673,6 +697,9 @@ func (w *exportWriter) qualifiedType(obj *types.TypeName) {
w.pkg(obj.Pkg()) w.pkg(obj.Pkg())
} }
// TODO(rfindley): what does 'pkg' even mean here? It would be better to pass
// it in explicitly into signatures and structs that may use it for
// constructing fields.
func (w *exportWriter) typ(t types.Type, pkg *types.Package) { func (w *exportWriter) typ(t types.Type, pkg *types.Package) {
w.data.uint64(w.p.typOff(t, pkg)) w.data.uint64(w.p.typOff(t, pkg))
} }
@ -764,30 +791,53 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
case *types.Signature: case *types.Signature:
w.startType(signatureType) w.startType(signatureType)
w.setPkg(pkg, true) w.pkg(pkg)
w.signature(t) w.signature(t)
case *types.Struct: case *types.Struct:
w.startType(structType) w.startType(structType)
n := t.NumFields() n := t.NumFields()
// Even for struct{} we must emit some qualifying package, because that's
// what the compiler does, and thus that's what the importer expects.
fieldPkg := pkg
if n > 0 { if n > 0 {
w.setPkg(t.Field(0).Pkg(), true) // qualifying package for field objects fieldPkg = t.Field(0).Pkg()
} else {
w.setPkg(pkg, true)
} }
if fieldPkg == nil {
// TODO(rfindley): improve this very hacky logic.
//
// The importer expects a package to be set for all struct types, even
// those with no fields. A better encoding might be to set NumFields
// before pkg. setPkg panics with a nil package, which may be possible
// to reach with invalid packages (and perhaps valid packages, too?), so
// (arbitrarily) set the localpkg if available.
//
// Alternatively, we may be able to simply guarantee that pkg != nil, by
// reconsidering the encoding of constant values.
if w.p.shallow {
fieldPkg = w.p.localpkg
} else {
panic(internalErrorf("no package to set for empty struct"))
}
}
w.pkg(fieldPkg)
w.uint64(uint64(n)) w.uint64(uint64(n))
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
f := t.Field(i) f := t.Field(i)
if w.p.shallow {
w.objectPath(f)
}
w.pos(f.Pos()) w.pos(f.Pos())
w.string(f.Name()) // unexported fields implicitly qualified by prior setPkg w.string(f.Name()) // unexported fields implicitly qualified by prior setPkg
w.typ(f.Type(), pkg) w.typ(f.Type(), fieldPkg)
w.bool(f.Anonymous()) w.bool(f.Anonymous())
w.string(t.Tag(i)) // note (or tag) w.string(t.Tag(i)) // note (or tag)
} }
case *types.Interface: case *types.Interface:
w.startType(interfaceType) w.startType(interfaceType)
w.setPkg(pkg, true) w.pkg(pkg)
n := t.NumEmbeddeds() n := t.NumEmbeddeds()
w.uint64(uint64(n)) w.uint64(uint64(n))
@ -802,10 +852,16 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
w.typ(ft, tPkg) w.typ(ft, tPkg)
} }
// See comment for struct fields. In shallow mode we change the encoding
// for interface methods that are promoted from other packages.
n = t.NumExplicitMethods() n = t.NumExplicitMethods()
w.uint64(uint64(n)) w.uint64(uint64(n))
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
m := t.ExplicitMethod(i) m := t.ExplicitMethod(i)
if w.p.shallow {
w.objectPath(m)
}
w.pos(m.Pos()) w.pos(m.Pos())
w.string(m.Name()) w.string(m.Name())
sig, _ := m.Type().(*types.Signature) sig, _ := m.Type().(*types.Signature)
@ -827,12 +883,61 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
} }
} }
func (w *exportWriter) setPkg(pkg *types.Package, write bool) { // objectPath writes the package and objectPath to use to look up obj in a
if write { // different package, when encoding in "shallow" mode.
w.pkg(pkg) //
// When doing a shallow import, the importer creates only the local package,
// and requests package symbols for dependencies from the client.
// However, certain types defined in the local package may hold objects defined
// (perhaps deeply) within another package.
//
// For example, consider the following:
//
// package a
// func F() chan * map[string] struct { X int }
//
// package b
// import "a"
// var B = a.F()
//
// In this example, the type of b.B holds fields defined in package a.
// In order to have the correct canonical objects for the field defined in the
// type of B, they are encoded as objectPaths and later looked up in the
// importer. The same problem applies to interface methods.
func (w *exportWriter) objectPath(obj types.Object) {
if obj.Pkg() == nil || obj.Pkg() == w.p.localpkg {
// obj.Pkg() may be nil for the builtin error.Error.
// In this case, or if obj is declared in the local package, no need to
// encode.
w.string("")
return
} }
objectPath, err := w.p.objectpathEncoder().For(obj)
w.currPkg = pkg if err != nil {
// Fall back to the empty string, which will cause the importer to create a
// new object, which matches earlier behavior. Creating a new object is
// sufficient for many purposes (such as type checking), but causes certain
// references algorithms to fail (golang/go#60819). However, we didn't
// notice this problem during months of gopls@v0.12.0 testing.
//
// TODO(golang/go#61674): this workaround is insufficient, as in the case
// where the field forwarded from an instantiated type that may not appear
// in the export data of the original package:
//
// // package a
// type A[P any] struct{ F P }
//
// // package b
// type B a.A[int]
//
// We need to update references algorithms not to depend on this
// de-duplication, at which point we may want to simply remove the
// workaround here.
w.string("")
return
}
w.string(string(objectPath))
w.pkg(obj.Pkg())
} }
func (w *exportWriter) signature(sig *types.Signature) { func (w *exportWriter) signature(sig *types.Signature) {
@ -1205,6 +1310,13 @@ type internalError string
func (e internalError) Error() string { return "gcimporter: " + string(e) } func (e internalError) Error() string { return "gcimporter: " + string(e) }
// TODO(adonovan): make this call panic, so that it's symmetric with errorf.
// Otherwise it's easy to forget to do anything with the error.
//
// TODO(adonovan): also, consider switching the names "errorf" and
// "internalErrorf" as the former is used for bugs, whose cause is
// internal inconsistency, whereas the latter is used for ordinary
// situations like bad input, whose cause is external.
func internalErrorf(format string, args ...interface{}) error { func internalErrorf(format string, args ...interface{}) error {
return internalError(fmt.Sprintf(format, args...)) return internalError(fmt.Sprintf(format, args...))
} }

View File

@ -21,6 +21,7 @@ import (
"sort" "sort"
"strings" "strings"
"golang.org/x/tools/go/types/objectpath"
"golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/typeparams"
) )
@ -85,7 +86,7 @@ const (
// If the export data version is not recognized or the format is otherwise // If the export data version is not recognized or the format is otherwise
// compromised, an error is returned. // compromised, an error is returned.
func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) { func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) {
pkgs, err := iimportCommon(fset, GetPackageFromMap(imports), data, false, path, nil) pkgs, err := iimportCommon(fset, GetPackagesFromMap(imports), data, false, path, false, nil)
if err != nil { if err != nil {
return 0, nil, err return 0, nil, err
} }
@ -94,33 +95,49 @@ func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []
// IImportBundle imports a set of packages from the serialized package bundle. // IImportBundle imports a set of packages from the serialized package bundle.
func IImportBundle(fset *token.FileSet, imports map[string]*types.Package, data []byte) ([]*types.Package, error) { func IImportBundle(fset *token.FileSet, imports map[string]*types.Package, data []byte) ([]*types.Package, error) {
return iimportCommon(fset, GetPackageFromMap(imports), data, true, "", nil) return iimportCommon(fset, GetPackagesFromMap(imports), data, true, "", false, nil)
} }
// A GetPackageFunc is a function that gets the package with the given path // A GetPackagesFunc function obtains the non-nil symbols for a set of
// from the importer state, creating it (with the specified name) if necessary. // packages, creating and recursively importing them as needed. An
// It is an abstraction of the map historically used to memoize package creation. // implementation should store each package symbol is in the Pkg
// field of the items array.
// //
// Two calls with the same path must return the same package. // Any error causes importing to fail. This can be used to quickly read
// // the import manifest of an export data file without fully decoding it.
// If the given getPackage func returns nil, the import will fail. type GetPackagesFunc = func(items []GetPackagesItem) error
type GetPackageFunc = func(path, name string) *types.Package
// GetPackageFromMap returns a GetPackageFunc that retrieves packages from the // A GetPackagesItem is a request from the importer for the package
// given map of package path -> package. // symbol of the specified name and path.
type GetPackagesItem struct {
Name, Path string
Pkg *types.Package // to be filled in by GetPackagesFunc call
// private importer state
pathOffset uint64
nameIndex map[string]uint64
}
// GetPackagesFromMap returns a GetPackagesFunc that retrieves
// packages from the given map of package path to package.
// //
// The resulting func may mutate m: if a requested package is not found, a new // The returned function may mutate m: each requested package that is not
// package will be inserted into m. // found is created with types.NewPackage and inserted into m.
func GetPackageFromMap(m map[string]*types.Package) GetPackageFunc { func GetPackagesFromMap(m map[string]*types.Package) GetPackagesFunc {
return func(path, name string) *types.Package { return func(items []GetPackagesItem) error {
if _, ok := m[path]; !ok { for i, item := range items {
m[path] = types.NewPackage(path, name) pkg, ok := m[item.Path]
if !ok {
pkg = types.NewPackage(item.Path, item.Name)
m[item.Path] = pkg
}
items[i].Pkg = pkg
} }
return m[path] return nil
} }
} }
func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte, bundle bool, path string, insert InsertType) (pkgs []*types.Package, err error) { func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte, bundle bool, path string, shallow bool, reportf ReportFunc) (pkgs []*types.Package, err error) {
const currentVersion = iexportVersionCurrent const currentVersion = iexportVersionCurrent
version := int64(-1) version := int64(-1)
if !debug { if !debug {
@ -159,7 +176,7 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte,
sLen := int64(r.uint64()) sLen := int64(r.uint64())
var fLen int64 var fLen int64
var fileOffset []uint64 var fileOffset []uint64
if insert != nil { if shallow {
// Shallow mode uses a different position encoding. // Shallow mode uses a different position encoding.
fLen = int64(r.uint64()) fLen = int64(r.uint64())
fileOffset = make([]uint64, r.uint64()) fileOffset = make([]uint64, r.uint64())
@ -178,7 +195,8 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte,
p := iimporter{ p := iimporter{
version: int(version), version: int(version),
ipath: path, ipath: path,
insert: insert, shallow: shallow,
reportf: reportf,
stringData: stringData, stringData: stringData,
stringCache: make(map[uint64]string), stringCache: make(map[uint64]string),
@ -205,8 +223,9 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte,
p.typCache[uint64(i)] = pt p.typCache[uint64(i)] = pt
} }
pkgList := make([]*types.Package, r.uint64()) // Gather the relevant packages from the manifest.
for i := range pkgList { items := make([]GetPackagesItem, r.uint64())
for i := range items {
pkgPathOff := r.uint64() pkgPathOff := r.uint64()
pkgPath := p.stringAt(pkgPathOff) pkgPath := p.stringAt(pkgPathOff)
pkgName := p.stringAt(r.uint64()) pkgName := p.stringAt(r.uint64())
@ -215,29 +234,42 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte,
if pkgPath == "" { if pkgPath == "" {
pkgPath = path pkgPath = path
} }
pkg := getPackage(pkgPath, pkgName) items[i].Name = pkgName
if pkg == nil { items[i].Path = pkgPath
errorf("internal error: getPackage returned nil package for %s", pkgPath) items[i].pathOffset = pkgPathOff
} else if pkg.Name() != pkgName {
errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path)
}
if i == 0 && !bundle {
p.localpkg = pkg
}
p.pkgCache[pkgPathOff] = pkg
// Read index for package. // Read index for package.
nameIndex := make(map[string]uint64) nameIndex := make(map[string]uint64)
nSyms := r.uint64() nSyms := r.uint64()
// In shallow mode we don't expect an index for other packages. // In shallow mode, only the current package (i=0) has an index.
assert(nSyms == 0 || p.localpkg == pkg || p.insert == nil) assert(!(shallow && i > 0 && nSyms != 0))
for ; nSyms > 0; nSyms-- { for ; nSyms > 0; nSyms-- {
name := p.stringAt(r.uint64()) name := p.stringAt(r.uint64())
nameIndex[name] = r.uint64() nameIndex[name] = r.uint64()
} }
p.pkgIndex[pkg] = nameIndex items[i].nameIndex = nameIndex
}
// Request packages all at once from the client,
// enabling a parallel implementation.
if err := getPackages(items); err != nil {
return nil, err // don't wrap this error
}
// Check the results and complete the index.
pkgList := make([]*types.Package, len(items))
for i, item := range items {
pkg := item.Pkg
if pkg == nil {
errorf("internal error: getPackages returned nil package for %q", item.Path)
} else if pkg.Path() != item.Path {
errorf("internal error: getPackages returned wrong path %q, want %q", pkg.Path(), item.Path)
} else if pkg.Name() != item.Name {
errorf("internal error: getPackages returned wrong name %s for package %q, want %s", pkg.Name(), item.Path, item.Name)
}
p.pkgCache[item.pathOffset] = pkg
p.pkgIndex[pkg] = item.nameIndex
pkgList[i] = pkg pkgList[i] = pkg
} }
@ -296,6 +328,13 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte,
typ.Complete() typ.Complete()
} }
// Workaround for golang/go#61561. See the doc for instanceList for details.
for _, typ := range p.instanceList {
if iface, _ := typ.Underlying().(*types.Interface); iface != nil {
iface.Complete()
}
}
return pkgs, nil return pkgs, nil
} }
@ -308,8 +347,8 @@ type iimporter struct {
version int version int
ipath string ipath string
localpkg *types.Package shallow bool
insert func(pkg *types.Package, name string) // "shallow" mode only reportf ReportFunc // if non-nil, used to report bugs
stringData []byte stringData []byte
stringCache map[uint64]string stringCache map[uint64]string
@ -326,6 +365,12 @@ type iimporter struct {
fake fakeFileSet fake fakeFileSet
interfaceList []*types.Interface interfaceList []*types.Interface
// Workaround for the go/types bug golang/go#61561: instances produced during
// instantiation may contain incomplete interfaces. Here we only complete the
// underlying type of the instance, which is the most common case but doesn't
// handle parameterized interface literals defined deeper in the type.
instanceList []types.Type // instances for later completion (see golang/go#61561)
// Arguments for calls to SetConstraint that are deferred due to recursive types // Arguments for calls to SetConstraint that are deferred due to recursive types
later []setConstraintArgs later []setConstraintArgs
@ -357,13 +402,9 @@ func (p *iimporter) doDecl(pkg *types.Package, name string) {
off, ok := p.pkgIndex[pkg][name] off, ok := p.pkgIndex[pkg][name]
if !ok { if !ok {
// In "shallow" mode, call back to the application to // In deep mode, the index should be complete. In shallow
// find the object and insert it into the package scope. // mode, we should have already recursively loaded necessary
if p.insert != nil { // dependencies so the above Lookup succeeds.
assert(pkg != p.localpkg)
p.insert(pkg, name) // "can't fail"
return
}
errorf("%v.%v not in index", pkg, name) errorf("%v.%v not in index", pkg, name)
} }
@ -730,7 +771,8 @@ func (r *importReader) qualifiedIdent() (*types.Package, string) {
} }
func (r *importReader) pos() token.Pos { func (r *importReader) pos() token.Pos {
if r.p.insert != nil { // shallow mode if r.p.shallow {
// precise offsets are encoded only in shallow mode
return r.posv2() return r.posv2()
} }
if r.p.version >= iexportVersionPosCol { if r.p.version >= iexportVersionPosCol {
@ -831,13 +873,28 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
fields := make([]*types.Var, r.uint64()) fields := make([]*types.Var, r.uint64())
tags := make([]string, len(fields)) tags := make([]string, len(fields))
for i := range fields { for i := range fields {
var field *types.Var
if r.p.shallow {
field, _ = r.objectPathObject().(*types.Var)
}
fpos := r.pos() fpos := r.pos()
fname := r.ident() fname := r.ident()
ftyp := r.typ() ftyp := r.typ()
emb := r.bool() emb := r.bool()
tag := r.string() tag := r.string()
fields[i] = types.NewField(fpos, r.currPkg, fname, ftyp, emb) // Either this is not a shallow import, the field is local, or the
// encoded objectPath failed to produce an object (a bug).
//
// Even in this last, buggy case, fall back on creating a new field. As
// discussed in iexport.go, this is not correct, but mostly works and is
// preferable to failing (for now at least).
if field == nil {
field = types.NewField(fpos, r.currPkg, fname, ftyp, emb)
}
fields[i] = field
tags[i] = tag tags[i] = tag
} }
return types.NewStruct(fields, tags) return types.NewStruct(fields, tags)
@ -853,6 +910,11 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
methods := make([]*types.Func, r.uint64()) methods := make([]*types.Func, r.uint64())
for i := range methods { for i := range methods {
var method *types.Func
if r.p.shallow {
method, _ = r.objectPathObject().(*types.Func)
}
mpos := r.pos() mpos := r.pos()
mname := r.ident() mname := r.ident()
@ -862,9 +924,12 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
if base != nil { if base != nil {
recv = types.NewVar(token.NoPos, r.currPkg, "", base) recv = types.NewVar(token.NoPos, r.currPkg, "", base)
} }
msig := r.signature(recv, nil, nil) msig := r.signature(recv, nil, nil)
methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig)
if method == nil {
method = types.NewFunc(mpos, r.currPkg, mname, msig)
}
methods[i] = method
} }
typ := newInterface(methods, embeddeds) typ := newInterface(methods, embeddeds)
@ -902,6 +967,9 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
// we must always use the methods of the base (orig) type. // we must always use the methods of the base (orig) type.
// TODO provide a non-nil *Environment // TODO provide a non-nil *Environment
t, _ := typeparams.Instantiate(nil, baseType, targs, false) t, _ := typeparams.Instantiate(nil, baseType, targs, false)
// Workaround for golang/go#61561. See the doc for instanceList for details.
r.p.instanceList = append(r.p.instanceList, t)
return t return t
case unionType: case unionType:
@ -920,6 +988,26 @@ func (r *importReader) kind() itag {
return itag(r.uint64()) return itag(r.uint64())
} }
// objectPathObject is the inverse of exportWriter.objectPath.
//
// In shallow mode, certain fields and methods may need to be looked up in an
// imported package. See the doc for exportWriter.objectPath for a full
// explanation.
func (r *importReader) objectPathObject() types.Object {
objPath := objectpath.Path(r.string())
if objPath == "" {
return nil
}
pkg := r.pkg()
obj, err := objectpath.Object(pkg, objPath)
if err != nil {
if r.p.reportf != nil {
r.p.reportf("failed to find object for objectPath %q: %v", objPath, err)
}
}
return obj
}
func (r *importReader) signature(recv *types.Var, rparams []*typeparams.TypeParam, tparams []*typeparams.TypeParam) *types.Signature { func (r *importReader) signature(recv *types.Var, rparams []*typeparams.TypeParam, tparams []*typeparams.TypeParam) *types.Signature {
params := r.paramList() params := r.paramList()
results := r.paramList() results := r.paramList()

View File

@ -13,6 +13,7 @@ import (
"io" "io"
"log" "log"
"os" "os"
"os/exec"
"reflect" "reflect"
"regexp" "regexp"
"runtime" "runtime"
@ -21,8 +22,6 @@ import (
"sync" "sync"
"time" "time"
exec "golang.org/x/sys/execabs"
"golang.org/x/tools/internal/event" "golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/event/keys" "golang.org/x/tools/internal/event/keys"
"golang.org/x/tools/internal/event/label" "golang.org/x/tools/internal/event/label"
@ -85,6 +84,7 @@ func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stde
// RunRaw runs the invocation, serializing requests only if they fight over // RunRaw runs the invocation, serializing requests only if they fight over
// go.mod changes. // go.mod changes.
// Postcondition: both error results have same nilness.
func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
ctx, done := event.Start(ctx, "gocommand.Runner.RunRaw", invLabels(inv)...) ctx, done := event.Start(ctx, "gocommand.Runner.RunRaw", invLabels(inv)...)
defer done() defer done()
@ -95,23 +95,24 @@ func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer
stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv) stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv)
// If we encounter a load concurrency error, we need to retry serially. // If we encounter a load concurrency error, we need to retry serially.
if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) { if friendlyErr != nil && modConcurrencyError.MatchString(friendlyErr.Error()) {
return stdout, stderr, friendlyErr, err event.Error(ctx, "Load concurrency error, will retry serially", err)
}
event.Error(ctx, "Load concurrency error, will retry serially", err) // Run serially by calling runPiped.
stdout.Reset()
stderr.Reset()
friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr)
}
// Run serially by calling runPiped.
stdout.Reset()
stderr.Reset()
friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr)
return stdout, stderr, friendlyErr, err return stdout, stderr, friendlyErr, err
} }
// Postcondition: both error results have same nilness.
func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
// Wait for 1 worker to become available. // Wait for 1 worker to become available.
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil, nil, nil, ctx.Err() return nil, nil, ctx.Err(), ctx.Err()
case runner.inFlight <- struct{}{}: case runner.inFlight <- struct{}{}:
defer func() { <-runner.inFlight }() defer func() { <-runner.inFlight }()
} }
@ -121,6 +122,7 @@ func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes
return stdout, stderr, friendlyErr, err return stdout, stderr, friendlyErr, err
} }
// Postcondition: both error results have same nilness.
func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) { func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) {
// Make sure the runner is always initialized. // Make sure the runner is always initialized.
runner.initialize() runner.initialize()
@ -129,7 +131,7 @@ func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stde
// runPiped commands. // runPiped commands.
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil, ctx.Err() return ctx.Err(), ctx.Err()
case runner.serialized <- struct{}{}: case runner.serialized <- struct{}{}:
defer func() { <-runner.serialized }() defer func() { <-runner.serialized }()
} }
@ -139,7 +141,7 @@ func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stde
for i := 0; i < maxInFlight; i++ { for i := 0; i < maxInFlight; i++ {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil, ctx.Err() return ctx.Err(), ctx.Err()
case runner.inFlight <- struct{}{}: case runner.inFlight <- struct{}{}:
// Make sure we always "return" any workers we took. // Make sure we always "return" any workers we took.
defer func() { <-runner.inFlight }() defer func() { <-runner.inFlight }()
@ -172,6 +174,7 @@ type Invocation struct {
Logf func(format string, args ...interface{}) Logf func(format string, args ...interface{})
} }
// Postcondition: both error results have same nilness.
func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) { func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) {
rawError = i.run(ctx, stdout, stderr) rawError = i.run(ctx, stdout, stderr)
if rawError != nil { if rawError != nil {
@ -319,7 +322,7 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) {
// Per https://pkg.go.dev/os#File.Close, the call to stdoutR.Close // Per https://pkg.go.dev/os#File.Close, the call to stdoutR.Close
// should cause the Read call in io.Copy to unblock and return // should cause the Read call in io.Copy to unblock and return
// immediately, but we still need to receive from stdoutErr to confirm // immediately, but we still need to receive from stdoutErr to confirm
// that that has happened. // that it has happened.
<-stdoutErr <-stdoutErr
err2 = ctx.Err() err2 = ctx.Err()
} }
@ -333,7 +336,7 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) {
// one goroutine at a time will call Write.” // one goroutine at a time will call Write.”
// //
// Since we're starting a goroutine that writes to cmd.Stdout, we must // Since we're starting a goroutine that writes to cmd.Stdout, we must
// also update cmd.Stderr so that that still holds. // also update cmd.Stderr so that it still holds.
func() { func() {
defer func() { recover() }() defer func() { recover() }()
if cmd.Stderr == prevStdout { if cmd.Stderr == prevStdout {

View File

@ -5,10 +5,6 @@
// Package packagesinternal exposes internal-only fields from go/packages. // Package packagesinternal exposes internal-only fields from go/packages.
package packagesinternal package packagesinternal
import (
"golang.org/x/tools/internal/gocommand"
)
var GetForTest = func(p interface{}) string { return "" } var GetForTest = func(p interface{}) string { return "" }
var GetDepsErrors = func(p interface{}) []*PackageError { return nil } var GetDepsErrors = func(p interface{}) []*PackageError { return nil }
@ -18,10 +14,6 @@ type PackageError struct {
Err string // the error itself Err string // the error itself
} }
var GetGoCmdRunner = func(config interface{}) *gocommand.Runner { return nil }
var SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {}
var TypecheckCgo int var TypecheckCgo int
var DepsErrors int // must be set as a LoadMode to call GetDepsErrors var DepsErrors int // must be set as a LoadMode to call GetDepsErrors
var ForTest int // must be set as a LoadMode to call GetForTest var ForTest int // must be set as a LoadMode to call GetForTest

View File

@ -23,6 +23,7 @@
package typeparams package typeparams
import ( import (
"fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types" "go/types"
@ -125,6 +126,11 @@ func OriginMethod(fn *types.Func) *types.Func {
} }
} }
// In golang/go#61196, we observe another crash, this time inexplicable.
if gfn == nil {
panic(fmt.Sprintf("missing origin method for %s.%s; named == origin: %t, named.NumMethods(): %d, origin.NumMethods(): %d", named, fn, named == orig, named.NumMethods(), orig.NumMethods()))
}
return gfn.(*types.Func) return gfn.(*types.Func)
} }

View File

@ -81,13 +81,13 @@ func CoreType(T types.Type) types.Type {
// restrictions may be arbitrarily complex. For example, consider the // restrictions may be arbitrarily complex. For example, consider the
// following: // following:
// //
// type A interface{ ~string|~[]byte } // type A interface{ ~string|~[]byte }
// //
// type B interface{ int|string } // type B interface{ int|string }
// //
// type C interface { ~string|~int } // type C interface { ~string|~int }
// //
// type T[P interface{ A|B; C }] int // type T[P interface{ A|B; C }] int
// //
// In this example, the structural type restriction of P is ~string|int: A|B // In this example, the structural type restriction of P is ~string|int: A|B
// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int, // expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int,

View File

@ -30,7 +30,7 @@ func (xl termlist) String() string {
var buf bytes.Buffer var buf bytes.Buffer
for i, x := range xl { for i, x := range xl {
if i > 0 { if i > 0 {
buf.WriteString(" ") buf.WriteString(" | ")
} }
buf.WriteString(x.String()) buf.WriteString(x.String())
} }

View File

@ -129,7 +129,7 @@ func NamedTypeArgs(*types.Named) *TypeList {
} }
// NamedTypeOrigin is the identity method at this Go version. // NamedTypeOrigin is the identity method at this Go version.
func NamedTypeOrigin(named *types.Named) types.Type { func NamedTypeOrigin(named *types.Named) *types.Named {
return named return named
} }

View File

@ -103,7 +103,7 @@ func NamedTypeArgs(named *types.Named) *TypeList {
} }
// NamedTypeOrigin returns named.Orig(). // NamedTypeOrigin returns named.Orig().
func NamedTypeOrigin(named *types.Named) types.Type { func NamedTypeOrigin(named *types.Named) *types.Named {
return named.Origin() return named.Origin()
} }

View File

@ -10,11 +10,10 @@ import "go/types"
// A term describes elementary type sets: // A term describes elementary type sets:
// //
// ∅: (*term)(nil) == ∅ // set of no types (empty set) // ∅: (*term)(nil) == ∅ // set of no types (empty set)
// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse) // 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
// T: &term{false, T} == {T} // set of type T // T: &term{false, T} == {T} // set of type T
// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t // ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
//
type term struct { type term struct {
tilde bool // valid if typ != nil tilde bool // valid if typ != nil
typ types.Type typ types.Type

172
vendor/golang.org/x/tools/internal/versions/gover.go generated vendored Normal file
View File

@ -0,0 +1,172 @@
// Copyright 2023 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.
// This is a fork of internal/gover for use by x/tools until
// go1.21 and earlier are no longer supported by x/tools.
package versions
import "strings"
// A gover is a parsed Go gover: major[.Minor[.Patch]][kind[pre]]
// The numbers are the original decimal strings to avoid integer overflows
// and since there is very little actual math. (Probably overflow doesn't matter in practice,
// but at the time this code was written, there was an existing test that used
// go1.99999999999, which does not fit in an int on 32-bit platforms.
// The "big decimal" representation avoids the problem entirely.)
type gover struct {
major string // decimal
minor string // decimal or ""
patch string // decimal or ""
kind string // "", "alpha", "beta", "rc"
pre string // decimal or ""
}
// compare returns -1, 0, or +1 depending on whether
// x < y, x == y, or x > y, interpreted as toolchain versions.
// The versions x and y must not begin with a "go" prefix: just "1.21" not "go1.21".
// Malformed versions compare less than well-formed versions and equal to each other.
// The language version "1.21" compares less than the release candidate and eventual releases "1.21rc1" and "1.21.0".
func compare(x, y string) int {
vx := parse(x)
vy := parse(y)
if c := cmpInt(vx.major, vy.major); c != 0 {
return c
}
if c := cmpInt(vx.minor, vy.minor); c != 0 {
return c
}
if c := cmpInt(vx.patch, vy.patch); c != 0 {
return c
}
if c := strings.Compare(vx.kind, vy.kind); c != 0 { // "" < alpha < beta < rc
return c
}
if c := cmpInt(vx.pre, vy.pre); c != 0 {
return c
}
return 0
}
// lang returns the Go language version. For example, lang("1.2.3") == "1.2".
func lang(x string) string {
v := parse(x)
if v.minor == "" || v.major == "1" && v.minor == "0" {
return v.major
}
return v.major + "." + v.minor
}
// isValid reports whether the version x is valid.
func isValid(x string) bool {
return parse(x) != gover{}
}
// parse parses the Go version string x into a version.
// It returns the zero version if x is malformed.
func parse(x string) gover {
var v gover
// Parse major version.
var ok bool
v.major, x, ok = cutInt(x)
if !ok {
return gover{}
}
if x == "" {
// Interpret "1" as "1.0.0".
v.minor = "0"
v.patch = "0"
return v
}
// Parse . before minor version.
if x[0] != '.' {
return gover{}
}
// Parse minor version.
v.minor, x, ok = cutInt(x[1:])
if !ok {
return gover{}
}
if x == "" {
// Patch missing is same as "0" for older versions.
// Starting in Go 1.21, patch missing is different from explicit .0.
if cmpInt(v.minor, "21") < 0 {
v.patch = "0"
}
return v
}
// Parse patch if present.
if x[0] == '.' {
v.patch, x, ok = cutInt(x[1:])
if !ok || x != "" {
// Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != "").
// Allowing them would be a bit confusing because we already have:
// 1.21 < 1.21rc1
// But a prerelease of a patch would have the opposite effect:
// 1.21.3rc1 < 1.21.3
// We've never needed them before, so let's not start now.
return gover{}
}
return v
}
// Parse prerelease.
i := 0
for i < len(x) && (x[i] < '0' || '9' < x[i]) {
if x[i] < 'a' || 'z' < x[i] {
return gover{}
}
i++
}
if i == 0 {
return gover{}
}
v.kind, x = x[:i], x[i:]
if x == "" {
return v
}
v.pre, x, ok = cutInt(x)
if !ok || x != "" {
return gover{}
}
return v
}
// cutInt scans the leading decimal number at the start of x to an integer
// and returns that value and the rest of the string.
func cutInt(x string) (n, rest string, ok bool) {
i := 0
for i < len(x) && '0' <= x[i] && x[i] <= '9' {
i++
}
if i == 0 || x[0] == '0' && i != 1 { // no digits or unnecessary leading zero
return "", "", false
}
return x[:i], x[i:], true
}
// cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers.
// (Copied from golang.org/x/mod/semver's compareInt.)
func cmpInt(x, y string) int {
if x == y {
return 0
}
if len(x) < len(y) {
return -1
}
if len(x) > len(y) {
return +1
}
if x < y {
return -1
} else {
return +1
}
}

19
vendor/golang.org/x/tools/internal/versions/types.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
// Copyright 2023 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 versions
import (
"go/types"
)
// GoVersion returns the Go version of the type package.
// It returns zero if no version can be determined.
func GoVersion(pkg *types.Package) string {
// TODO(taking): x/tools can call GoVersion() [from 1.21] after 1.25.
if pkg, ok := any(pkg).(interface{ GoVersion() string }); ok {
return pkg.GoVersion()
}
return ""
}

View File

@ -0,0 +1,20 @@
// Copyright 2023 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
// +build !go1.22
package versions
import (
"go/ast"
"go/types"
)
// FileVersions always reports the a file's Go version as the
// zero version at this Go version.
func FileVersions(info *types.Info, file *ast.File) string { return "" }
// InitFileVersions is a noop at this Go version.
func InitFileVersions(*types.Info) {}

View File

@ -0,0 +1,24 @@
// Copyright 2023 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
// +build go1.22
package versions
import (
"go/ast"
"go/types"
)
// FileVersions maps a file to the file's semantic Go version.
// The reported version is the zero version if a version cannot be determined.
func FileVersions(info *types.Info, file *ast.File) string {
return info.FileVersions[file]
}
// InitFileVersions initializes info to record Go versions for Go files.
func InitFileVersions(info *types.Info) {
info.FileVersions = make(map[*ast.File]string)
}

View File

@ -0,0 +1,49 @@
// Copyright 2023 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
// +build !go1.22
package versions
// Lang returns the Go language version for version x.
// If x is not a valid version, Lang returns the empty string.
// For example:
//
// Lang("go1.21rc2") = "go1.21"
// Lang("go1.21.2") = "go1.21"
// Lang("go1.21") = "go1.21"
// Lang("go1") = "go1"
// Lang("bad") = ""
// Lang("1.21") = ""
func Lang(x string) string {
v := lang(stripGo(x))
if v == "" {
return ""
}
return x[:2+len(v)] // "go"+v without allocation
}
// Compare returns -1, 0, or +1 depending on whether
// x < y, x == y, or x > y, interpreted as Go versions.
// The versions x and y must begin with a "go" prefix: "go1.21" not "1.21".
// Invalid versions, including the empty string, compare less than
// valid versions and equal to each other.
// The language version "go1.21" compares less than the
// release candidate and eventual releases "go1.21rc1" and "go1.21.0".
// Custom toolchain suffixes are ignored during comparison:
// "go1.21.0" and "go1.21.0-bigcorp" are equal.
func Compare(x, y string) int { return compare(stripGo(x), stripGo(y)) }
// IsValid reports whether the version x is valid.
func IsValid(x string) bool { return isValid(stripGo(x)) }
// stripGo converts from a "go1.21" version to a "1.21" version.
// If v does not start with "go", stripGo returns the empty string (a known invalid version).
func stripGo(v string) string {
if len(v) < 2 || v[:2] != "go" {
return ""
}
return v[2:]
}

View File

@ -0,0 +1,38 @@
// Copyright 2023 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
// +build go1.22
package versions
import (
"go/version"
)
// Lang returns the Go language version for version x.
// If x is not a valid version, Lang returns the empty string.
// For example:
//
// Lang("go1.21rc2") = "go1.21"
// Lang("go1.21.2") = "go1.21"
// Lang("go1.21") = "go1.21"
// Lang("go1") = "go1"
// Lang("bad") = ""
// Lang("1.21") = ""
func Lang(x string) string { return version.Lang(x) }
// Compare returns -1, 0, or +1 depending on whether
// x < y, x == y, or x > y, interpreted as Go versions.
// The versions x and y must begin with a "go" prefix: "go1.21" not "1.21".
// Invalid versions, including the empty string, compare less than
// valid versions and equal to each other.
// The language version "go1.21" compares less than the
// release candidate and eventual releases "go1.21rc1" and "go1.21.0".
// Custom toolchain suffixes are ignored during comparison:
// "go1.21.0" and "go1.21.0-bigcorp" are equal.
func Compare(x, y string) int { return version.Compare(x, y) }
// IsValid reports whether the version x is valid.
func IsValid(x string) bool { return version.IsValid(x) }

9
vendor/modules.txt vendored
View File

@ -309,8 +309,8 @@ go.opentelemetry.io/otel/trace
## explicit; go 1.18 ## explicit; go 1.18
golang.org/x/crypto/ed25519 golang.org/x/crypto/ed25519
golang.org/x/crypto/pbkdf2 golang.org/x/crypto/pbkdf2
# golang.org/x/mod v0.11.0 # golang.org/x/mod v0.14.0
## explicit; go 1.17 ## explicit; go 1.18
golang.org/x/mod/semver golang.org/x/mod/semver
# golang.org/x/net v0.19.0 # golang.org/x/net v0.19.0
## explicit; go 1.18 ## explicit; go 1.18
@ -325,7 +325,6 @@ golang.org/x/net/trace
golang.org/x/sync/errgroup golang.org/x/sync/errgroup
# golang.org/x/sys v0.15.0 # golang.org/x/sys v0.15.0
## explicit; go 1.18 ## explicit; go 1.18
golang.org/x/sys/execabs
golang.org/x/sys/plan9 golang.org/x/sys/plan9
golang.org/x/sys/unix golang.org/x/sys/unix
golang.org/x/sys/windows golang.org/x/sys/windows
@ -342,12 +341,13 @@ golang.org/x/text/width
# golang.org/x/time v0.3.0 # golang.org/x/time v0.3.0
## explicit ## explicit
golang.org/x/time/rate golang.org/x/time/rate
# golang.org/x/tools v0.10.0 # golang.org/x/tools v0.16.0
## explicit; go 1.18 ## explicit; go 1.18
golang.org/x/tools/cmd/stringer golang.org/x/tools/cmd/stringer
golang.org/x/tools/go/gcexportdata golang.org/x/tools/go/gcexportdata
golang.org/x/tools/go/internal/packagesdriver golang.org/x/tools/go/internal/packagesdriver
golang.org/x/tools/go/packages golang.org/x/tools/go/packages
golang.org/x/tools/go/types/objectpath
golang.org/x/tools/internal/event golang.org/x/tools/internal/event
golang.org/x/tools/internal/event/core golang.org/x/tools/internal/event/core
golang.org/x/tools/internal/event/keys golang.org/x/tools/internal/event/keys
@ -360,6 +360,7 @@ golang.org/x/tools/internal/pkgbits
golang.org/x/tools/internal/tokeninternal golang.org/x/tools/internal/tokeninternal
golang.org/x/tools/internal/typeparams golang.org/x/tools/internal/typeparams
golang.org/x/tools/internal/typesinternal golang.org/x/tools/internal/typesinternal
golang.org/x/tools/internal/versions
# google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 # google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98
## explicit; go 1.19 ## explicit; go 1.19
google.golang.org/genproto/googleapis/rpc/status google.golang.org/genproto/googleapis/rpc/status