forked from toolshed/abra
chore: go mod vendor / tidy
This commit is contained in:
239
vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go
generated
vendored
Normal file
239
vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go
generated
vendored
Normal file
@ -0,0 +1,239 @@
|
||||
// Copyright 2016 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 gcexportdata provides functions for reading and writing
|
||||
// export data, which is a serialized description of the API of a Go
|
||||
// package including the names, kinds, types, and locations of all
|
||||
// exported declarations.
|
||||
//
|
||||
// The standard Go compiler (cmd/compile) writes an export data file
|
||||
// for each package it compiles, which it later reads when compiling
|
||||
// packages that import the earlier one. The compiler must thus
|
||||
// contain logic to both write and read export data.
|
||||
// (See the "Export" section in the cmd/compile/README file.)
|
||||
//
|
||||
// The [Read] function in this package can read files produced by the
|
||||
// compiler, producing [go/types] data structures. As a matter of
|
||||
// policy, Read supports export data files produced by only the last
|
||||
// two Go releases plus tip; see https://go.dev/issue/68898. The
|
||||
// export data files produced by the compiler contain additional
|
||||
// details related to generics, inlining, and other optimizations that
|
||||
// cannot be decoded by the [Read] function.
|
||||
//
|
||||
// In files written by the compiler, the export data is not at the
|
||||
// start of the file. Before calling Read, use [NewReader] to locate
|
||||
// the desired portion of the file.
|
||||
//
|
||||
// The [Write] function in this package encodes the exported API of a
|
||||
// Go package ([types.Package]) as a file. Such files can be later
|
||||
// decoded by Read, but cannot be consumed by the compiler.
|
||||
//
|
||||
// # Future changes
|
||||
//
|
||||
// Although Read supports the formats written by both Write and the
|
||||
// compiler, the two are quite different, and there is an open
|
||||
// proposal (https://go.dev/issue/69491) to separate these APIs.
|
||||
//
|
||||
// Under that proposal, this package would ultimately provide only the
|
||||
// Read operation for compiler export data, which must be defined in
|
||||
// this module (golang.org/x/tools), not in the standard library, to
|
||||
// avoid version skew for developer tools that need to read compiler
|
||||
// export data both before and after a Go release, such as from Go
|
||||
// 1.23 to Go 1.24. Because this package lives in the tools module,
|
||||
// clients can update their version of the module some time before the
|
||||
// Go 1.24 release and rebuild and redeploy their tools, which will
|
||||
// then be able to consume both Go 1.23 and Go 1.24 export data files,
|
||||
// so they will work before and after the Go update. (See discussion
|
||||
// at https://go.dev/issue/15651.)
|
||||
//
|
||||
// The operations to import and export [go/types] data structures
|
||||
// would be defined in the go/types package as Import and Export.
|
||||
// [Write] would (eventually) delegate to Export,
|
||||
// and [Read], when it detects a file produced by Export,
|
||||
// would delegate to Import.
|
||||
//
|
||||
// # Deprecations
|
||||
//
|
||||
// The [NewImporter] and [Find] functions are deprecated and should
|
||||
// not be used in new code. The [WriteBundle] and [ReadBundle]
|
||||
// functions are experimental, and there is an open proposal to
|
||||
// deprecate them (https://go.dev/issue/69573).
|
||||
package gcexportdata
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"os/exec"
|
||||
|
||||
"golang.org/x/tools/internal/gcimporter"
|
||||
)
|
||||
|
||||
// Find returns the name of an object (.o) or archive (.a) file
|
||||
// containing type information for the specified import path,
|
||||
// using the go command.
|
||||
// If no file was found, an empty filename is returned.
|
||||
//
|
||||
// A relative srcDir is interpreted relative to the current working directory.
|
||||
//
|
||||
// Find also returns the package's resolved (canonical) import path,
|
||||
// reflecting the effects of srcDir and vendoring on importPath.
|
||||
//
|
||||
// Deprecated: Use the higher-level API in golang.org/x/tools/go/packages,
|
||||
// which is more efficient.
|
||||
func Find(importPath, srcDir string) (filename, path string) {
|
||||
cmd := exec.Command("go", "list", "-json", "-export", "--", importPath)
|
||||
cmd.Dir = srcDir
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", ""
|
||||
}
|
||||
var data struct {
|
||||
ImportPath string
|
||||
Export string
|
||||
}
|
||||
json.Unmarshal(out, &data)
|
||||
return data.Export, data.ImportPath
|
||||
}
|
||||
|
||||
// NewReader returns a reader for the export data section of an object
|
||||
// (.o) or archive (.a) file read from r. The new reader may provide
|
||||
// additional trailing data beyond the end of the export data.
|
||||
func NewReader(r io.Reader) (io.Reader, error) {
|
||||
buf := bufio.NewReader(r)
|
||||
size, err := gcimporter.FindExportData(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We were given an archive and found the __.PKGDEF in it.
|
||||
// This tells us the size of the export data, and we don't
|
||||
// need to return the entire file.
|
||||
return &io.LimitedReader{
|
||||
R: buf,
|
||||
N: size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// readAll works the same way as io.ReadAll, but avoids allocations and copies
|
||||
// by preallocating a byte slice of the necessary size if the size is known up
|
||||
// front. This is always possible when the input is an archive. In that case,
|
||||
// NewReader will return the known size using an io.LimitedReader.
|
||||
func readAll(r io.Reader) ([]byte, error) {
|
||||
if lr, ok := r.(*io.LimitedReader); ok {
|
||||
data := make([]byte, lr.N)
|
||||
_, err := io.ReadFull(lr, data)
|
||||
return data, err
|
||||
}
|
||||
return io.ReadAll(r)
|
||||
}
|
||||
|
||||
// Read reads export data from in, decodes it, and returns type
|
||||
// information for the package.
|
||||
//
|
||||
// Read is capable of reading export data produced by [Write] at the
|
||||
// same source code version, or by the last two Go releases (plus tip)
|
||||
// of the standard Go compiler. Reading files from older compilers may
|
||||
// produce an error.
|
||||
//
|
||||
// The package path (effectively its linker symbol prefix) is
|
||||
// specified by path, since unlike the package name, this information
|
||||
// may not be recorded in the export data.
|
||||
//
|
||||
// File position information is added to fset.
|
||||
//
|
||||
// Read may inspect and add to the imports map to ensure that references
|
||||
// within the export data to other packages are consistent. The caller
|
||||
// must ensure that imports[path] does not exist, or exists but is
|
||||
// incomplete (see types.Package.Complete), and Read inserts the
|
||||
// resulting package into this map entry.
|
||||
//
|
||||
// On return, the state of the reader is undefined.
|
||||
func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) {
|
||||
data, err := readAll(in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading export data for %q: %v", path, err)
|
||||
}
|
||||
|
||||
if bytes.HasPrefix(data, []byte("!<arch>")) {
|
||||
return nil, fmt.Errorf("can't read export data for %q directly from an archive file (call gcexportdata.NewReader first to extract export data)", path)
|
||||
}
|
||||
|
||||
// The indexed export format starts with an 'i'; the older
|
||||
// binary export format starts with a 'c', 'd', or 'v'
|
||||
// (from "version"). Select appropriate importer.
|
||||
if len(data) > 0 {
|
||||
switch data[0] {
|
||||
case 'v', 'c', 'd':
|
||||
// binary, produced by cmd/compile till go1.10
|
||||
return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0])
|
||||
|
||||
case 'i':
|
||||
// indexed, produced by cmd/compile till go1.19,
|
||||
// and also by [Write].
|
||||
//
|
||||
// If proposal #69491 is accepted, go/types
|
||||
// serialization will be implemented by
|
||||
// types.Export, to which Write would eventually
|
||||
// delegate (explicitly dropping any pretence at
|
||||
// inter-version Write-Read compatibility).
|
||||
// This [Read] function would delegate to types.Import
|
||||
// when it detects that the file was produced by Export.
|
||||
_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
|
||||
return pkg, err
|
||||
|
||||
case 'u':
|
||||
// unified, produced by cmd/compile since go1.20
|
||||
_, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path)
|
||||
return pkg, err
|
||||
|
||||
default:
|
||||
l := len(data)
|
||||
if l > 10 {
|
||||
l = 10
|
||||
}
|
||||
return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string(data[:l]), path)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("empty export data for %s", path)
|
||||
}
|
||||
|
||||
// Write writes encoded type information for the specified package to out.
|
||||
// The FileSet provides file position information for named objects.
|
||||
func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error {
|
||||
if _, err := io.WriteString(out, "i"); err != nil {
|
||||
return err
|
||||
}
|
||||
return gcimporter.IExportData(out, fset, pkg)
|
||||
}
|
||||
|
||||
// ReadBundle reads an export bundle from in, decodes it, and returns type
|
||||
// information for the packages.
|
||||
// File position information is added to fset.
|
||||
//
|
||||
// ReadBundle may inspect and add to the imports map to ensure that references
|
||||
// within the export bundle to other packages are consistent.
|
||||
//
|
||||
// On return, the state of the reader is undefined.
|
||||
//
|
||||
// Experimental: This API is experimental and may change in the future.
|
||||
func ReadBundle(in io.Reader, fset *token.FileSet, imports map[string]*types.Package) ([]*types.Package, error) {
|
||||
data, err := readAll(in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading export bundle: %v", err)
|
||||
}
|
||||
return gcimporter.IImportBundle(fset, imports, data)
|
||||
}
|
||||
|
||||
// WriteBundle writes encoded type information for the specified packages to out.
|
||||
// The FileSet provides file position information for named objects.
|
||||
//
|
||||
// Experimental: This API is experimental and may change in the future.
|
||||
func WriteBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error {
|
||||
return gcimporter.IExportBundle(out, fset, pkgs)
|
||||
}
|
75
vendor/golang.org/x/tools/go/gcexportdata/importer.go
generated
vendored
Normal file
75
vendor/golang.org/x/tools/go/gcexportdata/importer.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2016 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 gcexportdata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"os"
|
||||
)
|
||||
|
||||
// NewImporter returns a new instance of the types.Importer interface
|
||||
// that reads type information from export data files written by gc.
|
||||
// The Importer also satisfies types.ImporterFrom.
|
||||
//
|
||||
// Export data files are located using "go build" workspace conventions
|
||||
// and the build.Default context.
|
||||
//
|
||||
// Use this importer instead of go/importer.For("gc", ...) to avoid the
|
||||
// version-skew problems described in the documentation of this package,
|
||||
// or to control the FileSet or access the imports map populated during
|
||||
// package loading.
|
||||
//
|
||||
// Deprecated: Use the higher-level API in golang.org/x/tools/go/packages,
|
||||
// which is more efficient.
|
||||
func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom {
|
||||
return importer{fset, imports}
|
||||
}
|
||||
|
||||
type importer struct {
|
||||
fset *token.FileSet
|
||||
imports map[string]*types.Package
|
||||
}
|
||||
|
||||
func (imp importer) Import(importPath string) (*types.Package, error) {
|
||||
return imp.ImportFrom(importPath, "", 0)
|
||||
}
|
||||
|
||||
func (imp importer) ImportFrom(importPath, srcDir string, mode types.ImportMode) (_ *types.Package, err error) {
|
||||
filename, path := Find(importPath, srcDir)
|
||||
if filename == "" {
|
||||
if importPath == "unsafe" {
|
||||
// Even for unsafe, call Find first in case
|
||||
// the package was vendored.
|
||||
return types.Unsafe, nil
|
||||
}
|
||||
return nil, fmt.Errorf("can't find import: %s", importPath)
|
||||
}
|
||||
|
||||
if pkg, ok := imp.imports[path]; ok && pkg.Complete() {
|
||||
return pkg, nil // cache hit
|
||||
}
|
||||
|
||||
// open file
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
f.Close()
|
||||
if err != nil {
|
||||
// add file name to error
|
||||
err = fmt.Errorf("reading export data: %s: %v", filename, err)
|
||||
}
|
||||
}()
|
||||
|
||||
r, err := NewReader(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return Read(r, imp.fset, imp.imports, path)
|
||||
}
|
251
vendor/golang.org/x/tools/go/packages/doc.go
generated
vendored
Normal file
251
vendor/golang.org/x/tools/go/packages/doc.go
generated
vendored
Normal file
@ -0,0 +1,251 @@
|
||||
// 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 packages loads Go packages for inspection and analysis.
|
||||
|
||||
The [Load] function takes as input a list of patterns and returns a
|
||||
list of [Package] values describing individual packages matched by those
|
||||
patterns.
|
||||
A [Config] specifies configuration options, the most important of which is
|
||||
the [LoadMode], which controls the amount of detail in the loaded packages.
|
||||
|
||||
Load passes most patterns directly to the underlying build tool.
|
||||
The default build tool is the go command.
|
||||
Its supported patterns are described at
|
||||
https://pkg.go.dev/cmd/go#hdr-Package_lists_and_patterns.
|
||||
Other build systems may be supported by providing a "driver";
|
||||
see [The driver protocol].
|
||||
|
||||
All patterns with the prefix "query=", where query is a
|
||||
non-empty string of letters from [a-z], are reserved and may be
|
||||
interpreted as query operators.
|
||||
|
||||
Two query operators are currently supported: "file" and "pattern".
|
||||
|
||||
The query "file=path/to/file.go" matches the package or packages enclosing
|
||||
the Go source file path/to/file.go. For example "file=~/go/src/fmt/print.go"
|
||||
might return the packages "fmt" and "fmt [fmt.test]".
|
||||
|
||||
The query "pattern=string" causes "string" to be passed directly to
|
||||
the underlying build tool. In most cases this is unnecessary,
|
||||
but an application can use Load("pattern=" + x) as an escaping mechanism
|
||||
to ensure that x is not interpreted as a query operator if it contains '='.
|
||||
|
||||
All other query operators are reserved for future use and currently
|
||||
cause Load to report an error.
|
||||
|
||||
The Package struct provides basic information about the package, including
|
||||
|
||||
- ID, a unique identifier for the package in the returned set;
|
||||
- GoFiles, the names of the package's Go source files;
|
||||
- Imports, a map from source import strings to the Packages they name;
|
||||
- Types, the type information for the package's exported symbols;
|
||||
- Syntax, the parsed syntax trees for the package's source code; and
|
||||
- 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
|
||||
and more detailed descriptions.)
|
||||
|
||||
For example,
|
||||
|
||||
Load(nil, "bytes", "unicode...")
|
||||
|
||||
returns four Package structs describing the standard library packages
|
||||
bytes, unicode, unicode/utf16, and unicode/utf8. Note that one pattern
|
||||
can match multiple packages and that a package might be matched by
|
||||
multiple patterns: in general it is not possible to determine which
|
||||
packages correspond to which patterns.
|
||||
|
||||
Note that the list returned by Load contains only the packages matched
|
||||
by the patterns. Their dependencies can be found by walking the import
|
||||
graph using the Imports fields.
|
||||
|
||||
The Load function can be configured by passing a pointer to a Config as
|
||||
the first argument. A nil Config is equivalent to the zero Config, which
|
||||
causes Load to run in [LoadFiles] mode, collecting minimal information.
|
||||
See the documentation for type Config for details.
|
||||
|
||||
As noted earlier, the Config.Mode controls the amount of detail
|
||||
reported about the loaded packages. See the documentation for type LoadMode
|
||||
for details.
|
||||
|
||||
Most tools should pass their command-line arguments (after any flags)
|
||||
uninterpreted to Load, so that it can interpret them
|
||||
according to the conventions of the underlying build system.
|
||||
|
||||
See the Example function for typical usage.
|
||||
|
||||
# The driver protocol
|
||||
|
||||
Load may be used to load Go packages even in Go projects that use
|
||||
alternative build systems, by installing an appropriate "driver"
|
||||
program for the build system and specifying its location in the
|
||||
GOPACKAGESDRIVER environment variable.
|
||||
For example,
|
||||
https://github.com/bazelbuild/rules_go/wiki/Editor-and-tool-integration
|
||||
explains how to use the driver for Bazel.
|
||||
|
||||
The driver program is responsible for interpreting patterns in its
|
||||
preferred notation and reporting information about the packages that
|
||||
those patterns identify. Drivers must also support the special "file="
|
||||
and "pattern=" patterns described above.
|
||||
|
||||
The patterns are provided as positional command-line arguments. A
|
||||
JSON-encoded [DriverRequest] message providing additional information
|
||||
is written to the driver's standard input. The driver must write a
|
||||
JSON-encoded [DriverResponse] message to its standard output. (This
|
||||
message differs from the JSON schema produced by 'go list'.)
|
||||
|
||||
The value of the PWD environment variable seen by the driver process
|
||||
is the preferred name of its working directory. (The working directory
|
||||
may have other aliases due to symbolic links; see the comment on the
|
||||
Dir field of [exec.Cmd] for related information.)
|
||||
When the driver process emits in its response the name of a file
|
||||
that is a descendant of this directory, it must use an absolute path
|
||||
that has the value of PWD as a prefix, to ensure that the returned
|
||||
filenames satisfy the original query.
|
||||
*/
|
||||
package packages // import "golang.org/x/tools/go/packages"
|
||||
|
||||
/*
|
||||
|
||||
Motivation and design considerations
|
||||
|
||||
The new package's design solves problems addressed by two existing
|
||||
packages: go/build, which locates and describes packages, and
|
||||
golang.org/x/tools/go/loader, which loads, parses and type-checks them.
|
||||
The go/build.Package structure encodes too much of the 'go build' way
|
||||
of organizing projects, leaving us in need of a data type that describes a
|
||||
package of Go source code independent of the underlying build system.
|
||||
We wanted something that works equally well with go build and vgo, and
|
||||
also other build systems such as Bazel and Blaze, making it possible to
|
||||
construct analysis tools that work in all these environments.
|
||||
Tools such as errcheck and staticcheck were essentially unavailable to
|
||||
the Go community at Google, and some of Google's internal tools for Go
|
||||
are unavailable externally.
|
||||
This new package provides a uniform way to obtain package metadata by
|
||||
querying each of these build systems, optionally supporting their
|
||||
preferred command-line notations for packages, so that tools integrate
|
||||
neatly with users' build environments. The Metadata query function
|
||||
executes an external query tool appropriate to the current workspace.
|
||||
|
||||
Loading packages always returns the complete import graph "all the way down",
|
||||
even if all you want is information about a single package, because the query
|
||||
mechanisms of all the build systems we currently support ({go,vgo} list, and
|
||||
blaze/bazel aspect-based query) cannot provide detailed information
|
||||
about one package without visiting all its dependencies too, so there is
|
||||
no additional asymptotic cost to providing transitive information.
|
||||
(This property might not be true of a hypothetical 5th build system.)
|
||||
|
||||
In calls to TypeCheck, all initial packages, and any package that
|
||||
transitively depends on one of them, must be loaded from source.
|
||||
Consider A->B->C->D->E: if A,C are initial, A,B,C must be loaded from
|
||||
source; D may be loaded from export data, and E may not be loaded at all
|
||||
(though it's possible that D's export data mentions it, so a
|
||||
types.Package may be created for it and exposed.)
|
||||
|
||||
The old loader had a feature to suppress type-checking of function
|
||||
bodies on a per-package basis, primarily intended to reduce the work of
|
||||
obtaining type information for imported packages. Now that imports are
|
||||
satisfied by export data, the optimization no longer seems necessary.
|
||||
|
||||
Despite some early attempts, the old loader did not exploit export data,
|
||||
instead always using the equivalent of WholeProgram mode. This was due
|
||||
to the complexity of mixing source and export data packages (now
|
||||
resolved by the upward traversal mentioned above), and because export data
|
||||
files were nearly always missing or stale. Now that 'go build' supports
|
||||
caching, all the underlying build systems can guarantee to produce
|
||||
export data in a reasonable (amortized) time.
|
||||
|
||||
Test "main" packages synthesized by the build system are now reported as
|
||||
first-class packages, avoiding the need for clients (such as go/ssa) to
|
||||
reinvent this generation logic.
|
||||
|
||||
One way in which go/packages is simpler than the old loader is in its
|
||||
treatment of in-package tests. In-package tests are packages that
|
||||
consist of all the files of the library under test, plus the test files.
|
||||
The old loader constructed in-package tests by a two-phase process of
|
||||
mutation called "augmentation": first it would construct and type check
|
||||
all the ordinary library packages and type-check the packages that
|
||||
depend on them; then it would add more (test) files to the package and
|
||||
type-check again. This two-phase approach had four major problems:
|
||||
1) in processing the tests, the loader modified the library package,
|
||||
leaving no way for a client application to see both the test
|
||||
package and the library package; one would mutate into the other.
|
||||
2) because test files can declare additional methods on types defined in
|
||||
the library portion of the package, the dispatch of method calls in
|
||||
the library portion was affected by the presence of the test files.
|
||||
This should have been a clue that the packages were logically
|
||||
different.
|
||||
3) this model of "augmentation" assumed at most one in-package test
|
||||
per library package, which is true of projects using 'go build',
|
||||
but not other build systems.
|
||||
4) because of the two-phase nature of test processing, all packages that
|
||||
import the library package had to be processed before augmentation,
|
||||
forcing a "one-shot" API and preventing the client from calling Load
|
||||
in several times in sequence as is now possible in WholeProgram mode.
|
||||
(TypeCheck mode has a similar one-shot restriction for a different reason.)
|
||||
|
||||
Early drafts of this package supported "multi-shot" operation.
|
||||
Although it allowed clients to make a sequence of calls (or concurrent
|
||||
calls) to Load, building up the graph of Packages incrementally,
|
||||
it was of marginal value: it complicated the API
|
||||
(since it allowed some options to vary across calls but not others),
|
||||
it complicated the implementation,
|
||||
it cannot be made to work in Types mode, as explained above,
|
||||
and it was less efficient than making one combined call (when this is possible).
|
||||
Among the clients we have inspected, none made multiple calls to load
|
||||
but could not be easily and satisfactorily modified to make only a single call.
|
||||
However, applications changes may be required.
|
||||
For example, the ssadump command loads the user-specified packages
|
||||
and in addition the runtime package. It is tempting to simply append
|
||||
"runtime" to the user-provided list, but that does not work if the user
|
||||
specified an ad-hoc package such as [a.go b.go].
|
||||
Instead, ssadump no longer requests the runtime package,
|
||||
but seeks it among the dependencies of the user-specified packages,
|
||||
and emits an error if it is not found.
|
||||
|
||||
Questions & Tasks
|
||||
|
||||
- Add GOARCH/GOOS?
|
||||
They are not portable concepts, but could be made portable.
|
||||
Our goal has been to allow users to express themselves using the conventions
|
||||
of the underlying build system: if the build system honors GOARCH
|
||||
during a build and during a metadata query, then so should
|
||||
applications built atop that query mechanism.
|
||||
Conversely, if the target architecture of the build is determined by
|
||||
command-line flags, the application can pass the relevant
|
||||
flags through to the build system using a command such as:
|
||||
myapp -query_flag="--cpu=amd64" -query_flag="--os=darwin"
|
||||
However, this approach is low-level, unwieldy, and non-portable.
|
||||
GOOS and GOARCH seem important enough to warrant a dedicated option.
|
||||
|
||||
- How should we handle partial failures such as a mixture of good and
|
||||
malformed patterns, existing and non-existent packages, successful and
|
||||
failed builds, import failures, import cycles, and so on, in a call to
|
||||
Load?
|
||||
|
||||
- Support bazel, blaze, and go1.10 list, not just go1.11 list.
|
||||
|
||||
- Handle (and test) various partial success cases, e.g.
|
||||
a mixture of good packages and:
|
||||
invalid patterns
|
||||
nonexistent packages
|
||||
empty packages
|
||||
packages with malformed package or import declarations
|
||||
unreadable files
|
||||
import cycles
|
||||
other parse errors
|
||||
type errors
|
||||
Make sure we record errors at the correct place in the graph.
|
||||
|
||||
- Missing packages among initial arguments are not reported.
|
||||
Return bogus packages for them, like golist does.
|
||||
|
||||
- "undeclared name" errors (for example) are reported out of source file
|
||||
order. I suspect this is due to the breadth-first resolution now used
|
||||
by go/types. Is that a bug? Discuss with gri.
|
||||
|
||||
*/
|
153
vendor/golang.org/x/tools/go/packages/external.go
generated
vendored
Normal file
153
vendor/golang.org/x/tools/go/packages/external.go
generated
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
// 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 packages
|
||||
|
||||
// This file defines the protocol that enables an external "driver"
|
||||
// tool to supply package metadata in place of 'go list'.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DriverRequest defines the schema of a request for package metadata
|
||||
// from an external driver program. The JSON-encoded DriverRequest
|
||||
// message is provided to the driver program's standard input. The
|
||||
// query patterns are provided as command-line arguments.
|
||||
//
|
||||
// See the package documentation for an overview.
|
||||
type DriverRequest struct {
|
||||
Mode LoadMode `json:"mode"`
|
||||
|
||||
// Env specifies the environment the underlying build system should be run in.
|
||||
Env []string `json:"env"`
|
||||
|
||||
// BuildFlags are flags that should be passed to the underlying build system.
|
||||
BuildFlags []string `json:"build_flags"`
|
||||
|
||||
// Tests specifies whether the patterns should also return test packages.
|
||||
Tests bool `json:"tests"`
|
||||
|
||||
// Overlay maps file paths (relative to the driver's working directory)
|
||||
// to the contents of overlay files (see Config.Overlay).
|
||||
Overlay map[string][]byte `json:"overlay"`
|
||||
}
|
||||
|
||||
// DriverResponse defines the schema of a response from an external
|
||||
// driver program, providing the results of a query for package
|
||||
// metadata. The driver program must write a JSON-encoded
|
||||
// DriverResponse message to its standard output.
|
||||
//
|
||||
// See the package documentation for an overview.
|
||||
type DriverResponse struct {
|
||||
// NotHandled is returned if the request can't be handled by the current
|
||||
// driver. If an external driver returns a response with NotHandled, the
|
||||
// rest of the DriverResponse is ignored, and go/packages will fallback
|
||||
// to the next driver. If go/packages is extended in the future to support
|
||||
// lists of multiple drivers, go/packages will fall back to the next driver.
|
||||
NotHandled bool
|
||||
|
||||
// Compiler and Arch are the arguments pass of types.SizesFor
|
||||
// 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.
|
||||
// We have to encode this separately because when we encode a single package
|
||||
// we cannot know if it is one of the roots as that requires knowledge of the
|
||||
// graph it is part of.
|
||||
Roots []string `json:",omitempty"`
|
||||
|
||||
// Packages is the full set of packages in the graph.
|
||||
// The packages are not connected into a graph.
|
||||
// The Imports if populated will be stubs that only have their ID set.
|
||||
// Imports will be connected and then type and syntax information added in a
|
||||
// later pass (see refine).
|
||||
Packages []*Package
|
||||
|
||||
// GoVersion is the minor version number used by the driver
|
||||
// (e.g. the go command on the PATH) when selecting .go files.
|
||||
// Zero means unknown.
|
||||
GoVersion int
|
||||
}
|
||||
|
||||
// driver is the type for functions that query the build system for the
|
||||
// packages named by the patterns.
|
||||
type driver func(cfg *Config, patterns []string) (*DriverResponse, error)
|
||||
|
||||
// findExternalDriver returns the file path of a tool that supplies
|
||||
// the build system package structure, or "" if not found.
|
||||
// If GOPACKAGESDRIVER is set in the environment findExternalTool returns its
|
||||
// value, otherwise it searches for a binary named gopackagesdriver on the PATH.
|
||||
func findExternalDriver(cfg *Config) driver {
|
||||
const toolPrefix = "GOPACKAGESDRIVER="
|
||||
tool := ""
|
||||
for _, env := range cfg.Env {
|
||||
if val := strings.TrimPrefix(env, toolPrefix); val != env {
|
||||
tool = val
|
||||
}
|
||||
}
|
||||
if tool != "" && tool == "off" {
|
||||
return nil
|
||||
}
|
||||
if tool == "" {
|
||||
var err error
|
||||
tool, err = exec.LookPath("gopackagesdriver")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return func(cfg *Config, patterns []string) (*DriverResponse, error) {
|
||||
req, err := json.Marshal(DriverRequest{
|
||||
Mode: cfg.Mode,
|
||||
Env: cfg.Env,
|
||||
BuildFlags: cfg.BuildFlags,
|
||||
Tests: cfg.Tests,
|
||||
Overlay: cfg.Overlay,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode message to driver tool: %v", err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
stderr := new(bytes.Buffer)
|
||||
cmd := exec.CommandContext(cfg.Context, tool, patterns...)
|
||||
cmd.Dir = cfg.Dir
|
||||
// The cwd gets resolved to the real path. On Darwin, where
|
||||
// /tmp is a symlink, this breaks anything that expects the
|
||||
// working directory to keep the original path, including the
|
||||
// go command when dealing with modules.
|
||||
//
|
||||
// os.Getwd stdlib has a special feature where if the
|
||||
// cwd and the PWD are the same node then it trusts
|
||||
// the PWD, so by setting it in the env for the child
|
||||
// process we fix up all the paths returned by the go
|
||||
// command.
|
||||
//
|
||||
// (See similar trick in Invocation.run in ../../internal/gocommand/invoke.go)
|
||||
cmd.Env = append(slices.Clip(cfg.Env), "PWD="+cfg.Dir)
|
||||
cmd.Stdin = bytes.NewReader(req)
|
||||
cmd.Stdout = buf
|
||||
cmd.Stderr = stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr)
|
||||
}
|
||||
if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTDRIVERERRORS") != "" {
|
||||
fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd), stderr)
|
||||
}
|
||||
|
||||
var response DriverResponse
|
||||
if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
}
|
1081
vendor/golang.org/x/tools/go/packages/golist.go
generated
vendored
Normal file
1081
vendor/golang.org/x/tools/go/packages/golist.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
83
vendor/golang.org/x/tools/go/packages/golist_overlay.go
generated
vendored
Normal file
83
vendor/golang.org/x/tools/go/packages/golist_overlay.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
// 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 packages
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/tools/internal/gocommand"
|
||||
)
|
||||
|
||||
// determineRootDirs returns a mapping from absolute directories that could
|
||||
// contain code to their corresponding import path prefixes.
|
||||
func (state *golistState) determineRootDirs() (map[string]string, error) {
|
||||
env, err := state.getEnv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if env["GOMOD"] != "" {
|
||||
state.rootsOnce.Do(func() {
|
||||
state.rootDirs, state.rootDirsError = state.determineRootDirsModules()
|
||||
})
|
||||
} else {
|
||||
state.rootsOnce.Do(func() {
|
||||
state.rootDirs, state.rootDirsError = state.determineRootDirsGOPATH()
|
||||
})
|
||||
}
|
||||
return state.rootDirs, state.rootDirsError
|
||||
}
|
||||
|
||||
func (state *golistState) determineRootDirsModules() (map[string]string, error) {
|
||||
// List all of the modules--the first will be the directory for the main
|
||||
// module. Any replaced modules will also need to be treated as roots.
|
||||
// Editing files in the module cache isn't a great idea, so we don't
|
||||
// plan to ever support that.
|
||||
out, err := state.invokeGo("list", "-m", "-json", "all")
|
||||
if err != nil {
|
||||
// 'go list all' will fail if we're outside of a module and
|
||||
// GO111MODULE=on. Try falling back without 'all'.
|
||||
var innerErr error
|
||||
out, innerErr = state.invokeGo("list", "-m", "-json")
|
||||
if innerErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
roots := map[string]string{}
|
||||
modules := map[string]string{}
|
||||
var i int
|
||||
for dec := json.NewDecoder(out); dec.More(); {
|
||||
mod := new(gocommand.ModuleJSON)
|
||||
if err := dec.Decode(mod); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if mod.Dir != "" && mod.Path != "" {
|
||||
// This is a valid module; add it to the map.
|
||||
absDir, err := filepath.Abs(mod.Dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
modules[absDir] = mod.Path
|
||||
// The first result is the main module.
|
||||
if i == 0 || mod.Replace != nil && mod.Replace.Path != "" {
|
||||
roots[absDir] = mod.Path
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
return roots, nil
|
||||
}
|
||||
|
||||
func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) {
|
||||
m := map[string]string{}
|
||||
for _, dir := range filepath.SplitList(state.mustGetEnv()["GOPATH"]) {
|
||||
absDir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m[filepath.Join(absDir, "src")] = ""
|
||||
}
|
||||
return m, nil
|
||||
}
|
55
vendor/golang.org/x/tools/go/packages/loadmode_string.go
generated
vendored
Normal file
55
vendor/golang.org/x/tools/go/packages/loadmode_string.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2019 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 packages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var modes = [...]struct {
|
||||
mode LoadMode
|
||||
name string
|
||||
}{
|
||||
{NeedName, "NeedName"},
|
||||
{NeedFiles, "NeedFiles"},
|
||||
{NeedCompiledGoFiles, "NeedCompiledGoFiles"},
|
||||
{NeedImports, "NeedImports"},
|
||||
{NeedDeps, "NeedDeps"},
|
||||
{NeedExportFile, "NeedExportFile"},
|
||||
{NeedTypes, "NeedTypes"},
|
||||
{NeedSyntax, "NeedSyntax"},
|
||||
{NeedTypesInfo, "NeedTypesInfo"},
|
||||
{NeedTypesSizes, "NeedTypesSizes"},
|
||||
{NeedForTest, "NeedForTest"},
|
||||
{NeedModule, "NeedModule"},
|
||||
{NeedEmbedFiles, "NeedEmbedFiles"},
|
||||
{NeedEmbedPatterns, "NeedEmbedPatterns"},
|
||||
}
|
||||
|
||||
func (mode LoadMode) String() string {
|
||||
if mode == 0 {
|
||||
return "LoadMode(0)"
|
||||
}
|
||||
var out []string
|
||||
// named bits
|
||||
for _, item := range modes {
|
||||
if (mode & item.mode) != 0 {
|
||||
mode ^= item.mode
|
||||
out = append(out, item.name)
|
||||
}
|
||||
}
|
||||
// unnamed residue
|
||||
if mode != 0 {
|
||||
if out == nil {
|
||||
return fmt.Sprintf("LoadMode(%#x)", int(mode))
|
||||
}
|
||||
out = append(out, fmt.Sprintf("%#x", int(mode)))
|
||||
}
|
||||
if len(out) == 1 {
|
||||
return out[0]
|
||||
}
|
||||
return "(" + strings.Join(out, "|") + ")"
|
||||
}
|
1564
vendor/golang.org/x/tools/go/packages/packages.go
generated
vendored
Normal file
1564
vendor/golang.org/x/tools/go/packages/packages.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
68
vendor/golang.org/x/tools/go/packages/visit.go
generated
vendored
Normal file
68
vendor/golang.org/x/tools/go/packages/visit.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
// 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 packages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Visit visits all the packages in the import graph whose roots are
|
||||
// pkgs, calling the optional pre function the first time each package
|
||||
// is encountered (preorder), and the optional post function after a
|
||||
// package's dependencies have been visited (postorder).
|
||||
// The boolean result of pre(pkg) determines whether
|
||||
// the imports of package pkg are visited.
|
||||
func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) {
|
||||
seen := make(map[*Package]bool)
|
||||
var visit func(*Package)
|
||||
visit = func(pkg *Package) {
|
||||
if !seen[pkg] {
|
||||
seen[pkg] = true
|
||||
|
||||
if pre == nil || pre(pkg) {
|
||||
paths := make([]string, 0, len(pkg.Imports))
|
||||
for path := range pkg.Imports {
|
||||
paths = append(paths, path)
|
||||
}
|
||||
sort.Strings(paths) // Imports is a map, this makes visit stable
|
||||
for _, path := range paths {
|
||||
visit(pkg.Imports[path])
|
||||
}
|
||||
}
|
||||
|
||||
if post != nil {
|
||||
post(pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, pkg := range pkgs {
|
||||
visit(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintErrors prints to os.Stderr the accumulated errors of all
|
||||
// packages in the import graph rooted at pkgs, dependencies first.
|
||||
// PrintErrors returns the number of errors printed.
|
||||
func PrintErrors(pkgs []*Package) int {
|
||||
var n int
|
||||
errModules := make(map[*Module]bool)
|
||||
Visit(pkgs, nil, func(pkg *Package) {
|
||||
for _, err := range pkg.Errors {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
n++
|
||||
}
|
||||
|
||||
// Print pkg.Module.Error once if present.
|
||||
mod := pkg.Module
|
||||
if mod != nil && mod.Error != nil && !errModules[mod] {
|
||||
errModules[mod] = true
|
||||
fmt.Fprintln(os.Stderr, mod.Error.Err)
|
||||
n++
|
||||
}
|
||||
})
|
||||
return n
|
||||
}
|
817
vendor/golang.org/x/tools/go/types/objectpath/objectpath.go
generated
vendored
Normal file
817
vendor/golang.org/x/tools/go/types/objectpath/objectpath.go
generated
vendored
Normal file
@ -0,0 +1,817 @@
|
||||
// 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/aliases"
|
||||
"golang.org/x/tools/internal/typesinternal"
|
||||
)
|
||||
|
||||
// TODO(adonovan): think about generic aliases.
|
||||
|
||||
// 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,{,{,Recv}Type}Params,Results,Underlying,Rhs} [EKPRUTrCa]
|
||||
// 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 [EKPRUTrCa];
|
||||
// two of these ({,Recv}TypeParams) require 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)
|
||||
opRecvTypeParam = 'r' // .RecvTypeParams.At(i) (Signature)
|
||||
opConstraint = 'C' // .Constraint() (TypeParam)
|
||||
opRhs = 'a' // .Rhs() (Alias)
|
||||
|
||||
// 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 := types.Unalias(obj.Type()).(*types.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 alias, ok := T.(*types.Alias); ok {
|
||||
if r := findTypeParam(obj, aliases.TypeParams(alias), path, opTypeParam); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
if r := find(obj, aliases.Rhs(alias), append(path, opRhs)); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
|
||||
} else if tname.IsAlias() {
|
||||
// legacy alias
|
||||
if r := find(obj, T, path); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
|
||||
} else if named, ok := T.(*types.Named); ok {
|
||||
// defined (named) type
|
||||
if r := findTypeParam(obj, named.TypeParams(), path, opTypeParam); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
if r := find(obj, named.Underlying(), append(path, opUnderlying)); 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)); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Inspect declared methods of defined types.
|
||||
if T, ok := types.Unalias(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)); 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 meth.Origin() != meth {
|
||||
return "", false
|
||||
}
|
||||
|
||||
_, named := typesinternal.ReceiverNamed(meth.Type().(*types.Signature).Recv())
|
||||
if named == nil {
|
||||
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.
|
||||
//
|
||||
// The seenMethods map is used internally to short circuit cycles through
|
||||
// interface methods, such as occur in the following example:
|
||||
//
|
||||
// type I interface { f() interface{I} }
|
||||
//
|
||||
// See golang/go#68046 for details.
|
||||
func find(obj types.Object, T types.Type, path []byte) []byte {
|
||||
return (&finder{obj: obj}).find(T, path)
|
||||
}
|
||||
|
||||
// finder closes over search state for a call to find.
|
||||
type finder struct {
|
||||
obj types.Object // the sought object
|
||||
seenTParamNames map[*types.TypeName]bool // for cycle breaking through type parameters
|
||||
seenMethods map[*types.Func]bool // for cycle breaking through recursive interfaces
|
||||
}
|
||||
|
||||
func (f *finder) find(T types.Type, path []byte) []byte {
|
||||
switch T := T.(type) {
|
||||
case *types.Alias:
|
||||
return f.find(types.Unalias(T), path)
|
||||
case *types.Basic, *types.Named:
|
||||
// Named types belonging to pkg were handled already,
|
||||
// so T must belong to another package. No path.
|
||||
return nil
|
||||
case *types.Pointer:
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Slice:
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Array:
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Chan:
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Map:
|
||||
if r := f.find(T.Key(), append(path, opKey)); r != nil {
|
||||
return r
|
||||
}
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Signature:
|
||||
if r := f.findTypeParam(T.RecvTypeParams(), path, opRecvTypeParam); r != nil {
|
||||
return r
|
||||
}
|
||||
if r := f.findTypeParam(T.TypeParams(), path, opTypeParam); r != nil {
|
||||
return r
|
||||
}
|
||||
if r := f.find(T.Params(), append(path, opParams)); r != nil {
|
||||
return r
|
||||
}
|
||||
return f.find(T.Results(), append(path, opResults))
|
||||
case *types.Struct:
|
||||
for i := 0; i < T.NumFields(); i++ {
|
||||
fld := T.Field(i)
|
||||
path2 := appendOpArg(path, opField, i)
|
||||
if fld == f.obj {
|
||||
return path2 // found field var
|
||||
}
|
||||
if r := f.find(fld.Type(), append(path2, opType)); 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 == f.obj {
|
||||
return path2 // found param/result var
|
||||
}
|
||||
if r := f.find(v.Type(), append(path2, opType)); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case *types.Interface:
|
||||
for i := 0; i < T.NumMethods(); i++ {
|
||||
m := T.Method(i)
|
||||
if f.seenMethods[m] {
|
||||
return nil
|
||||
}
|
||||
path2 := appendOpArg(path, opMethod, i)
|
||||
if m == f.obj {
|
||||
return path2 // found interface method
|
||||
}
|
||||
if f.seenMethods == nil {
|
||||
f.seenMethods = make(map[*types.Func]bool)
|
||||
}
|
||||
f.seenMethods[m] = true
|
||||
if r := f.find(m.Type(), append(path2, opType)); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case *types.TypeParam:
|
||||
name := T.Obj()
|
||||
if f.seenTParamNames[name] {
|
||||
return nil
|
||||
}
|
||||
if name == f.obj {
|
||||
return append(path, opObj)
|
||||
}
|
||||
if f.seenTParamNames == nil {
|
||||
f.seenTParamNames = make(map[*types.TypeName]bool)
|
||||
}
|
||||
f.seenTParamNames[name] = true
|
||||
if r := f.find(T.Constraint(), append(path, opConstraint)); r != nil {
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
panic(T)
|
||||
}
|
||||
|
||||
func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, op byte) []byte {
|
||||
return (&finder{obj: obj}).findTypeParam(list, path, op)
|
||||
}
|
||||
|
||||
func (f *finder) findTypeParam(list *types.TypeParamList, path []byte, op byte) []byte {
|
||||
for i := 0; i < list.Len(); i++ {
|
||||
tparam := list.At(i)
|
||||
path2 := appendOpArg(path, op, i)
|
||||
if r := f.find(tparam, path2); 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() *types.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 [AFMTr] have an integer operand.
|
||||
var index int
|
||||
switch code {
|
||||
case opAt, opField, opMethod, opTypeParam, opRecvTypeParam:
|
||||
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
|
||||
|
||||
t = types.Unalias(t)
|
||||
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 opRhs:
|
||||
if alias, ok := t.(*types.Alias); ok {
|
||||
t = aliases.Rhs(alias)
|
||||
} else if false && aliases.Enabled() {
|
||||
// The Enabled check is too expensive, so for now we
|
||||
// simply assume that aliases are not enabled.
|
||||
// TODO(adonovan): replace with "if true {" when go1.24 is assured.
|
||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want alias)", code, t, t)
|
||||
}
|
||||
|
||||
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 opRecvTypeParam:
|
||||
sig, ok := t.(*types.Signature) // Signature
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
|
||||
}
|
||||
rtparams := sig.RecvTypeParams()
|
||||
if n := rtparams.Len(); index >= n {
|
||||
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
|
||||
}
|
||||
t = rtparams.At(index)
|
||||
|
||||
case opConstraint:
|
||||
tparam, ok := t.(*types.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 == nil {
|
||||
panic(p) // path does not end in an object-valued operator
|
||||
}
|
||||
|
||||
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
|
||||
}
|
68
vendor/golang.org/x/tools/go/types/typeutil/callee.go
generated
vendored
Normal file
68
vendor/golang.org/x/tools/go/types/typeutil/callee.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
// 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 typeutil
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
// Callee returns the named target of a function call, if any:
|
||||
// a function, method, builtin, or variable.
|
||||
//
|
||||
// Functions and methods may potentially have type parameters.
|
||||
func Callee(info *types.Info, call *ast.CallExpr) types.Object {
|
||||
fun := ast.Unparen(call.Fun)
|
||||
|
||||
// Look through type instantiation if necessary.
|
||||
isInstance := false
|
||||
switch fun.(type) {
|
||||
case *ast.IndexExpr, *ast.IndexListExpr:
|
||||
// When extracting the callee from an *IndexExpr, we need to check that
|
||||
// it is a *types.Func and not a *types.Var.
|
||||
// Example: Don't match a slice m within the expression `m[0]()`.
|
||||
isInstance = true
|
||||
fun, _, _, _ = typeparams.UnpackIndexExpr(fun)
|
||||
}
|
||||
|
||||
var obj types.Object
|
||||
switch fun := fun.(type) {
|
||||
case *ast.Ident:
|
||||
obj = info.Uses[fun] // type, var, builtin, or declared func
|
||||
case *ast.SelectorExpr:
|
||||
if sel, ok := info.Selections[fun]; ok {
|
||||
obj = sel.Obj() // method or field
|
||||
} else {
|
||||
obj = info.Uses[fun.Sel] // qualified identifier?
|
||||
}
|
||||
}
|
||||
if _, ok := obj.(*types.TypeName); ok {
|
||||
return nil // T(x) is a conversion, not a call
|
||||
}
|
||||
// A Func is required to match instantiations.
|
||||
if _, ok := obj.(*types.Func); isInstance && !ok {
|
||||
return nil // Was not a Func.
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
// StaticCallee returns the target (function or method) of a static function
|
||||
// call, if any. It returns nil for calls to builtins.
|
||||
//
|
||||
// Note: for calls of instantiated functions and methods, StaticCallee returns
|
||||
// the corresponding generic function or method on the generic type.
|
||||
func StaticCallee(info *types.Info, call *ast.CallExpr) *types.Func {
|
||||
if f, ok := Callee(info, call).(*types.Func); ok && !interfaceMethod(f) {
|
||||
return f
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func interfaceMethod(f *types.Func) bool {
|
||||
recv := f.Type().(*types.Signature).Recv()
|
||||
return recv != nil && types.IsInterface(recv.Type())
|
||||
}
|
30
vendor/golang.org/x/tools/go/types/typeutil/imports.go
generated
vendored
Normal file
30
vendor/golang.org/x/tools/go/types/typeutil/imports.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2014 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 typeutil
|
||||
|
||||
import "go/types"
|
||||
|
||||
// Dependencies returns all dependencies of the specified packages.
|
||||
//
|
||||
// Dependent packages appear in topological order: if package P imports
|
||||
// package Q, Q appears earlier than P in the result.
|
||||
// The algorithm follows import statements in the order they
|
||||
// appear in the source code, so the result is a total order.
|
||||
func Dependencies(pkgs ...*types.Package) []*types.Package {
|
||||
var result []*types.Package
|
||||
seen := make(map[*types.Package]bool)
|
||||
var visit func(pkgs []*types.Package)
|
||||
visit = func(pkgs []*types.Package) {
|
||||
for _, p := range pkgs {
|
||||
if !seen[p] {
|
||||
seen[p] = true
|
||||
visit(p.Imports())
|
||||
result = append(result, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(pkgs)
|
||||
return result
|
||||
}
|
517
vendor/golang.org/x/tools/go/types/typeutil/map.go
generated
vendored
Normal file
517
vendor/golang.org/x/tools/go/types/typeutil/map.go
generated
vendored
Normal file
@ -0,0 +1,517 @@
|
||||
// Copyright 2014 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 typeutil defines various utilities for types, such as Map,
|
||||
// a mapping from types.Type to any values.
|
||||
package typeutil // import "golang.org/x/tools/go/types/typeutil"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
// Map is a hash-table-based mapping from types (types.Type) to
|
||||
// arbitrary any values. The concrete types that implement
|
||||
// the Type interface are pointers. Since they are not canonicalized,
|
||||
// == cannot be used to check for equivalence, and thus we cannot
|
||||
// simply use a Go map.
|
||||
//
|
||||
// Just as with map[K]V, a nil *Map is a valid empty map.
|
||||
//
|
||||
// Not thread-safe.
|
||||
type Map struct {
|
||||
hasher Hasher // shared by many Maps
|
||||
table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused
|
||||
length int // number of map entries
|
||||
}
|
||||
|
||||
// entry is an entry (key/value association) in a hash bucket.
|
||||
type entry struct {
|
||||
key types.Type
|
||||
value any
|
||||
}
|
||||
|
||||
// SetHasher sets the hasher used by Map.
|
||||
//
|
||||
// All Hashers are functionally equivalent but contain internal state
|
||||
// used to cache the results of hashing previously seen types.
|
||||
//
|
||||
// A single Hasher created by MakeHasher() may be shared among many
|
||||
// Maps. This is recommended if the instances have many keys in
|
||||
// common, as it will amortize the cost of hash computation.
|
||||
//
|
||||
// A Hasher may grow without bound as new types are seen. Even when a
|
||||
// type is deleted from the map, the Hasher never shrinks, since other
|
||||
// types in the map may reference the deleted type indirectly.
|
||||
//
|
||||
// Hashers are not thread-safe, and read-only operations such as
|
||||
// Map.Lookup require updates to the hasher, so a full Mutex lock (not a
|
||||
// read-lock) is require around all Map operations if a shared
|
||||
// hasher is accessed from multiple threads.
|
||||
//
|
||||
// If SetHasher is not called, the Map will create a private hasher at
|
||||
// the first call to Insert.
|
||||
func (m *Map) SetHasher(hasher Hasher) {
|
||||
m.hasher = hasher
|
||||
}
|
||||
|
||||
// Delete removes the entry with the given key, if any.
|
||||
// It returns true if the entry was found.
|
||||
func (m *Map) Delete(key types.Type) bool {
|
||||
if m != nil && m.table != nil {
|
||||
hash := m.hasher.Hash(key)
|
||||
bucket := m.table[hash]
|
||||
for i, e := range bucket {
|
||||
if e.key != nil && types.Identical(key, e.key) {
|
||||
// We can't compact the bucket as it
|
||||
// would disturb iterators.
|
||||
bucket[i] = entry{}
|
||||
m.length--
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// At returns the map entry for the given key.
|
||||
// The result is nil if the entry is not present.
|
||||
func (m *Map) At(key types.Type) any {
|
||||
if m != nil && m.table != nil {
|
||||
for _, e := range m.table[m.hasher.Hash(key)] {
|
||||
if e.key != nil && types.Identical(key, e.key) {
|
||||
return e.value
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set sets the map entry for key to val,
|
||||
// and returns the previous entry, if any.
|
||||
func (m *Map) Set(key types.Type, value any) (prev any) {
|
||||
if m.table != nil {
|
||||
hash := m.hasher.Hash(key)
|
||||
bucket := m.table[hash]
|
||||
var hole *entry
|
||||
for i, e := range bucket {
|
||||
if e.key == nil {
|
||||
hole = &bucket[i]
|
||||
} else if types.Identical(key, e.key) {
|
||||
prev = e.value
|
||||
bucket[i].value = value
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if hole != nil {
|
||||
*hole = entry{key, value} // overwrite deleted entry
|
||||
} else {
|
||||
m.table[hash] = append(bucket, entry{key, value})
|
||||
}
|
||||
} else {
|
||||
if m.hasher.memo == nil {
|
||||
m.hasher = MakeHasher()
|
||||
}
|
||||
hash := m.hasher.Hash(key)
|
||||
m.table = map[uint32][]entry{hash: {entry{key, value}}}
|
||||
}
|
||||
|
||||
m.length++
|
||||
return
|
||||
}
|
||||
|
||||
// Len returns the number of map entries.
|
||||
func (m *Map) Len() int {
|
||||
if m != nil {
|
||||
return m.length
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Iterate calls function f on each entry in the map in unspecified order.
|
||||
//
|
||||
// If f should mutate the map, Iterate provides the same guarantees as
|
||||
// Go maps: if f deletes a map entry that Iterate has not yet reached,
|
||||
// f will not be invoked for it, but if f inserts a map entry that
|
||||
// Iterate has not yet reached, whether or not f will be invoked for
|
||||
// it is unspecified.
|
||||
func (m *Map) Iterate(f func(key types.Type, value any)) {
|
||||
if m != nil {
|
||||
for _, bucket := range m.table {
|
||||
for _, e := range bucket {
|
||||
if e.key != nil {
|
||||
f(e.key, e.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Keys returns a new slice containing the set of map keys.
|
||||
// The order is unspecified.
|
||||
func (m *Map) Keys() []types.Type {
|
||||
keys := make([]types.Type, 0, m.Len())
|
||||
m.Iterate(func(key types.Type, _ any) {
|
||||
keys = append(keys, key)
|
||||
})
|
||||
return keys
|
||||
}
|
||||
|
||||
func (m *Map) toString(values bool) string {
|
||||
if m == nil {
|
||||
return "{}"
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprint(&buf, "{")
|
||||
sep := ""
|
||||
m.Iterate(func(key types.Type, value any) {
|
||||
fmt.Fprint(&buf, sep)
|
||||
sep = ", "
|
||||
fmt.Fprint(&buf, key)
|
||||
if values {
|
||||
fmt.Fprintf(&buf, ": %q", value)
|
||||
}
|
||||
})
|
||||
fmt.Fprint(&buf, "}")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// String returns a string representation of the map's entries.
|
||||
// Values are printed using fmt.Sprintf("%v", v).
|
||||
// Order is unspecified.
|
||||
func (m *Map) String() string {
|
||||
return m.toString(true)
|
||||
}
|
||||
|
||||
// KeysString returns a string representation of the map's key set.
|
||||
// Order is unspecified.
|
||||
func (m *Map) KeysString() string {
|
||||
return m.toString(false)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Hasher
|
||||
|
||||
// A Hasher maps each type to its hash value.
|
||||
// For efficiency, a hasher uses memoization; thus its memory
|
||||
// footprint grows monotonically over time.
|
||||
// Hashers are not thread-safe.
|
||||
// Hashers have reference semantics.
|
||||
// Call MakeHasher to create a Hasher.
|
||||
type Hasher struct {
|
||||
memo map[types.Type]uint32
|
||||
|
||||
// ptrMap records pointer identity.
|
||||
ptrMap map[any]uint32
|
||||
|
||||
// sigTParams holds type parameters from the signature being hashed.
|
||||
// Signatures are considered identical modulo renaming of type parameters, so
|
||||
// within the scope of a signature type the identity of the signature's type
|
||||
// parameters is just their index.
|
||||
//
|
||||
// Since the language does not currently support referring to uninstantiated
|
||||
// generic types or functions, and instantiated signatures do not have type
|
||||
// parameter lists, we should never encounter a second non-empty type
|
||||
// parameter list when hashing a generic signature.
|
||||
sigTParams *types.TypeParamList
|
||||
}
|
||||
|
||||
// MakeHasher returns a new Hasher instance.
|
||||
func MakeHasher() Hasher {
|
||||
return Hasher{
|
||||
memo: make(map[types.Type]uint32),
|
||||
ptrMap: make(map[any]uint32),
|
||||
sigTParams: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// Hash computes a hash value for the given type t such that
|
||||
// Identical(t, t') => Hash(t) == Hash(t').
|
||||
func (h Hasher) Hash(t types.Type) uint32 {
|
||||
hash, ok := h.memo[t]
|
||||
if !ok {
|
||||
hash = h.hashFor(t)
|
||||
h.memo[t] = hash
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
// hashString computes the Fowler–Noll–Vo hash of s.
|
||||
func hashString(s string) uint32 {
|
||||
var h uint32
|
||||
for i := 0; i < len(s); i++ {
|
||||
h ^= uint32(s[i])
|
||||
h *= 16777619
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// hashFor computes the hash of t.
|
||||
func (h Hasher) hashFor(t types.Type) uint32 {
|
||||
// See Identical for rationale.
|
||||
switch t := t.(type) {
|
||||
case *types.Basic:
|
||||
return uint32(t.Kind())
|
||||
|
||||
case *types.Alias:
|
||||
return h.Hash(types.Unalias(t))
|
||||
|
||||
case *types.Array:
|
||||
return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem())
|
||||
|
||||
case *types.Slice:
|
||||
return 9049 + 2*h.Hash(t.Elem())
|
||||
|
||||
case *types.Struct:
|
||||
var hash uint32 = 9059
|
||||
for i, n := 0, t.NumFields(); i < n; i++ {
|
||||
f := t.Field(i)
|
||||
if f.Anonymous() {
|
||||
hash += 8861
|
||||
}
|
||||
hash += hashString(t.Tag(i))
|
||||
hash += hashString(f.Name()) // (ignore f.Pkg)
|
||||
hash += h.Hash(f.Type())
|
||||
}
|
||||
return hash
|
||||
|
||||
case *types.Pointer:
|
||||
return 9067 + 2*h.Hash(t.Elem())
|
||||
|
||||
case *types.Signature:
|
||||
var hash uint32 = 9091
|
||||
if t.Variadic() {
|
||||
hash *= 8863
|
||||
}
|
||||
|
||||
// Use a separate hasher for types inside of the signature, where type
|
||||
// parameter identity is modified to be (index, constraint). We must use a
|
||||
// new memo for this hasher as type identity may be affected by this
|
||||
// masking. For example, in func[T any](*T), the identity of *T depends on
|
||||
// whether we are mapping the argument in isolation, or recursively as part
|
||||
// of hashing the signature.
|
||||
//
|
||||
// We should never encounter a generic signature while hashing another
|
||||
// generic signature, but defensively set sigTParams only if h.mask is
|
||||
// unset.
|
||||
tparams := t.TypeParams()
|
||||
if h.sigTParams == nil && tparams.Len() != 0 {
|
||||
h = Hasher{
|
||||
// There may be something more efficient than discarding the existing
|
||||
// memo, but it would require detecting whether types are 'tainted' by
|
||||
// references to type parameters.
|
||||
memo: make(map[types.Type]uint32),
|
||||
// Re-using ptrMap ensures that pointer identity is preserved in this
|
||||
// hasher.
|
||||
ptrMap: h.ptrMap,
|
||||
sigTParams: tparams,
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < tparams.Len(); i++ {
|
||||
tparam := tparams.At(i)
|
||||
hash += 7 * h.Hash(tparam.Constraint())
|
||||
}
|
||||
|
||||
return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
|
||||
|
||||
case *types.Union:
|
||||
return h.hashUnion(t)
|
||||
|
||||
case *types.Interface:
|
||||
// Interfaces are identical if they have the same set of methods, with
|
||||
// identical names and types, and they have the same set of type
|
||||
// restrictions. See go/types.identical for more details.
|
||||
var hash uint32 = 9103
|
||||
|
||||
// Hash methods.
|
||||
for i, n := 0, t.NumMethods(); i < n; i++ {
|
||||
// Method order is not significant.
|
||||
// Ignore m.Pkg().
|
||||
m := t.Method(i)
|
||||
// Use shallow hash on method signature to
|
||||
// avoid anonymous interface cycles.
|
||||
hash += 3*hashString(m.Name()) + 5*h.shallowHash(m.Type())
|
||||
}
|
||||
|
||||
// Hash type restrictions.
|
||||
terms, err := typeparams.InterfaceTermSet(t)
|
||||
// if err != nil t has invalid type restrictions.
|
||||
if err == nil {
|
||||
hash += h.hashTermSet(terms)
|
||||
}
|
||||
|
||||
return hash
|
||||
|
||||
case *types.Map:
|
||||
return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem())
|
||||
|
||||
case *types.Chan:
|
||||
return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem())
|
||||
|
||||
case *types.Named:
|
||||
hash := h.hashPtr(t.Obj())
|
||||
targs := t.TypeArgs()
|
||||
for i := 0; i < targs.Len(); i++ {
|
||||
targ := targs.At(i)
|
||||
hash += 2 * h.Hash(targ)
|
||||
}
|
||||
return hash
|
||||
|
||||
case *types.TypeParam:
|
||||
return h.hashTypeParam(t)
|
||||
|
||||
case *types.Tuple:
|
||||
return h.hashTuple(t)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("%T: %v", t, t))
|
||||
}
|
||||
|
||||
func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
|
||||
// See go/types.identicalTypes for rationale.
|
||||
n := tuple.Len()
|
||||
hash := 9137 + 2*uint32(n)
|
||||
for i := 0; i < n; i++ {
|
||||
hash += 3 * h.Hash(tuple.At(i).Type())
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
func (h Hasher) hashUnion(t *types.Union) uint32 {
|
||||
// Hash type restrictions.
|
||||
terms, err := typeparams.UnionTermSet(t)
|
||||
// if err != nil t has invalid type restrictions. Fall back on a non-zero
|
||||
// hash.
|
||||
if err != nil {
|
||||
return 9151
|
||||
}
|
||||
return h.hashTermSet(terms)
|
||||
}
|
||||
|
||||
func (h Hasher) hashTermSet(terms []*types.Term) uint32 {
|
||||
hash := 9157 + 2*uint32(len(terms))
|
||||
for _, term := range terms {
|
||||
// term order is not significant.
|
||||
termHash := h.Hash(term.Type())
|
||||
if term.Tilde() {
|
||||
termHash *= 9161
|
||||
}
|
||||
hash += 3 * termHash
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
// hashTypeParam returns a hash of the type parameter t, with a hash value
|
||||
// depending on whether t is contained in h.sigTParams.
|
||||
//
|
||||
// If h.sigTParams is set and contains t, then we are in the process of hashing
|
||||
// a signature, and the hash value of t must depend only on t's index and
|
||||
// constraint: signatures are considered identical modulo type parameter
|
||||
// renaming. To avoid infinite recursion, we only hash the type parameter
|
||||
// index, and rely on types.Identical to handle signatures where constraints
|
||||
// are not identical.
|
||||
//
|
||||
// Otherwise the hash of t depends only on t's pointer identity.
|
||||
func (h Hasher) hashTypeParam(t *types.TypeParam) uint32 {
|
||||
if h.sigTParams != nil {
|
||||
i := t.Index()
|
||||
if i >= 0 && i < h.sigTParams.Len() && t == h.sigTParams.At(i) {
|
||||
return 9173 + 3*uint32(i)
|
||||
}
|
||||
}
|
||||
return h.hashPtr(t.Obj())
|
||||
}
|
||||
|
||||
// hashPtr hashes the pointer identity of ptr. It uses h.ptrMap to ensure that
|
||||
// pointers values are not dependent on the GC.
|
||||
func (h Hasher) hashPtr(ptr any) uint32 {
|
||||
if hash, ok := h.ptrMap[ptr]; ok {
|
||||
return hash
|
||||
}
|
||||
hash := uint32(reflect.ValueOf(ptr).Pointer())
|
||||
h.ptrMap[ptr] = hash
|
||||
return hash
|
||||
}
|
||||
|
||||
// shallowHash computes a hash of t without looking at any of its
|
||||
// element Types, to avoid potential anonymous cycles in the types of
|
||||
// interface methods.
|
||||
//
|
||||
// When an unnamed non-empty interface type appears anywhere among the
|
||||
// arguments or results of an interface method, there is a potential
|
||||
// for endless recursion. Consider:
|
||||
//
|
||||
// type X interface { m() []*interface { X } }
|
||||
//
|
||||
// The problem is that the Methods of the interface in m's result type
|
||||
// include m itself; there is no mention of the named type X that
|
||||
// might help us break the cycle.
|
||||
// (See comment in go/types.identical, case *Interface, for more.)
|
||||
func (h Hasher) shallowHash(t types.Type) uint32 {
|
||||
// t is the type of an interface method (Signature),
|
||||
// its params or results (Tuples), or their immediate
|
||||
// elements (mostly Slice, Pointer, Basic, Named),
|
||||
// so there's no need to optimize anything else.
|
||||
switch t := t.(type) {
|
||||
case *types.Alias:
|
||||
return h.shallowHash(types.Unalias(t))
|
||||
|
||||
case *types.Signature:
|
||||
var hash uint32 = 604171
|
||||
if t.Variadic() {
|
||||
hash *= 971767
|
||||
}
|
||||
// The Signature/Tuple recursion is always finite
|
||||
// and invariably shallow.
|
||||
return hash + 1062599*h.shallowHash(t.Params()) + 1282529*h.shallowHash(t.Results())
|
||||
|
||||
case *types.Tuple:
|
||||
n := t.Len()
|
||||
hash := 9137 + 2*uint32(n)
|
||||
for i := 0; i < n; i++ {
|
||||
hash += 53471161 * h.shallowHash(t.At(i).Type())
|
||||
}
|
||||
return hash
|
||||
|
||||
case *types.Basic:
|
||||
return 45212177 * uint32(t.Kind())
|
||||
|
||||
case *types.Array:
|
||||
return 1524181 + 2*uint32(t.Len())
|
||||
|
||||
case *types.Slice:
|
||||
return 2690201
|
||||
|
||||
case *types.Struct:
|
||||
return 3326489
|
||||
|
||||
case *types.Pointer:
|
||||
return 4393139
|
||||
|
||||
case *types.Union:
|
||||
return 562448657
|
||||
|
||||
case *types.Interface:
|
||||
return 2124679 // no recursion here
|
||||
|
||||
case *types.Map:
|
||||
return 9109
|
||||
|
||||
case *types.Chan:
|
||||
return 9127
|
||||
|
||||
case *types.Named:
|
||||
return h.hashPtr(t.Obj())
|
||||
|
||||
case *types.TypeParam:
|
||||
return h.hashPtr(t.Obj())
|
||||
}
|
||||
panic(fmt.Sprintf("shallowHash: %T: %v", t, t))
|
||||
}
|
71
vendor/golang.org/x/tools/go/types/typeutil/methodsetcache.go
generated
vendored
Normal file
71
vendor/golang.org/x/tools/go/types/typeutil/methodsetcache.go
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright 2014 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 file implements a cache of method sets.
|
||||
|
||||
package typeutil
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A MethodSetCache records the method set of each type T for which
|
||||
// MethodSet(T) is called so that repeat queries are fast.
|
||||
// The zero value is a ready-to-use cache instance.
|
||||
type MethodSetCache struct {
|
||||
mu sync.Mutex
|
||||
named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N
|
||||
others map[types.Type]*types.MethodSet // all other types
|
||||
}
|
||||
|
||||
// MethodSet returns the method set of type T. It is thread-safe.
|
||||
//
|
||||
// If cache is nil, this function is equivalent to types.NewMethodSet(T).
|
||||
// Utility functions can thus expose an optional *MethodSetCache
|
||||
// parameter to clients that care about performance.
|
||||
func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet {
|
||||
if cache == nil {
|
||||
return types.NewMethodSet(T)
|
||||
}
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
|
||||
switch T := types.Unalias(T).(type) {
|
||||
case *types.Named:
|
||||
return cache.lookupNamed(T).value
|
||||
|
||||
case *types.Pointer:
|
||||
if N, ok := types.Unalias(T.Elem()).(*types.Named); ok {
|
||||
return cache.lookupNamed(N).pointer
|
||||
}
|
||||
}
|
||||
|
||||
// all other types
|
||||
// (The map uses pointer equivalence, not type identity.)
|
||||
mset := cache.others[T]
|
||||
if mset == nil {
|
||||
mset = types.NewMethodSet(T)
|
||||
if cache.others == nil {
|
||||
cache.others = make(map[types.Type]*types.MethodSet)
|
||||
}
|
||||
cache.others[T] = mset
|
||||
}
|
||||
return mset
|
||||
}
|
||||
|
||||
func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } {
|
||||
if cache.named == nil {
|
||||
cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet })
|
||||
}
|
||||
// Avoid recomputing mset(*T) for each distinct Pointer
|
||||
// instance whose underlying type is a named type.
|
||||
msets, ok := cache.named[named]
|
||||
if !ok {
|
||||
msets.value = types.NewMethodSet(named)
|
||||
msets.pointer = types.NewMethodSet(types.NewPointer(named))
|
||||
cache.named[named] = msets
|
||||
}
|
||||
return msets
|
||||
}
|
53
vendor/golang.org/x/tools/go/types/typeutil/ui.go
generated
vendored
Normal file
53
vendor/golang.org/x/tools/go/types/typeutil/ui.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2014 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 typeutil
|
||||
|
||||
// This file defines utilities for user interfaces that display types.
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
)
|
||||
|
||||
// IntuitiveMethodSet returns the intuitive method set of a type T,
|
||||
// which is the set of methods you can call on an addressable value of
|
||||
// that type.
|
||||
//
|
||||
// The result always contains MethodSet(T), and is exactly MethodSet(T)
|
||||
// for interface types and for pointer-to-concrete types.
|
||||
// For all other concrete types T, the result additionally
|
||||
// contains each method belonging to *T if there is no identically
|
||||
// named method on T itself.
|
||||
//
|
||||
// This corresponds to user intuition about method sets;
|
||||
// this function is intended only for user interfaces.
|
||||
//
|
||||
// The order of the result is as for types.MethodSet(T).
|
||||
func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection {
|
||||
isPointerToConcrete := func(T types.Type) bool {
|
||||
ptr, ok := types.Unalias(T).(*types.Pointer)
|
||||
return ok && !types.IsInterface(ptr.Elem())
|
||||
}
|
||||
|
||||
var result []*types.Selection
|
||||
mset := msets.MethodSet(T)
|
||||
if types.IsInterface(T) || isPointerToConcrete(T) {
|
||||
for i, n := 0, mset.Len(); i < n; i++ {
|
||||
result = append(result, mset.At(i))
|
||||
}
|
||||
} else {
|
||||
// T is some other concrete type.
|
||||
// Report methods of T and *T, preferring those of T.
|
||||
pmset := msets.MethodSet(types.NewPointer(T))
|
||||
for i, n := 0, pmset.Len(); i < n; i++ {
|
||||
meth := pmset.At(i)
|
||||
if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil {
|
||||
meth = m
|
||||
}
|
||||
result = append(result, meth)
|
||||
}
|
||||
|
||||
}
|
||||
return result
|
||||
}
|
Reference in New Issue
Block a user