vendor: golang.org/x/tools v0.14.0, golang.org/x/mod v0.13.0, golang.org/x/sync v0.4.0

full diff:

- https://github.com/golang/tools/compare/v0.10.0...v0.14.0
- https://github.com/golang/mod/compare/v0.11.0...v0.13.0
- https://github.com/golang/sync/compare/v0.3.0...v0.4.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2024-01-20 02:22:49 +01:00
parent 7206e2d179
commit da95d9f0ca
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
25 changed files with 1212 additions and 149 deletions

6
go.mod
View File

@ -44,8 +44,8 @@ require (
go.opentelemetry.io/otel/metric v1.19.0 go.opentelemetry.io/otel/metric v1.19.0
go.opentelemetry.io/otel/sdk/metric v1.19.0 go.opentelemetry.io/otel/sdk/metric v1.19.0
go.opentelemetry.io/otel/trace v1.19.0 go.opentelemetry.io/otel/trace v1.19.0
golang.org/x/mod v0.11.0 golang.org/x/mod v0.13.0
golang.org/x/sync v0.3.0 golang.org/x/sync v0.4.0
golang.org/x/sys v0.16.0 golang.org/x/sys v0.16.0
golang.org/x/term v0.15.0 golang.org/x/term v0.15.0
google.golang.org/grpc v1.58.3 google.golang.org/grpc v1.58.3
@ -155,7 +155,7 @@ require (
golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.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.14.0 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 // indirect google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect

12
go.sum
View File

@ -527,8 +527,8 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
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.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -553,8 +553,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -592,8 +592,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
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.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -13,7 +13,7 @@ import (
"sync" "sync"
) )
// Regexp is a wrapper around regexp.Regexp, where the underlying regexp will be // Regexp is a wrapper around [regexp.Regexp], where the underlying regexp will be
// compiled the first time it is needed. // compiled the first time it is needed.
type Regexp struct { type Regexp struct {
str string str string

View File

@ -4,7 +4,7 @@
// Package module defines the module.Version type along with support code. // Package module defines the module.Version type along with support code.
// //
// The module.Version type is a simple Path, Version pair: // The [module.Version] type is a simple Path, Version pair:
// //
// type Version struct { // type Version struct {
// Path string // Path string
@ -12,7 +12,7 @@
// } // }
// //
// There are no restrictions imposed directly by use of this structure, // There are no restrictions imposed directly by use of this structure,
// but additional checking functions, most notably Check, verify that // but additional checking functions, most notably [Check], verify that
// a particular path, version pair is valid. // a particular path, version pair is valid.
// //
// # Escaped Paths // # Escaped Paths
@ -140,7 +140,7 @@ type ModuleError struct {
Err error Err error
} }
// VersionError returns a ModuleError derived from a Version and error, // VersionError returns a [ModuleError] derived from a [Version] and error,
// or err itself if it is already such an error. // or err itself if it is already such an error.
func VersionError(v Version, err error) error { func VersionError(v Version, err error) error {
var mErr *ModuleError var mErr *ModuleError
@ -169,7 +169,7 @@ func (e *ModuleError) Unwrap() error { return e.Err }
// An InvalidVersionError indicates an error specific to a version, with the // An InvalidVersionError indicates an error specific to a version, with the
// module path unknown or specified externally. // module path unknown or specified externally.
// //
// A ModuleError may wrap an InvalidVersionError, but an InvalidVersionError // A [ModuleError] may wrap an InvalidVersionError, but an InvalidVersionError
// must not wrap a ModuleError. // must not wrap a ModuleError.
type InvalidVersionError struct { type InvalidVersionError struct {
Version string Version string
@ -193,8 +193,8 @@ func (e *InvalidVersionError) Error() string {
func (e *InvalidVersionError) Unwrap() error { return e.Err } func (e *InvalidVersionError) Unwrap() error { return e.Err }
// An InvalidPathError indicates a module, import, or file path doesn't // An InvalidPathError indicates a module, import, or file path doesn't
// satisfy all naming constraints. See CheckPath, CheckImportPath, // satisfy all naming constraints. See [CheckPath], [CheckImportPath],
// and CheckFilePath for specific restrictions. // and [CheckFilePath] for specific restrictions.
type InvalidPathError struct { type InvalidPathError struct {
Kind string // "module", "import", or "file" Kind string // "module", "import", or "file"
Path string Path string
@ -294,7 +294,7 @@ func fileNameOK(r rune) bool {
} }
// CheckPath checks that a module path is valid. // CheckPath checks that a module path is valid.
// A valid module path is a valid import path, as checked by CheckImportPath, // A valid module path is a valid import path, as checked by [CheckImportPath],
// with three additional constraints. // with three additional constraints.
// First, the leading path element (up to the first slash, if any), // First, the leading path element (up to the first slash, if any),
// by convention a domain name, must contain only lower-case ASCII letters, // by convention a domain name, must contain only lower-case ASCII letters,
@ -380,7 +380,7 @@ const (
// checkPath returns an error describing why the path is not valid. // checkPath returns an error describing why the path is not valid.
// Because these checks apply to module, import, and file paths, // Because these checks apply to module, import, and file paths,
// and because other checks may be applied, the caller is expected to wrap // and because other checks may be applied, the caller is expected to wrap
// this error with InvalidPathError. // this error with [InvalidPathError].
func checkPath(path string, kind pathKind) error { func checkPath(path string, kind pathKind) error {
if !utf8.ValidString(path) { if !utf8.ValidString(path) {
return fmt.Errorf("invalid UTF-8") return fmt.Errorf("invalid UTF-8")
@ -532,7 +532,7 @@ var badWindowsNames = []string{
// they require ".vN" instead of "/vN", and for all N, not just N >= 2. // they require ".vN" instead of "/vN", and for all N, not just N >= 2.
// SplitPathVersion returns with ok = false when presented with // SplitPathVersion returns with ok = false when presented with
// a path whose last path element does not satisfy the constraints // a path whose last path element does not satisfy the constraints
// applied by CheckPath, such as "example.com/pkg/v1" or "example.com/pkg/v1.2". // applied by [CheckPath], such as "example.com/pkg/v1" or "example.com/pkg/v1.2".
func SplitPathVersion(path string) (prefix, pathMajor string, ok bool) { func SplitPathVersion(path string) (prefix, pathMajor string, ok bool) {
if strings.HasPrefix(path, "gopkg.in/") { if strings.HasPrefix(path, "gopkg.in/") {
return splitGopkgIn(path) return splitGopkgIn(path)
@ -582,7 +582,7 @@ func splitGopkgIn(path string) (prefix, pathMajor string, ok bool) {
// MatchPathMajor reports whether the semantic version v // MatchPathMajor reports whether the semantic version v
// matches the path major version pathMajor. // matches the path major version pathMajor.
// //
// MatchPathMajor returns true if and only if CheckPathMajor returns nil. // MatchPathMajor returns true if and only if [CheckPathMajor] returns nil.
func MatchPathMajor(v, pathMajor string) bool { func MatchPathMajor(v, pathMajor string) bool {
return CheckPathMajor(v, pathMajor) == nil return CheckPathMajor(v, pathMajor) == nil
} }
@ -622,7 +622,7 @@ func CheckPathMajor(v, pathMajor string) error {
// PathMajorPrefix returns the major-version tag prefix implied by pathMajor. // PathMajorPrefix returns the major-version tag prefix implied by pathMajor.
// An empty PathMajorPrefix allows either v0 or v1. // An empty PathMajorPrefix allows either v0 or v1.
// //
// Note that MatchPathMajor may accept some versions that do not actually begin // Note that [MatchPathMajor] may accept some versions that do not actually begin
// with this prefix: namely, it accepts a 'v0.0.0-' prefix for a '.v1' // with this prefix: namely, it accepts a 'v0.0.0-' prefix for a '.v1'
// pathMajor, even though that pathMajor implies 'v1' tagging. // pathMajor, even though that pathMajor implies 'v1' tagging.
func PathMajorPrefix(pathMajor string) string { func PathMajorPrefix(pathMajor string) string {
@ -643,7 +643,7 @@ func PathMajorPrefix(pathMajor string) string {
} }
// CanonicalVersion returns the canonical form of the version string v. // CanonicalVersion returns the canonical form of the version string v.
// It is the same as semver.Canonical(v) except that it preserves the special build suffix "+incompatible". // It is the same as [semver.Canonical] except that it preserves the special build suffix "+incompatible".
func CanonicalVersion(v string) string { func CanonicalVersion(v string) string {
cv := semver.Canonical(v) cv := semver.Canonical(v)
if semver.Build(v) == "+incompatible" { if semver.Build(v) == "+incompatible" {
@ -652,8 +652,8 @@ func CanonicalVersion(v string) string {
return cv return cv
} }
// Sort sorts the list by Path, breaking ties by comparing Version fields. // Sort sorts the list by Path, breaking ties by comparing [Version] fields.
// The Version fields are interpreted as semantic versions (using semver.Compare) // The Version fields are interpreted as semantic versions (using [semver.Compare])
// optionally followed by a tie-breaking suffix introduced by a slash character, // optionally followed by a tie-breaking suffix introduced by a slash character,
// like in "v0.0.1/go.mod". // like in "v0.0.1/go.mod".
func Sort(list []Version) { func Sort(list []Version) {
@ -793,7 +793,7 @@ func unescapeString(escaped string) (string, bool) {
} }
// MatchPrefixPatterns reports whether any path prefix of target matches one of // MatchPrefixPatterns reports whether any path prefix of target matches one of
// the glob patterns (as defined by path.Match) in the comma-separated globs // the glob patterns (as defined by [path.Match]) in the comma-separated globs
// list. This implements the algorithm used when matching a module path to the // list. This implements the algorithm used when matching a module path to the
// GOPRIVATE environment variable, as described by 'go help module-private'. // GOPRIVATE environment variable, as described by 'go help module-private'.
// //

View File

@ -125,7 +125,7 @@ func IsPseudoVersion(v string) bool {
} }
// IsZeroPseudoVersion returns whether v is a pseudo-version with a zero base, // IsZeroPseudoVersion returns whether v is a pseudo-version with a zero base,
// timestamp, and revision, as returned by ZeroPseudoVersion. // timestamp, and revision, as returned by [ZeroPseudoVersion].
func IsZeroPseudoVersion(v string) bool { func IsZeroPseudoVersion(v string) bool {
return v == ZeroPseudoVersion(semver.Major(v)) return v == ZeroPseudoVersion(semver.Major(v))
} }

View File

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

View File

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

View File

@ -8,7 +8,6 @@ package packagesdriver
import ( import (
"context" "context"
"fmt" "fmt"
"go/types"
"strings" "strings"
"golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/gocommand"
@ -16,7 +15,7 @@ import (
var debug = false var debug = false
func GetSizesGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (types.Sizes, error) { func GetSizesForArgsGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (string, string, 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)
@ -29,21 +28,21 @@ func GetSizesGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *
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 { } else {
return nil, friendlyErr return "", "", friendlyErr
} }
} else { } else {
fields := strings.Fields(stdout.String()) fields := strings.Fields(stdout.String())
if len(fields) < 2 { if len(fields) < 2 {
return nil, fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>", return "", "", fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>",
stdout.String(), stderr.String()) stdout.String(), stderr.String())
} }
goarch = fields[0] goarch = fields[0]
compiler = fields[1] compiler = fields[1]
} }
return types.SizesFor(compiler, goarch), nil return compiler, goarch, nil
} }

View File

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

View File

@ -9,8 +9,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"go/types"
"io/ioutil"
"log" "log"
"os" "os"
"path" "path"
@ -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()
}() }()
} }
@ -671,6 +669,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 +1108,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 +1127,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 +1145,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err
} }
// Write out the overlay file that contains the filepath mappings. // Write out the overlay file that contains the filepath mappings.
filename = filepath.Join(dir, "overlay.json") filename = filepath.Join(dir, "overlay.json")
if err := ioutil.WriteFile(filename, b, 0665); err != nil { if err := os.WriteFile(filename, b, 0665); err != nil {
return "", func() {}, err return "", func() {}, err
} }
return filename, cleanup, nil return filename, cleanup, nil

View File

@ -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"
@ -220,8 +219,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
@ -262,7 +263,7 @@ func Load(cfg *Config, patterns ...string) ([]*Package, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
l.sizes = response.Sizes l.sizes = types.SizesFor(response.Compiler, response.Arch)
return l.refine(response) return l.refine(response)
} }
@ -630,7 +631,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
@ -1043,6 +1044,9 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
Error: appendError, Error: appendError,
Sizes: ld.sizes, Sizes: ld.sizes,
} }
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) {
appendError(Error{ appendError(Error{
@ -1122,7 +1126,7 @@ func (ld *loader) parseFile(filename string) (*ast.File, error) {
var err error var err error
if src == nil { if src == nil {
ioLimit <- true // wait ioLimit <- true // wait
src, err = ioutil.ReadFile(filename) src, err = os.ReadFile(filename)
<-ioLimit // signal <-ioLimit // signal
} }
if err != nil { if err != nil {

View File

@ -0,0 +1,827 @@
// 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"
"sort"
"strconv"
"strings"
_ "unsafe"
"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/typesinternal"
)
// 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
namedMethodsMemo map[*types.Named][]*types.Func // memoization of namedMethods()
skipMethodSorting bool
}
// Expose back doors so that gopls can avoid method sorting, which can dominate
// analysis on certain repositories.
//
// TODO(golang/go#61443): remove this.
func init() {
typesinternal.SkipEncoderMethodSorting = func(enc interface{}) {
enc.(*Encoder).skipMethodSorting = true
}
typesinternal.ObjectpathObject = object
}
// 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)
if !enc.skipMethodSorting {
// Note that method index here is always with respect
// to canonical ordering of methods, regardless of how
// they appear in the underlying type.
for i, m := range enc.namedMethods(T) {
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
}
}
} else {
// This branch must match the logic in the branch above, using go/types
// APIs without sorting.
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)
if !enc.skipMethodSorting {
for i, m := range enc.namedMethods(named) {
if m == meth {
path = appendOpArg(path, opMethod, i)
return Path(path), true
}
}
} else {
// This branch must match the logic of the branch above, using go/types
// APIs without sorting.
for i := 0; i < named.NumMethods(); i++ {
m := named.Method(i)
if m == 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) {
return object(pkg, string(p), false)
}
// Note: the skipMethodSorting parameter must match the value of
// Encoder.skipMethodSorting used during encoding.
func object(pkg *types.Package, pathstr string, skipMethodSorting bool) (types.Object, error) {
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())
}
if skipMethodSorting {
obj = t.Method(index)
} else {
methods := namedMethods(t) // (unmemoized)
obj = methods[index] // Id-ordered
}
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
}
// namedMethods returns the methods of a Named type in ascending Id order.
func namedMethods(named *types.Named) []*types.Func {
methods := make([]*types.Func, named.NumMethods())
for i := range methods {
methods[i] = named.Method(i)
}
sort.Slice(methods, func(i, j int) bool {
return methods[i].Id() < methods[j].Id()
})
return methods
}
// namedMethods is a memoization of the namedMethods function. Callers must not modify the result.
func (enc *Encoder) namedMethods(named *types.Named) []*types.Func {
m := enc.namedMethodsMemo
if m == nil {
m = make(map[*types.Named][]*types.Func)
enc.namedMethodsMemo = m
}
methods, ok := m[named]
if !ok {
methods = namedMethods(named) // allocates and sorts
m[named] = methods
}
return methods
}
// scopeObjects is a memoization of scope objects.
// Callers must not modify the result.
func (enc *Encoder) scopeObjects(scope *types.Scope) []types.Object {
m := enc.scopeMemo
if m == nil {
m = make(map[*types.Scope][]types.Object)
enc.scopeMemo = m
}
objs, ok := m[scope]
if !ok {
names := scope.Names() // allocates and sorts
objs = make([]types.Object, len(names))
for i, name := range names {
objs[i] = scope.Lookup(name)
}
m[scope] = objs
}
return objs
}

View File

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

View File

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

View File

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

View File

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

View File

@ -319,7 +319,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 +333,7 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) {
// one goroutine at a time will call Write.” // one goroutine at a time will call Write.”
// //
// Since we're starting a goroutine that writes to cmd.Stdout, we must // Since we're starting a goroutine that writes to cmd.Stdout, we must
// also update cmd.Stderr so that that still holds. // also update cmd.Stderr so that it still holds.
func() { func() {
defer func() { recover() }() defer func() { recover() }()
if cmd.Stderr == prevStdout { if cmd.Stderr == prevStdout {

View File

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

View File

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

View File

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

View File

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

View File

@ -14,7 +14,6 @@ import "go/types"
// 𝓤: &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

View File

@ -0,0 +1,24 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package typesinternal
import "go/types"
// This file contains back doors that allow gopls to avoid method sorting when
// using the objectpath package.
//
// This is performance-critical in certain repositories, but changing the
// behavior of the objectpath package is still being discussed in
// golang/go#61443. If we decide to remove the sorting in objectpath we can
// simply delete these back doors. Otherwise, we should add a new API to
// objectpath that allows controlling the sorting.
// SkipEncoderMethodSorting marks enc (which must be an *objectpath.Encoder) as
// not requiring sorted methods.
var SkipEncoderMethodSorting func(enc interface{})
// ObjectpathObject is like objectpath.Object, but allows suppressing method
// sorting.
var ObjectpathObject func(pkg *types.Package, p string, skipMethodSorting bool) (types.Object, error)

9
vendor/modules.txt vendored
View File

@ -820,8 +820,8 @@ golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
## explicit; go 1.20 ## explicit; go 1.20
golang.org/x/exp/constraints golang.org/x/exp/constraints
golang.org/x/exp/slices golang.org/x/exp/slices
# golang.org/x/mod v0.11.0 # golang.org/x/mod v0.13.0
## explicit; go 1.17 ## explicit; go 1.18
golang.org/x/mod/internal/lazyregexp golang.org/x/mod/internal/lazyregexp
golang.org/x/mod/module golang.org/x/mod/module
golang.org/x/mod/semver golang.org/x/mod/semver
@ -840,7 +840,7 @@ golang.org/x/net/trace
## explicit; go 1.17 ## explicit; go 1.17
golang.org/x/oauth2 golang.org/x/oauth2
golang.org/x/oauth2/internal golang.org/x/oauth2/internal
# golang.org/x/sync v0.3.0 # golang.org/x/sync v0.4.0
## explicit; go 1.17 ## explicit; go 1.17
golang.org/x/sync/errgroup golang.org/x/sync/errgroup
golang.org/x/sync/semaphore golang.org/x/sync/semaphore
@ -865,12 +865,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.14.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