mirror of https://github.com/docker/cli.git
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:
parent
efae960e5a
commit
9db56ea2f6
|
@ -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
|
||||||
|
|
|
@ -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=
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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])
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.)
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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, "/")
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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", "")
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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...))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 ""
|
||||||
|
}
|
|
@ -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) {}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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:]
|
||||||
|
}
|
|
@ -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) }
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue