Fix lint errs (#59)
This commit is contained in:
241
vendor/golang.org/x/tools/go/packages/doc.go
generated
vendored
Normal file
241
vendor/golang.org/x/tools/go/packages/doc.go
generated
vendored
Normal file
@ -0,0 +1,241 @@
|
||||
// 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.
|
||||
|
||||
Note: Though this package is ready for widespread use, we may make minor
|
||||
breaking changes if absolutely necessary. Any such change will be
|
||||
announced on golang-tools@ at least one week before it is committed. No
|
||||
more breaking changes will be made after December 1, 2018.
|
||||
|
||||
The Load function takes as input a list of patterns and return a list of Package
|
||||
structs describing individual packages matched by those patterns.
|
||||
The LoadMode controls the amount of detail in the loaded packages.
|
||||
|
||||
Load passes most patterns directly to the underlying build tool,
|
||||
but 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.
|
||||
|
||||
Only 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 returns 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 '='.
|
||||
|
||||
A third query "name=identifier" will be added soon.
|
||||
It will match packages whose package declaration contains the specified identifier.
|
||||
For example, "name=rand" would match the packages "math/rand" and "crypto/rand",
|
||||
and "name=main" would match all executables.
|
||||
|
||||
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
|
||||
- TypeInfo, 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, with each mode returning all the data of the
|
||||
previous mode with some extra added. See the documentation for type LoadMode
|
||||
for details.
|
||||
|
||||
Most tools should pass their command-line arguments (after any flags)
|
||||
uninterpreted to the loader, so that the loader can interpret them
|
||||
according to the conventions of the underlying build system.
|
||||
See the Example function for typical usage.
|
||||
|
||||
*/
|
||||
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.
|
||||
|
||||
Overlays: the ParseFile hook in the API permits clients to vary the way
|
||||
in which ASTs are obtained from filenames; the default implementation is
|
||||
based on parser.ParseFile. This features enables editor-integrated tools
|
||||
that analyze the contents of modified but unsaved buffers: rather than
|
||||
read from the file system, a tool can read from an archive of modified
|
||||
buffers provided by the editor.
|
||||
This approach has its limits. Because package metadata is obtained by
|
||||
fork/execing an external query command for each build system, we can
|
||||
fake only the file contents seen by the parser, type-checker, and
|
||||
application, but not by the metadata query, so, for example:
|
||||
- additional imports in the fake file will not be described by the
|
||||
metadata, so the type checker will fail to load imports that create
|
||||
new dependencies.
|
||||
- in TypeCheck mode, because export data is produced by the query
|
||||
command, it will not reflect the fake file contents.
|
||||
- this mechanism cannot add files to a package without first saving them.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
68
vendor/golang.org/x/tools/go/packages/external.go
generated
vendored
Normal file
68
vendor/golang.org/x/tools/go/packages/external.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.
|
||||
|
||||
// This file enables an external tool to intercept package requests.
|
||||
// If the tool is present then its results are used in preference to
|
||||
// the go list command.
|
||||
|
||||
package packages
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// findExternalTool 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, words ...string) (*driverResponse, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
fullargs := []string{
|
||||
"list",
|
||||
fmt.Sprintf("-test=%t", cfg.Tests),
|
||||
fmt.Sprintf("-export=%t", usesExportData(cfg)),
|
||||
fmt.Sprintf("-deps=%t", cfg.Mode >= LoadImports),
|
||||
}
|
||||
for _, f := range cfg.BuildFlags {
|
||||
fullargs = append(fullargs, fmt.Sprintf("-buildflag=%v", f))
|
||||
}
|
||||
fullargs = append(fullargs, "--")
|
||||
fullargs = append(fullargs, words...)
|
||||
cmd := exec.CommandContext(cfg.Context, tool, fullargs...)
|
||||
cmd.Env = cfg.Env
|
||||
cmd.Dir = cfg.Dir
|
||||
cmd.Stdout = buf
|
||||
cmd.Stderr = new(bytes.Buffer)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr)
|
||||
}
|
||||
var response driverResponse
|
||||
if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
}
|
625
vendor/golang.org/x/tools/go/packages/golist.go
generated
vendored
Normal file
625
vendor/golang.org/x/tools/go/packages/golist.go
generated
vendored
Normal file
@ -0,0 +1,625 @@
|
||||
// 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 (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/tools/internal/gopathwalk"
|
||||
"golang.org/x/tools/internal/semver"
|
||||
)
|
||||
|
||||
// A goTooOldError reports that the go command
|
||||
// found by exec.LookPath is too old to use the new go list behavior.
|
||||
type goTooOldError struct {
|
||||
error
|
||||
}
|
||||
|
||||
// goListDriver uses the go list command to interpret the patterns and produce
|
||||
// the build system package structure.
|
||||
// See driver for more details.
|
||||
func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
|
||||
// Determine files requested in contains patterns
|
||||
var containFiles []string
|
||||
var packagesNamed []string
|
||||
restPatterns := make([]string, 0, len(patterns))
|
||||
// Extract file= and other [querytype]= patterns. Report an error if querytype
|
||||
// doesn't exist.
|
||||
extractQueries:
|
||||
for _, pattern := range patterns {
|
||||
eqidx := strings.Index(pattern, "=")
|
||||
if eqidx < 0 {
|
||||
restPatterns = append(restPatterns, pattern)
|
||||
} else {
|
||||
query, value := pattern[:eqidx], pattern[eqidx+len("="):]
|
||||
switch query {
|
||||
case "file":
|
||||
containFiles = append(containFiles, value)
|
||||
case "pattern":
|
||||
restPatterns = append(restPatterns, value)
|
||||
case "name":
|
||||
packagesNamed = append(packagesNamed, value)
|
||||
case "": // not a reserved query
|
||||
restPatterns = append(restPatterns, pattern)
|
||||
default:
|
||||
for _, rune := range query {
|
||||
if rune < 'a' || rune > 'z' { // not a reserved query
|
||||
restPatterns = append(restPatterns, pattern)
|
||||
continue extractQueries
|
||||
}
|
||||
}
|
||||
// Reject all other patterns containing "="
|
||||
return nil, fmt.Errorf("invalid query type %q in query pattern %q", query, pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
patterns = restPatterns
|
||||
// Look for the deprecated contains: syntax.
|
||||
// TODO(matloob): delete this around mid-October 2018.
|
||||
restPatterns = restPatterns[:0]
|
||||
for _, pattern := range patterns {
|
||||
if strings.HasPrefix(pattern, "contains:") {
|
||||
containFile := strings.TrimPrefix(pattern, "contains:")
|
||||
containFiles = append(containFiles, containFile)
|
||||
} else {
|
||||
restPatterns = append(restPatterns, pattern)
|
||||
}
|
||||
}
|
||||
containFiles = absJoin(cfg.Dir, containFiles)
|
||||
|
||||
// TODO(matloob): Remove the definition of listfunc and just use golistPackages once go1.12 is released.
|
||||
var listfunc driver
|
||||
listfunc = func(cfg *Config, words ...string) (*driverResponse, error) {
|
||||
response, err := golistDriverCurrent(cfg, words...)
|
||||
if _, ok := err.(goTooOldError); ok {
|
||||
listfunc = golistDriverFallback
|
||||
return listfunc(cfg, words...)
|
||||
}
|
||||
listfunc = golistDriverCurrent
|
||||
return response, err
|
||||
}
|
||||
|
||||
var response *driverResponse
|
||||
var err error
|
||||
|
||||
// see if we have any patterns to pass through to go list.
|
||||
if len(restPatterns) > 0 {
|
||||
response, err = listfunc(cfg, restPatterns...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
response = &driverResponse{}
|
||||
}
|
||||
|
||||
if len(containFiles) == 0 && len(packagesNamed) == 0 {
|
||||
return response, nil
|
||||
}
|
||||
|
||||
seenPkgs := make(map[string]*Package) // for deduplication. different containing queries could produce same packages
|
||||
for _, pkg := range response.Packages {
|
||||
seenPkgs[pkg.ID] = pkg
|
||||
}
|
||||
addPkg := func(p *Package) {
|
||||
if _, ok := seenPkgs[p.ID]; ok {
|
||||
return
|
||||
}
|
||||
seenPkgs[p.ID] = p
|
||||
response.Packages = append(response.Packages, p)
|
||||
}
|
||||
|
||||
containsResults, err := runContainsQueries(cfg, listfunc, addPkg, containFiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.Roots = append(response.Roots, containsResults...)
|
||||
|
||||
namedResults, err := runNamedQueries(cfg, listfunc, addPkg, packagesNamed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.Roots = append(response.Roots, namedResults...)
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func runContainsQueries(cfg *Config, driver driver, addPkg func(*Package), queries []string) ([]string, error) {
|
||||
var results []string
|
||||
for _, query := range queries {
|
||||
// TODO(matloob): Do only one query per directory.
|
||||
fdir := filepath.Dir(query)
|
||||
cfg.Dir = fdir
|
||||
dirResponse, err := driver(cfg, ".")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
isRoot := make(map[string]bool, len(dirResponse.Roots))
|
||||
for _, root := range dirResponse.Roots {
|
||||
isRoot[root] = true
|
||||
}
|
||||
for _, pkg := range dirResponse.Packages {
|
||||
// Add any new packages to the main set
|
||||
// We don't bother to filter packages that will be dropped by the changes of roots,
|
||||
// that will happen anyway during graph construction outside this function.
|
||||
// Over-reporting packages is not a problem.
|
||||
addPkg(pkg)
|
||||
// if the package was not a root one, it cannot have the file
|
||||
if !isRoot[pkg.ID] {
|
||||
continue
|
||||
}
|
||||
for _, pkgFile := range pkg.GoFiles {
|
||||
if filepath.Base(query) == filepath.Base(pkgFile) {
|
||||
results = append(results, pkg.ID)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// modCacheRegexp splits a path in a module cache into module, module version, and package.
|
||||
var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`)
|
||||
|
||||
func runNamedQueries(cfg *Config, driver driver, addPkg func(*Package), queries []string) ([]string, error) {
|
||||
// Determine which directories are relevant to scan.
|
||||
roots, modulesEnabled, err := roots(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Scan the selected directories. Simple matches, from GOPATH/GOROOT
|
||||
// or the local module, can simply be "go list"ed. Matches from the
|
||||
// module cache need special treatment.
|
||||
var matchesMu sync.Mutex
|
||||
var simpleMatches, modCacheMatches []string
|
||||
add := func(root gopathwalk.Root, dir string) {
|
||||
// Walk calls this concurrently; protect the result slices.
|
||||
matchesMu.Lock()
|
||||
defer matchesMu.Unlock()
|
||||
|
||||
path := dir[len(root.Path)+1:]
|
||||
if pathMatchesQueries(path, queries) {
|
||||
switch root.Type {
|
||||
case gopathwalk.RootModuleCache:
|
||||
modCacheMatches = append(modCacheMatches, path)
|
||||
case gopathwalk.RootCurrentModule:
|
||||
// We'd need to read go.mod to find the full
|
||||
// import path. Relative's easier.
|
||||
rel, err := filepath.Rel(cfg.Dir, dir)
|
||||
if err != nil {
|
||||
// This ought to be impossible, since
|
||||
// we found dir in the current module.
|
||||
panic(err)
|
||||
}
|
||||
simpleMatches = append(simpleMatches, "./"+rel)
|
||||
case gopathwalk.RootGOPATH, gopathwalk.RootGOROOT:
|
||||
simpleMatches = append(simpleMatches, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
gopathwalk.Walk(roots, add, gopathwalk.Options{ModulesEnabled: modulesEnabled})
|
||||
|
||||
var results []string
|
||||
addResponse := func(r *driverResponse) {
|
||||
for _, pkg := range r.Packages {
|
||||
addPkg(pkg)
|
||||
for _, name := range queries {
|
||||
if pkg.Name == name {
|
||||
results = append(results, pkg.ID)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(simpleMatches) != 0 {
|
||||
resp, err := driver(cfg, simpleMatches...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addResponse(resp)
|
||||
}
|
||||
|
||||
// Module cache matches are tricky. We want to avoid downloading new
|
||||
// versions of things, so we need to use the ones present in the cache.
|
||||
// go list doesn't accept version specifiers, so we have to write out a
|
||||
// temporary module, and do the list in that module.
|
||||
if len(modCacheMatches) != 0 {
|
||||
// Collect all the matches, deduplicating by major version
|
||||
// and preferring the newest.
|
||||
type modInfo struct {
|
||||
mod string
|
||||
major string
|
||||
}
|
||||
mods := make(map[modInfo]string)
|
||||
var imports []string
|
||||
for _, modPath := range modCacheMatches {
|
||||
matches := modCacheRegexp.FindStringSubmatch(modPath)
|
||||
mod, ver := filepath.ToSlash(matches[1]), matches[2]
|
||||
importPath := filepath.ToSlash(filepath.Join(matches[1], matches[3]))
|
||||
|
||||
major := semver.Major(ver)
|
||||
if prevVer, ok := mods[modInfo{mod, major}]; !ok || semver.Compare(ver, prevVer) > 0 {
|
||||
mods[modInfo{mod, major}] = ver
|
||||
}
|
||||
|
||||
imports = append(imports, importPath)
|
||||
}
|
||||
|
||||
// Build the temporary module.
|
||||
var gomod bytes.Buffer
|
||||
gomod.WriteString("module modquery\nrequire (\n")
|
||||
for mod, version := range mods {
|
||||
gomod.WriteString("\t" + mod.mod + " " + version + "\n")
|
||||
}
|
||||
gomod.WriteString(")\n")
|
||||
|
||||
tmpCfg := *cfg
|
||||
var err error
|
||||
tmpCfg.Dir, err = ioutil.TempDir("", "gopackages-modquery")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer os.RemoveAll(tmpCfg.Dir)
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(tmpCfg.Dir, "go.mod"), gomod.Bytes(), 0777); err != nil {
|
||||
return nil, fmt.Errorf("writing go.mod for module cache query: %v", err)
|
||||
}
|
||||
|
||||
// Run the query, using the import paths calculated from the matches above.
|
||||
resp, err := driver(&tmpCfg, imports...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("querying module cache matches: %v", err)
|
||||
}
|
||||
addResponse(resp)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// roots selects the appropriate paths to walk based on the passed-in configuration,
|
||||
// particularly the environment and the presence of a go.mod in cfg.Dir's parents.
|
||||
func roots(cfg *Config) ([]gopathwalk.Root, bool, error) {
|
||||
stdout, err := invokeGo(cfg, "env", "GOROOT", "GOPATH", "GOMOD")
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
fields := strings.Split(stdout.String(), "\n")
|
||||
if len(fields) != 4 || len(fields[3]) != 0 {
|
||||
return nil, false, fmt.Errorf("go env returned unexpected output: %q", stdout.String())
|
||||
}
|
||||
goroot, gopath, gomod := fields[0], filepath.SplitList(fields[1]), fields[2]
|
||||
modsEnabled := gomod != ""
|
||||
|
||||
var roots []gopathwalk.Root
|
||||
// Always add GOROOT.
|
||||
roots = append(roots, gopathwalk.Root{filepath.Join(goroot, "/src"), gopathwalk.RootGOROOT})
|
||||
// If modules are enabled, scan the module dir.
|
||||
if modsEnabled {
|
||||
roots = append(roots, gopathwalk.Root{filepath.Dir(gomod), gopathwalk.RootCurrentModule})
|
||||
}
|
||||
// Add either GOPATH/src or GOPATH/pkg/mod, depending on module mode.
|
||||
for _, p := range gopath {
|
||||
if modsEnabled {
|
||||
roots = append(roots, gopathwalk.Root{filepath.Join(p, "/pkg/mod"), gopathwalk.RootModuleCache})
|
||||
} else {
|
||||
roots = append(roots, gopathwalk.Root{filepath.Join(p, "/src"), gopathwalk.RootGOPATH})
|
||||
}
|
||||
}
|
||||
|
||||
return roots, modsEnabled, nil
|
||||
}
|
||||
|
||||
// These functions were copied from goimports. See further documentation there.
|
||||
|
||||
// pathMatchesQueries is adapted from pkgIsCandidate.
|
||||
// TODO: is it reasonable to do Contains here, rather than an exact match on a path component?
|
||||
func pathMatchesQueries(path string, queries []string) bool {
|
||||
lastTwo := lastTwoComponents(path)
|
||||
for _, query := range queries {
|
||||
if strings.Contains(lastTwo, query) {
|
||||
return true
|
||||
}
|
||||
if hasHyphenOrUpperASCII(lastTwo) && !hasHyphenOrUpperASCII(query) {
|
||||
lastTwo = lowerASCIIAndRemoveHyphen(lastTwo)
|
||||
if strings.Contains(lastTwo, query) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// lastTwoComponents returns at most the last two path components
|
||||
// of v, using either / or \ as the path separator.
|
||||
func lastTwoComponents(v string) string {
|
||||
nslash := 0
|
||||
for i := len(v) - 1; i >= 0; i-- {
|
||||
if v[i] == '/' || v[i] == '\\' {
|
||||
nslash++
|
||||
if nslash == 2 {
|
||||
return v[i:]
|
||||
}
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func hasHyphenOrUpperASCII(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
b := s[i]
|
||||
if b == '-' || ('A' <= b && b <= 'Z') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func lowerASCIIAndRemoveHyphen(s string) (ret string) {
|
||||
buf := make([]byte, 0, len(s))
|
||||
for i := 0; i < len(s); i++ {
|
||||
b := s[i]
|
||||
switch {
|
||||
case b == '-':
|
||||
continue
|
||||
case 'A' <= b && b <= 'Z':
|
||||
buf = append(buf, b+('a'-'A'))
|
||||
default:
|
||||
buf = append(buf, b)
|
||||
}
|
||||
}
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
// Fields must match go list;
|
||||
// see $GOROOT/src/cmd/go/internal/load/pkg.go.
|
||||
type jsonPackage struct {
|
||||
ImportPath string
|
||||
Dir string
|
||||
Name string
|
||||
Export string
|
||||
GoFiles []string
|
||||
CompiledGoFiles []string
|
||||
CFiles []string
|
||||
CgoFiles []string
|
||||
CXXFiles []string
|
||||
MFiles []string
|
||||
HFiles []string
|
||||
FFiles []string
|
||||
SFiles []string
|
||||
SwigFiles []string
|
||||
SwigCXXFiles []string
|
||||
SysoFiles []string
|
||||
Imports []string
|
||||
ImportMap map[string]string
|
||||
Deps []string
|
||||
TestGoFiles []string
|
||||
TestImports []string
|
||||
XTestGoFiles []string
|
||||
XTestImports []string
|
||||
ForTest string // q in a "p [q.test]" package, else ""
|
||||
DepOnly bool
|
||||
|
||||
Error *jsonPackageError
|
||||
}
|
||||
|
||||
type jsonPackageError struct {
|
||||
ImportStack []string
|
||||
Pos string
|
||||
Err string
|
||||
}
|
||||
|
||||
func otherFiles(p *jsonPackage) [][]string {
|
||||
return [][]string{p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.SwigFiles, p.SwigCXXFiles, p.SysoFiles}
|
||||
}
|
||||
|
||||
// golistDriverCurrent uses the "go list" command to expand the
|
||||
// pattern words and return metadata for the specified packages.
|
||||
// dir may be "" and env may be nil, as per os/exec.Command.
|
||||
func golistDriverCurrent(cfg *Config, words ...string) (*driverResponse, error) {
|
||||
// go list uses the following identifiers in ImportPath and Imports:
|
||||
//
|
||||
// "p" -- importable package or main (command)
|
||||
// "q.test" -- q's test executable
|
||||
// "p [q.test]" -- variant of p as built for q's test executable
|
||||
// "q_test [q.test]" -- q's external test package
|
||||
//
|
||||
// The packages p that are built differently for a test q.test
|
||||
// are q itself, plus any helpers used by the external test q_test,
|
||||
// typically including "testing" and all its dependencies.
|
||||
|
||||
// Run "go list" for complete
|
||||
// information on the specified packages.
|
||||
buf, err := invokeGo(cfg, golistargs(cfg, words)...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Decode the JSON and convert it to Package form.
|
||||
var response driverResponse
|
||||
for dec := json.NewDecoder(buf); dec.More(); {
|
||||
p := new(jsonPackage)
|
||||
if err := dec.Decode(p); err != nil {
|
||||
return nil, fmt.Errorf("JSON decoding failed: %v", err)
|
||||
}
|
||||
|
||||
if p.ImportPath == "" {
|
||||
// The documentation for go list says that “[e]rroneous packages will have
|
||||
// a non-empty ImportPath”. If for some reason it comes back empty, we
|
||||
// prefer to error out rather than silently discarding data or handing
|
||||
// back a package without any way to refer to it.
|
||||
if p.Error != nil {
|
||||
return nil, Error{
|
||||
Pos: p.Error.Pos,
|
||||
Msg: p.Error.Err,
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("package missing import path: %+v", p)
|
||||
}
|
||||
|
||||
pkg := &Package{
|
||||
Name: p.Name,
|
||||
ID: p.ImportPath,
|
||||
GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles),
|
||||
CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles),
|
||||
OtherFiles: absJoin(p.Dir, otherFiles(p)...),
|
||||
}
|
||||
|
||||
// Extract the PkgPath from the package's ID.
|
||||
if i := strings.IndexByte(pkg.ID, ' '); i >= 0 {
|
||||
pkg.PkgPath = pkg.ID[:i]
|
||||
} else {
|
||||
pkg.PkgPath = pkg.ID
|
||||
}
|
||||
|
||||
if pkg.PkgPath == "unsafe" {
|
||||
pkg.GoFiles = nil // ignore fake unsafe.go file
|
||||
}
|
||||
|
||||
// Assume go list emits only absolute paths for Dir.
|
||||
if p.Dir != "" && !filepath.IsAbs(p.Dir) {
|
||||
log.Fatalf("internal error: go list returned non-absolute Package.Dir: %s", p.Dir)
|
||||
}
|
||||
|
||||
if p.Export != "" && !filepath.IsAbs(p.Export) {
|
||||
pkg.ExportFile = filepath.Join(p.Dir, p.Export)
|
||||
} else {
|
||||
pkg.ExportFile = p.Export
|
||||
}
|
||||
|
||||
// imports
|
||||
//
|
||||
// Imports contains the IDs of all imported packages.
|
||||
// ImportsMap records (path, ID) only where they differ.
|
||||
ids := make(map[string]bool)
|
||||
for _, id := range p.Imports {
|
||||
ids[id] = true
|
||||
}
|
||||
pkg.Imports = make(map[string]*Package)
|
||||
for path, id := range p.ImportMap {
|
||||
pkg.Imports[path] = &Package{ID: id} // non-identity import
|
||||
delete(ids, id)
|
||||
}
|
||||
for id := range ids {
|
||||
if id == "C" {
|
||||
continue
|
||||
}
|
||||
|
||||
pkg.Imports[id] = &Package{ID: id} // identity import
|
||||
}
|
||||
if !p.DepOnly {
|
||||
response.Roots = append(response.Roots, pkg.ID)
|
||||
}
|
||||
|
||||
// TODO(matloob): Temporary hack since CompiledGoFiles isn't always set.
|
||||
if len(pkg.CompiledGoFiles) == 0 {
|
||||
pkg.CompiledGoFiles = pkg.GoFiles
|
||||
}
|
||||
|
||||
if p.Error != nil {
|
||||
pkg.Errors = append(pkg.Errors, Error{
|
||||
Pos: p.Error.Pos,
|
||||
Msg: p.Error.Err,
|
||||
})
|
||||
}
|
||||
|
||||
response.Packages = append(response.Packages, pkg)
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// absJoin absolutizes and flattens the lists of files.
|
||||
func absJoin(dir string, fileses ...[]string) (res []string) {
|
||||
for _, files := range fileses {
|
||||
for _, file := range files {
|
||||
if !filepath.IsAbs(file) {
|
||||
file = filepath.Join(dir, file)
|
||||
}
|
||||
res = append(res, file)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func golistargs(cfg *Config, words []string) []string {
|
||||
fullargs := []string{
|
||||
"list", "-e", "-json", "-compiled",
|
||||
fmt.Sprintf("-test=%t", cfg.Tests),
|
||||
fmt.Sprintf("-export=%t", usesExportData(cfg)),
|
||||
fmt.Sprintf("-deps=%t", cfg.Mode >= LoadImports),
|
||||
}
|
||||
fullargs = append(fullargs, cfg.BuildFlags...)
|
||||
fullargs = append(fullargs, "--")
|
||||
fullargs = append(fullargs, words...)
|
||||
return fullargs
|
||||
}
|
||||
|
||||
// invokeGo returns the stdout of a go command invocation.
|
||||
func invokeGo(cfg *Config, args ...string) (*bytes.Buffer, error) {
|
||||
stdout := new(bytes.Buffer)
|
||||
stderr := new(bytes.Buffer)
|
||||
cmd := exec.CommandContext(cfg.Context, "go", args...)
|
||||
// On darwin the cwd gets resolved to the real path, which breaks anything that
|
||||
// expects the working directory to keep the original path, including the
|
||||
// go command when dealing with modules.
|
||||
// The Go 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.
|
||||
cmd.Env = append(append([]string{}, cfg.Env...), "PWD="+cfg.Dir)
|
||||
cmd.Dir = cfg.Dir
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
exitErr, ok := err.(*exec.ExitError)
|
||||
if !ok {
|
||||
// Catastrophic error:
|
||||
// - executable not found
|
||||
// - context cancellation
|
||||
return nil, fmt.Errorf("couldn't exec 'go %v': %s %T", args, err, err)
|
||||
}
|
||||
|
||||
// Old go version?
|
||||
if strings.Contains(stderr.String(), "flag provided but not defined") {
|
||||
return nil, goTooOldError{fmt.Errorf("unsupported version of go: %s: %s", exitErr, stderr)}
|
||||
}
|
||||
|
||||
// Export mode entails a build.
|
||||
// If that build fails, errors appear on stderr
|
||||
// (despite the -e flag) and the Export field is blank.
|
||||
// Do not fail in that case.
|
||||
if !usesExportData(cfg) {
|
||||
return nil, fmt.Errorf("go %v: %s: %s", args, exitErr, stderr)
|
||||
}
|
||||
}
|
||||
|
||||
// As of writing, go list -export prints some non-fatal compilation
|
||||
// errors to stderr, even with -e set. We would prefer that it put
|
||||
// them in the Package.Error JSON (see http://golang.org/issue/26319).
|
||||
// In the meantime, there's nowhere good to put them, but they can
|
||||
// be useful for debugging. Print them if $GOPACKAGESPRINTGOLISTERRORS
|
||||
// is set.
|
||||
if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTGOLISTERRORS") != "" {
|
||||
fmt.Fprintf(os.Stderr, "go %v stderr: <<%s>>\n", args, stderr)
|
||||
}
|
||||
|
||||
// debugging
|
||||
if false {
|
||||
fmt.Fprintf(os.Stderr, "go %v stdout: <<%s>>\n", args, stdout)
|
||||
}
|
||||
|
||||
return stdout, nil
|
||||
}
|
457
vendor/golang.org/x/tools/go/packages/golist_fallback.go
generated
vendored
Normal file
457
vendor/golang.org/x/tools/go/packages/golist_fallback.go
generated
vendored
Normal file
@ -0,0 +1,457 @@
|
||||
// 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"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/internal/cgo"
|
||||
)
|
||||
|
||||
// TODO(matloob): Delete this file once Go 1.12 is released.
|
||||
|
||||
// This file provides backwards compatibility support for
|
||||
// loading for versions of Go earlier than 1.10.4. This support is meant to
|
||||
// assist with migration to the Package API until there's
|
||||
// widespread adoption of these newer Go versions.
|
||||
// This support will be removed once Go 1.12 is released
|
||||
// in Q1 2019.
|
||||
|
||||
func golistDriverFallback(cfg *Config, words ...string) (*driverResponse, error) {
|
||||
// Turn absolute paths into GOROOT and GOPATH-relative paths to provide to go list.
|
||||
// This will have surprising behavior if GOROOT or GOPATH contain multiple packages with the same
|
||||
// path and a user provides an absolute path to a directory that's shadowed by an earlier
|
||||
// directory in GOROOT or GOPATH with the same package path.
|
||||
words = cleanAbsPaths(cfg, words)
|
||||
|
||||
original, deps, err := getDeps(cfg, words...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var tmpdir string // used for generated cgo files
|
||||
var needsTestVariant []struct {
|
||||
pkg, xtestPkg *Package
|
||||
}
|
||||
|
||||
var response driverResponse
|
||||
allPkgs := make(map[string]bool)
|
||||
addPackage := func(p *jsonPackage) {
|
||||
id := p.ImportPath
|
||||
|
||||
if allPkgs[id] {
|
||||
return
|
||||
}
|
||||
allPkgs[id] = true
|
||||
|
||||
isRoot := original[id] != nil
|
||||
pkgpath := id
|
||||
|
||||
if pkgpath == "unsafe" {
|
||||
p.GoFiles = nil // ignore fake unsafe.go file
|
||||
}
|
||||
|
||||
importMap := func(importlist []string) map[string]*Package {
|
||||
importMap := make(map[string]*Package)
|
||||
for _, id := range importlist {
|
||||
|
||||
if id == "C" {
|
||||
for _, path := range []string{"unsafe", "syscall", "runtime/cgo"} {
|
||||
if pkgpath != path && importMap[path] == nil {
|
||||
importMap[path] = &Package{ID: path}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
importMap[vendorlessPath(id)] = &Package{ID: id}
|
||||
}
|
||||
return importMap
|
||||
}
|
||||
compiledGoFiles := absJoin(p.Dir, p.GoFiles)
|
||||
// Use a function to simplify control flow. It's just a bunch of gotos.
|
||||
var cgoErrors []error
|
||||
var outdir string
|
||||
getOutdir := func() (string, error) {
|
||||
if outdir != "" {
|
||||
return outdir, nil
|
||||
}
|
||||
if tmpdir == "" {
|
||||
if tmpdir, err = ioutil.TempDir("", "gopackages"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
// Add a "go-build" component to the path to make the tests think the files are in the cache.
|
||||
// This allows the same test to test the pre- and post-Go 1.11 go list logic because the Go 1.11
|
||||
// go list generates test mains in the cache, and the test code knows not to rely on paths in the
|
||||
// cache to stay stable.
|
||||
outdir = filepath.Join(tmpdir, "go-build", strings.Replace(p.ImportPath, "/", "_", -1))
|
||||
if err := os.MkdirAll(outdir, 0755); err != nil {
|
||||
outdir = ""
|
||||
return "", err
|
||||
}
|
||||
return outdir, nil
|
||||
}
|
||||
processCgo := func() bool {
|
||||
// Suppress any cgo errors. Any relevant errors will show up in typechecking.
|
||||
// TODO(matloob): Skip running cgo if Mode < LoadTypes.
|
||||
outdir, err := getOutdir()
|
||||
if err != nil {
|
||||
cgoErrors = append(cgoErrors, err)
|
||||
return false
|
||||
}
|
||||
files, _, err := runCgo(p.Dir, outdir, cfg.Env)
|
||||
if err != nil {
|
||||
cgoErrors = append(cgoErrors, err)
|
||||
return false
|
||||
}
|
||||
compiledGoFiles = append(compiledGoFiles, files...)
|
||||
return true
|
||||
}
|
||||
if len(p.CgoFiles) == 0 || !processCgo() {
|
||||
compiledGoFiles = append(compiledGoFiles, absJoin(p.Dir, p.CgoFiles)...) // Punt to typechecker.
|
||||
}
|
||||
if isRoot {
|
||||
response.Roots = append(response.Roots, id)
|
||||
}
|
||||
pkg := &Package{
|
||||
ID: id,
|
||||
Name: p.Name,
|
||||
GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles),
|
||||
CompiledGoFiles: compiledGoFiles,
|
||||
OtherFiles: absJoin(p.Dir, otherFiles(p)...),
|
||||
PkgPath: pkgpath,
|
||||
Imports: importMap(p.Imports),
|
||||
// TODO(matloob): set errors on the Package to cgoErrors
|
||||
}
|
||||
if p.Error != nil {
|
||||
pkg.Errors = append(pkg.Errors, Error{
|
||||
Pos: p.Error.Pos,
|
||||
Msg: p.Error.Err,
|
||||
})
|
||||
}
|
||||
response.Packages = append(response.Packages, pkg)
|
||||
if cfg.Tests && isRoot {
|
||||
testID := fmt.Sprintf("%s [%s.test]", id, id)
|
||||
if len(p.TestGoFiles) > 0 || len(p.XTestGoFiles) > 0 {
|
||||
response.Roots = append(response.Roots, testID)
|
||||
testPkg := &Package{
|
||||
ID: testID,
|
||||
Name: p.Name,
|
||||
GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles, p.TestGoFiles),
|
||||
CompiledGoFiles: append(compiledGoFiles, absJoin(p.Dir, p.TestGoFiles)...),
|
||||
OtherFiles: absJoin(p.Dir, otherFiles(p)...),
|
||||
PkgPath: pkgpath,
|
||||
Imports: importMap(append(p.Imports, p.TestImports...)),
|
||||
// TODO(matloob): set errors on the Package to cgoErrors
|
||||
}
|
||||
response.Packages = append(response.Packages, testPkg)
|
||||
var xtestPkg *Package
|
||||
if len(p.XTestGoFiles) > 0 {
|
||||
xtestID := fmt.Sprintf("%s_test [%s.test]", id, id)
|
||||
response.Roots = append(response.Roots, xtestID)
|
||||
// Generate test variants for all packages q where a path exists
|
||||
// such that xtestPkg -> ... -> q -> ... -> p (where p is the package under test)
|
||||
// and rewrite all import map entries of p to point to testPkg (the test variant of
|
||||
// p), and of each q to point to the test variant of that q.
|
||||
xtestPkg = &Package{
|
||||
ID: xtestID,
|
||||
Name: p.Name + "_test",
|
||||
GoFiles: absJoin(p.Dir, p.XTestGoFiles),
|
||||
CompiledGoFiles: absJoin(p.Dir, p.XTestGoFiles),
|
||||
PkgPath: pkgpath + "_test",
|
||||
Imports: importMap(p.XTestImports),
|
||||
}
|
||||
// Add to list of packages we need to rewrite imports for to refer to test variants.
|
||||
// We may need to create a test variant of a package that hasn't been loaded yet, so
|
||||
// the test variants need to be created later.
|
||||
needsTestVariant = append(needsTestVariant, struct{ pkg, xtestPkg *Package }{pkg, xtestPkg})
|
||||
response.Packages = append(response.Packages, xtestPkg)
|
||||
}
|
||||
// testmain package
|
||||
testmainID := id + ".test"
|
||||
response.Roots = append(response.Roots, testmainID)
|
||||
imports := map[string]*Package{}
|
||||
imports[testPkg.PkgPath] = &Package{ID: testPkg.ID}
|
||||
if xtestPkg != nil {
|
||||
imports[xtestPkg.PkgPath] = &Package{ID: xtestPkg.ID}
|
||||
}
|
||||
testmainPkg := &Package{
|
||||
ID: testmainID,
|
||||
Name: "main",
|
||||
PkgPath: testmainID,
|
||||
Imports: imports,
|
||||
}
|
||||
response.Packages = append(response.Packages, testmainPkg)
|
||||
outdir, err := getOutdir()
|
||||
if err != nil {
|
||||
testmainPkg.Errors = append(testmainPkg.Errors, Error{
|
||||
Pos: "-",
|
||||
Msg: fmt.Sprintf("failed to generate testmain: %v", err),
|
||||
Kind: ListError,
|
||||
})
|
||||
return
|
||||
}
|
||||
testmain := filepath.Join(outdir, "testmain.go")
|
||||
extraimports, extradeps, err := generateTestmain(testmain, testPkg, xtestPkg)
|
||||
if err != nil {
|
||||
testmainPkg.Errors = append(testmainPkg.Errors, Error{
|
||||
Pos: "-",
|
||||
Msg: fmt.Sprintf("failed to generate testmain: %v", err),
|
||||
Kind: ListError,
|
||||
})
|
||||
}
|
||||
deps = append(deps, extradeps...)
|
||||
for _, imp := range extraimports { // testing, testing/internal/testdeps, and maybe os
|
||||
imports[imp] = &Package{ID: imp}
|
||||
}
|
||||
testmainPkg.GoFiles = []string{testmain}
|
||||
testmainPkg.CompiledGoFiles = []string{testmain}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range original {
|
||||
addPackage(pkg)
|
||||
}
|
||||
if cfg.Mode < LoadImports || len(deps) == 0 {
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
buf, err := invokeGo(cfg, golistArgsFallback(cfg, deps)...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Decode the JSON and convert it to Package form.
|
||||
for dec := json.NewDecoder(buf); dec.More(); {
|
||||
p := new(jsonPackage)
|
||||
if err := dec.Decode(p); err != nil {
|
||||
return nil, fmt.Errorf("JSON decoding failed: %v", err)
|
||||
}
|
||||
|
||||
addPackage(p)
|
||||
}
|
||||
|
||||
for _, v := range needsTestVariant {
|
||||
createTestVariants(&response, v.pkg, v.xtestPkg)
|
||||
}
|
||||
|
||||
// TODO(matloob): Is this the right ordering?
|
||||
sort.SliceStable(response.Packages, func(i, j int) bool {
|
||||
return response.Packages[i].PkgPath < response.Packages[j].PkgPath
|
||||
})
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func createTestVariants(response *driverResponse, pkgUnderTest, xtestPkg *Package) {
|
||||
allPkgs := make(map[string]*Package)
|
||||
for _, pkg := range response.Packages {
|
||||
allPkgs[pkg.ID] = pkg
|
||||
}
|
||||
needsTestVariant := make(map[string]bool)
|
||||
needsTestVariant[pkgUnderTest.ID] = true
|
||||
var needsVariantRec func(p *Package) bool
|
||||
needsVariantRec = func(p *Package) bool {
|
||||
if needsTestVariant[p.ID] {
|
||||
return true
|
||||
}
|
||||
for _, imp := range p.Imports {
|
||||
if needsVariantRec(allPkgs[imp.ID]) {
|
||||
// Don't break because we want to make sure all dependencies
|
||||
// have been processed, and all required test variants of our dependencies
|
||||
// exist.
|
||||
needsTestVariant[p.ID] = true
|
||||
}
|
||||
}
|
||||
if !needsTestVariant[p.ID] {
|
||||
return false
|
||||
}
|
||||
// Create a clone of the package. It will share the same strings and lists of source files,
|
||||
// but that's okay. It's only necessary for the Imports map to have a separate identity.
|
||||
testVariant := *p
|
||||
testVariant.ID = fmt.Sprintf("%s [%s.test]", p.ID, pkgUnderTest.ID)
|
||||
testVariant.Imports = make(map[string]*Package)
|
||||
for imp, pkg := range p.Imports {
|
||||
testVariant.Imports[imp] = pkg
|
||||
if needsTestVariant[pkg.ID] {
|
||||
testVariant.Imports[imp] = &Package{ID: fmt.Sprintf("%s [%s.test]", pkg.ID, pkgUnderTest.ID)}
|
||||
}
|
||||
}
|
||||
response.Packages = append(response.Packages, &testVariant)
|
||||
return needsTestVariant[p.ID]
|
||||
}
|
||||
// finally, update the xtest package's imports
|
||||
for imp, pkg := range xtestPkg.Imports {
|
||||
if allPkgs[pkg.ID] == nil {
|
||||
fmt.Printf("for %s: package %s doesn't exist\n", xtestPkg.ID, pkg.ID)
|
||||
}
|
||||
if needsVariantRec(allPkgs[pkg.ID]) {
|
||||
xtestPkg.Imports[imp] = &Package{ID: fmt.Sprintf("%s [%s.test]", pkg.ID, pkgUnderTest.ID)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cleanAbsPaths replaces all absolute paths with GOPATH- and GOROOT-relative
|
||||
// paths. If an absolute path is not GOPATH- or GOROOT- relative, it is left as an
|
||||
// absolute path so an error can be returned later.
|
||||
func cleanAbsPaths(cfg *Config, words []string) []string {
|
||||
var searchpaths []string
|
||||
var cleaned = make([]string, len(words))
|
||||
for i := range cleaned {
|
||||
cleaned[i] = words[i]
|
||||
// Ignore relative directory paths (they must already be goroot-relative) and Go source files
|
||||
// (absolute source files are already allowed for ad-hoc packages).
|
||||
// TODO(matloob): Can there be non-.go files in ad-hoc packages.
|
||||
if !filepath.IsAbs(cleaned[i]) || strings.HasSuffix(cleaned[i], ".go") {
|
||||
continue
|
||||
}
|
||||
// otherwise, it's an absolute path. Search GOPATH and GOROOT to find it.
|
||||
if searchpaths == nil {
|
||||
cmd := exec.Command("go", "env", "GOPATH", "GOROOT")
|
||||
cmd.Env = cfg.Env
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
searchpaths = []string{}
|
||||
continue // suppress the error, it will show up again when running go list
|
||||
}
|
||||
lines := strings.Split(string(out), "\n")
|
||||
if len(lines) != 3 || lines[0] == "" || lines[1] == "" || lines[2] != "" {
|
||||
continue // suppress error
|
||||
}
|
||||
// first line is GOPATH
|
||||
for _, path := range filepath.SplitList(lines[0]) {
|
||||
searchpaths = append(searchpaths, filepath.Join(path, "src"))
|
||||
}
|
||||
// second line is GOROOT
|
||||
searchpaths = append(searchpaths, filepath.Join(lines[1], "src"))
|
||||
}
|
||||
for _, sp := range searchpaths {
|
||||
if strings.HasPrefix(cleaned[i], sp) {
|
||||
cleaned[i] = strings.TrimPrefix(cleaned[i], sp)
|
||||
cleaned[i] = strings.TrimLeft(cleaned[i], string(filepath.Separator))
|
||||
}
|
||||
}
|
||||
}
|
||||
return cleaned
|
||||
}
|
||||
|
||||
// vendorlessPath returns the devendorized version of the import path ipath.
|
||||
// For example, VendorlessPath("foo/bar/vendor/a/b") returns "a/b".
|
||||
// Copied from golang.org/x/tools/imports/fix.go.
|
||||
func vendorlessPath(ipath string) string {
|
||||
// Devendorize for use in import statement.
|
||||
if i := strings.LastIndex(ipath, "/vendor/"); i >= 0 {
|
||||
return ipath[i+len("/vendor/"):]
|
||||
}
|
||||
if strings.HasPrefix(ipath, "vendor/") {
|
||||
return ipath[len("vendor/"):]
|
||||
}
|
||||
return ipath
|
||||
}
|
||||
|
||||
// getDeps runs an initial go list to determine all the dependency packages.
|
||||
func getDeps(cfg *Config, words ...string) (originalSet map[string]*jsonPackage, deps []string, err error) {
|
||||
buf, err := invokeGo(cfg, golistArgsFallback(cfg, words)...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
depsSet := make(map[string]bool)
|
||||
originalSet = make(map[string]*jsonPackage)
|
||||
var testImports []string
|
||||
|
||||
// Extract deps from the JSON.
|
||||
for dec := json.NewDecoder(buf); dec.More(); {
|
||||
p := new(jsonPackage)
|
||||
if err := dec.Decode(p); err != nil {
|
||||
return nil, nil, fmt.Errorf("JSON decoding failed: %v", err)
|
||||
}
|
||||
|
||||
originalSet[p.ImportPath] = p
|
||||
for _, dep := range p.Deps {
|
||||
depsSet[dep] = true
|
||||
}
|
||||
if cfg.Tests {
|
||||
// collect the additional imports of the test packages.
|
||||
pkgTestImports := append(p.TestImports, p.XTestImports...)
|
||||
for _, imp := range pkgTestImports {
|
||||
if depsSet[imp] {
|
||||
continue
|
||||
}
|
||||
depsSet[imp] = true
|
||||
testImports = append(testImports, imp)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Get the deps of the packages imported by tests.
|
||||
if len(testImports) > 0 {
|
||||
buf, err = invokeGo(cfg, golistArgsFallback(cfg, testImports)...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Extract deps from the JSON.
|
||||
for dec := json.NewDecoder(buf); dec.More(); {
|
||||
p := new(jsonPackage)
|
||||
if err := dec.Decode(p); err != nil {
|
||||
return nil, nil, fmt.Errorf("JSON decoding failed: %v", err)
|
||||
}
|
||||
for _, dep := range p.Deps {
|
||||
depsSet[dep] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for orig := range originalSet {
|
||||
delete(depsSet, orig)
|
||||
}
|
||||
|
||||
deps = make([]string, 0, len(depsSet))
|
||||
for dep := range depsSet {
|
||||
deps = append(deps, dep)
|
||||
}
|
||||
sort.Strings(deps) // ensure output is deterministic
|
||||
return originalSet, deps, nil
|
||||
}
|
||||
|
||||
func golistArgsFallback(cfg *Config, words []string) []string {
|
||||
fullargs := []string{"list", "-e", "-json"}
|
||||
fullargs = append(fullargs, cfg.BuildFlags...)
|
||||
fullargs = append(fullargs, "--")
|
||||
fullargs = append(fullargs, words...)
|
||||
return fullargs
|
||||
}
|
||||
|
||||
func runCgo(pkgdir, tmpdir string, env []string) (files, displayfiles []string, err error) {
|
||||
// Use go/build to open cgo files and determine the cgo flags, etc, from them.
|
||||
// This is tricky so it's best to avoid reimplementing as much as we can, and
|
||||
// we plan to delete this support once Go 1.12 is released anyways.
|
||||
// TODO(matloob): This isn't completely correct because we're using the Default
|
||||
// context. Perhaps we should more accurately fill in the context.
|
||||
bp, err := build.ImportDir(pkgdir, build.ImportMode(0))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, ev := range env {
|
||||
if v := strings.TrimPrefix(ev, "CGO_CPPFLAGS"); v != ev {
|
||||
bp.CgoCPPFLAGS = append(bp.CgoCPPFLAGS, strings.Fields(v)...)
|
||||
} else if v := strings.TrimPrefix(ev, "CGO_CFLAGS"); v != ev {
|
||||
bp.CgoCFLAGS = append(bp.CgoCFLAGS, strings.Fields(v)...)
|
||||
} else if v := strings.TrimPrefix(ev, "CGO_CXXFLAGS"); v != ev {
|
||||
bp.CgoCXXFLAGS = append(bp.CgoCXXFLAGS, strings.Fields(v)...)
|
||||
} else if v := strings.TrimPrefix(ev, "CGO_LDFLAGS"); v != ev {
|
||||
bp.CgoLDFLAGS = append(bp.CgoLDFLAGS, strings.Fields(v)...)
|
||||
}
|
||||
}
|
||||
return cgo.Run(bp, pkgdir, tmpdir, true)
|
||||
}
|
318
vendor/golang.org/x/tools/go/packages/golist_fallback_testmain.go
generated
vendored
Normal file
318
vendor/golang.org/x/tools/go/packages/golist_fallback_testmain.go
generated
vendored
Normal file
@ -0,0 +1,318 @@
|
||||
// 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.
|
||||
|
||||
// This file is largely based on the Go 1.10-era cmd/go/internal/test/test.go
|
||||
// testmain generation code.
|
||||
|
||||
package packages
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/doc"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// TODO(matloob): Delete this file once Go 1.12 is released.
|
||||
|
||||
// This file complements golist_fallback.go by providing
|
||||
// support for generating testmains.
|
||||
|
||||
func generateTestmain(out string, testPkg, xtestPkg *Package) (extraimports, extradeps []string, err error) {
|
||||
testFuncs, err := loadTestFuncs(testPkg, xtestPkg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
extraimports = []string{"testing", "testing/internal/testdeps"}
|
||||
if testFuncs.TestMain == nil {
|
||||
extraimports = append(extraimports, "os")
|
||||
}
|
||||
// Transitive dependencies of ("testing", "testing/internal/testdeps").
|
||||
// os is part of the transitive closure so it and its transitive dependencies are
|
||||
// included regardless of whether it's imported in the template below.
|
||||
extradeps = []string{
|
||||
"errors",
|
||||
"internal/cpu",
|
||||
"unsafe",
|
||||
"internal/bytealg",
|
||||
"internal/race",
|
||||
"runtime/internal/atomic",
|
||||
"runtime/internal/sys",
|
||||
"runtime",
|
||||
"sync/atomic",
|
||||
"sync",
|
||||
"io",
|
||||
"unicode",
|
||||
"unicode/utf8",
|
||||
"bytes",
|
||||
"math",
|
||||
"syscall",
|
||||
"time",
|
||||
"internal/poll",
|
||||
"internal/syscall/unix",
|
||||
"internal/testlog",
|
||||
"os",
|
||||
"math/bits",
|
||||
"strconv",
|
||||
"reflect",
|
||||
"fmt",
|
||||
"sort",
|
||||
"strings",
|
||||
"flag",
|
||||
"runtime/debug",
|
||||
"context",
|
||||
"runtime/trace",
|
||||
"testing",
|
||||
"bufio",
|
||||
"regexp/syntax",
|
||||
"regexp",
|
||||
"compress/flate",
|
||||
"encoding/binary",
|
||||
"hash",
|
||||
"hash/crc32",
|
||||
"compress/gzip",
|
||||
"path/filepath",
|
||||
"io/ioutil",
|
||||
"text/tabwriter",
|
||||
"runtime/pprof",
|
||||
"testing/internal/testdeps",
|
||||
}
|
||||
return extraimports, extradeps, writeTestmain(out, testFuncs)
|
||||
}
|
||||
|
||||
// The following is adapted from the cmd/go testmain generation code.
|
||||
|
||||
// isTestFunc tells whether fn has the type of a testing function. arg
|
||||
// specifies the parameter type we look for: B, M or T.
|
||||
func isTestFunc(fn *ast.FuncDecl, arg string) bool {
|
||||
if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
|
||||
fn.Type.Params.List == nil ||
|
||||
len(fn.Type.Params.List) != 1 ||
|
||||
len(fn.Type.Params.List[0].Names) > 1 {
|
||||
return false
|
||||
}
|
||||
ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
// We can't easily check that the type is *testing.M
|
||||
// because we don't know how testing has been imported,
|
||||
// but at least check that it's *M or *something.M.
|
||||
// Same applies for B and T.
|
||||
if name, ok := ptr.X.(*ast.Ident); ok && name.Name == arg {
|
||||
return true
|
||||
}
|
||||
if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == arg {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isTest tells whether name looks like a test (or benchmark, according to prefix).
|
||||
// It is a Test (say) if there is a character after Test that is not a lower-case letter.
|
||||
// We don't want TesticularCancer.
|
||||
func isTest(name, prefix string) bool {
|
||||
if !strings.HasPrefix(name, prefix) {
|
||||
return false
|
||||
}
|
||||
if len(name) == len(prefix) { // "Test" is ok
|
||||
return true
|
||||
}
|
||||
rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
|
||||
return !unicode.IsLower(rune)
|
||||
}
|
||||
|
||||
// loadTestFuncs returns the testFuncs describing the tests that will be run.
|
||||
func loadTestFuncs(ptest, pxtest *Package) (*testFuncs, error) {
|
||||
t := &testFuncs{
|
||||
TestPackage: ptest,
|
||||
XTestPackage: pxtest,
|
||||
}
|
||||
for _, file := range ptest.GoFiles {
|
||||
if !strings.HasSuffix(file, "_test.go") {
|
||||
continue
|
||||
}
|
||||
if err := t.load(file, "_test", &t.ImportTest, &t.NeedTest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if pxtest != nil {
|
||||
for _, file := range pxtest.GoFiles {
|
||||
if err := t.load(file, "_xtest", &t.ImportXtest, &t.NeedXtest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// writeTestmain writes the _testmain.go file for t to the file named out.
|
||||
func writeTestmain(out string, t *testFuncs) error {
|
||||
f, err := os.Create(out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := testmainTmpl.Execute(f, t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type testFuncs struct {
|
||||
Tests []testFunc
|
||||
Benchmarks []testFunc
|
||||
Examples []testFunc
|
||||
TestMain *testFunc
|
||||
TestPackage *Package
|
||||
XTestPackage *Package
|
||||
ImportTest bool
|
||||
NeedTest bool
|
||||
ImportXtest bool
|
||||
NeedXtest bool
|
||||
}
|
||||
|
||||
// Tested returns the name of the package being tested.
|
||||
func (t *testFuncs) Tested() string {
|
||||
return t.TestPackage.Name
|
||||
}
|
||||
|
||||
type testFunc struct {
|
||||
Package string // imported package name (_test or _xtest)
|
||||
Name string // function name
|
||||
Output string // output, for examples
|
||||
Unordered bool // output is allowed to be unordered.
|
||||
}
|
||||
|
||||
func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
|
||||
var fset = token.NewFileSet()
|
||||
|
||||
f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return errors.New("failed to parse test file " + filename)
|
||||
}
|
||||
for _, d := range f.Decls {
|
||||
n, ok := d.(*ast.FuncDecl)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if n.Recv != nil {
|
||||
continue
|
||||
}
|
||||
name := n.Name.String()
|
||||
switch {
|
||||
case name == "TestMain":
|
||||
if isTestFunc(n, "T") {
|
||||
t.Tests = append(t.Tests, testFunc{pkg, name, "", false})
|
||||
*doImport, *seen = true, true
|
||||
continue
|
||||
}
|
||||
err := checkTestFunc(fset, n, "M")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t.TestMain != nil {
|
||||
return errors.New("multiple definitions of TestMain")
|
||||
}
|
||||
t.TestMain = &testFunc{pkg, name, "", false}
|
||||
*doImport, *seen = true, true
|
||||
case isTest(name, "Test"):
|
||||
err := checkTestFunc(fset, n, "T")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Tests = append(t.Tests, testFunc{pkg, name, "", false})
|
||||
*doImport, *seen = true, true
|
||||
case isTest(name, "Benchmark"):
|
||||
err := checkTestFunc(fset, n, "B")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false})
|
||||
*doImport, *seen = true, true
|
||||
}
|
||||
}
|
||||
ex := doc.Examples(f)
|
||||
sort.Slice(ex, func(i, j int) bool { return ex[i].Order < ex[j].Order })
|
||||
for _, e := range ex {
|
||||
*doImport = true // import test file whether executed or not
|
||||
if e.Output == "" && !e.EmptyOutput {
|
||||
// Don't run examples with no output.
|
||||
continue
|
||||
}
|
||||
t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output, e.Unordered})
|
||||
*seen = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkTestFunc(fset *token.FileSet, fn *ast.FuncDecl, arg string) error {
|
||||
if !isTestFunc(fn, arg) {
|
||||
name := fn.Name.String()
|
||||
pos := fset.Position(fn.Pos())
|
||||
return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var testmainTmpl = template.Must(template.New("main").Parse(`
|
||||
package main
|
||||
|
||||
import (
|
||||
{{if not .TestMain}}
|
||||
"os"
|
||||
{{end}}
|
||||
"testing"
|
||||
"testing/internal/testdeps"
|
||||
|
||||
{{if .ImportTest}}
|
||||
{{if .NeedTest}}_test{{else}}_{{end}} {{.TestPackage.PkgPath | printf "%q"}}
|
||||
{{end}}
|
||||
{{if .ImportXtest}}
|
||||
{{if .NeedXtest}}_xtest{{else}}_{{end}} {{.XTestPackage.PkgPath | printf "%q"}}
|
||||
{{end}}
|
||||
)
|
||||
|
||||
var tests = []testing.InternalTest{
|
||||
{{range .Tests}}
|
||||
{"{{.Name}}", {{.Package}}.{{.Name}}},
|
||||
{{end}}
|
||||
}
|
||||
|
||||
var benchmarks = []testing.InternalBenchmark{
|
||||
{{range .Benchmarks}}
|
||||
{"{{.Name}}", {{.Package}}.{{.Name}}},
|
||||
{{end}}
|
||||
}
|
||||
|
||||
var examples = []testing.InternalExample{
|
||||
{{range .Examples}}
|
||||
{"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}},
|
||||
{{end}}
|
||||
}
|
||||
|
||||
func init() {
|
||||
testdeps.ImportPath = {{.TestPackage.PkgPath | printf "%q"}}
|
||||
}
|
||||
|
||||
func main() {
|
||||
m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples)
|
||||
{{with .TestMain}}
|
||||
{{.Package}}.{{.Name}}(m)
|
||||
{{else}}
|
||||
os.Exit(m.Run())
|
||||
{{end}}
|
||||
}
|
||||
|
||||
`))
|
935
vendor/golang.org/x/tools/go/packages/packages.go
generated
vendored
Normal file
935
vendor/golang.org/x/tools/go/packages/packages.go
generated
vendored
Normal file
@ -0,0 +1,935 @@
|
||||
// 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
|
||||
|
||||
// See doc.go for package documentation and implementation notes.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/tools/go/gcexportdata"
|
||||
)
|
||||
|
||||
// A LoadMode specifies the amount of detail to return when loading.
|
||||
// Higher-numbered modes cause Load to return more information,
|
||||
// but may be slower. Load may return more information than requested.
|
||||
type LoadMode int
|
||||
|
||||
const (
|
||||
// LoadFiles finds the packages and computes their source file lists.
|
||||
// Package fields: ID, Name, Errors, GoFiles, and OtherFiles.
|
||||
LoadFiles LoadMode = iota
|
||||
|
||||
// LoadImports adds import information for each package
|
||||
// and its dependencies.
|
||||
// Package fields added: Imports.
|
||||
LoadImports
|
||||
|
||||
// LoadTypes adds type information for package-level
|
||||
// declarations in the packages matching the patterns.
|
||||
// Package fields added: Types, Fset, and IllTyped.
|
||||
// This mode uses type information provided by the build system when
|
||||
// possible, and may fill in the ExportFile field.
|
||||
LoadTypes
|
||||
|
||||
// LoadSyntax adds typed syntax trees for the packages matching the patterns.
|
||||
// Package fields added: Syntax, and TypesInfo, for direct pattern matches only.
|
||||
LoadSyntax
|
||||
|
||||
// LoadAllSyntax adds typed syntax trees for the packages matching the patterns
|
||||
// and all dependencies.
|
||||
// Package fields added: Types, Fset, Illtyped, Syntax, and TypesInfo,
|
||||
// for all packages in the import graph.
|
||||
LoadAllSyntax
|
||||
)
|
||||
|
||||
// An Config specifies details about how packages should be loaded.
|
||||
// The zero value is a valid configuration.
|
||||
// Calls to Load do not modify this struct.
|
||||
type Config struct {
|
||||
// Mode controls the level of information returned for each package.
|
||||
Mode LoadMode
|
||||
|
||||
// Context specifies the context for the load operation.
|
||||
// If the context is cancelled, the loader may stop early
|
||||
// and return an ErrCancelled error.
|
||||
// If Context is nil, the load cannot be cancelled.
|
||||
Context context.Context
|
||||
|
||||
// Dir is the directory in which to run the build system's query tool
|
||||
// that provides information about the packages.
|
||||
// If Dir is empty, the tool is run in the current directory.
|
||||
Dir string
|
||||
|
||||
// Env is the environment to use when invoking the build system's query tool.
|
||||
// If Env is nil, the current environment is used.
|
||||
// As in os/exec's Cmd, only the last value in the slice for
|
||||
// each environment key is used. To specify the setting of only
|
||||
// a few variables, append to the current environment, as in:
|
||||
//
|
||||
// opt.Env = append(os.Environ(), "GOOS=plan9", "GOARCH=386")
|
||||
//
|
||||
Env []string
|
||||
|
||||
// BuildFlags is a list of command-line flags to be passed through to
|
||||
// the build system's query tool.
|
||||
BuildFlags []string
|
||||
|
||||
// Fset provides source position information for syntax trees and types.
|
||||
// If Fset is nil, the loader will create a new FileSet.
|
||||
Fset *token.FileSet
|
||||
|
||||
// ParseFile is called to read and parse each file
|
||||
// when preparing a package's type-checked syntax tree.
|
||||
// It must be safe to call ParseFile simultaneously from multiple goroutines.
|
||||
// If ParseFile is nil, the loader will uses parser.ParseFile.
|
||||
//
|
||||
// ParseFile should parse the source from src and use filename only for
|
||||
// recording position information.
|
||||
//
|
||||
// An application may supply a custom implementation of ParseFile
|
||||
// to change the effective file contents or the behavior of the parser,
|
||||
// or to modify the syntax tree. For example, selectively eliminating
|
||||
// unwanted function bodies can significantly accelerate type checking.
|
||||
ParseFile func(fset *token.FileSet, filename string, src []byte) (*ast.File, error)
|
||||
|
||||
// If Tests is set, the loader includes not just the packages
|
||||
// matching a particular pattern but also any related test packages,
|
||||
// including test-only variants of the package and the test executable.
|
||||
//
|
||||
// For example, when using the go command, loading "fmt" with Tests=true
|
||||
// returns four packages, with IDs "fmt" (the standard package),
|
||||
// "fmt [fmt.test]" (the package as compiled for the test),
|
||||
// "fmt_test" (the test functions from source files in package fmt_test),
|
||||
// and "fmt.test" (the test binary).
|
||||
//
|
||||
// In build systems with explicit names for tests,
|
||||
// setting Tests may have no effect.
|
||||
Tests bool
|
||||
|
||||
// Overlay provides a mapping of absolute file paths to file contents.
|
||||
// If the file with the given path already exists, the parser will use the
|
||||
// alternative file contents provided by the map.
|
||||
//
|
||||
// The Package.Imports map may not include packages that are imported only
|
||||
// by the alternative file contents provided by Overlay. This may cause
|
||||
// type-checking to fail.
|
||||
Overlay map[string][]byte
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// driverResponse contains the results for a driver query.
|
||||
type driverResponse struct {
|
||||
// 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
|
||||
}
|
||||
|
||||
// Load loads and returns the Go packages named by the given patterns.
|
||||
//
|
||||
// Config specifies loading options;
|
||||
// nil behaves the same as an empty Config.
|
||||
//
|
||||
// Load returns an error if any of the patterns was invalid
|
||||
// as defined by the underlying build system.
|
||||
// It may return an empty list of packages without an error,
|
||||
// for instance for an empty expansion of a valid wildcard.
|
||||
// Errors associated with a particular package are recorded in the
|
||||
// corresponding Package's Errors list, and do not cause Load to
|
||||
// return an error. Clients may need to handle such errors before
|
||||
// proceeding with further analysis. The PrintErrors function is
|
||||
// provided for convenient display of all errors.
|
||||
func Load(cfg *Config, patterns ...string) ([]*Package, error) {
|
||||
l := newLoader(cfg)
|
||||
response, err := defaultDriver(&l.Config, patterns...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l.refine(response.Roots, response.Packages...)
|
||||
}
|
||||
|
||||
// defaultDriver is a driver that looks for an external driver binary, and if
|
||||
// it does not find it falls back to the built in go list driver.
|
||||
func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
|
||||
driver := findExternalDriver(cfg)
|
||||
if driver == nil {
|
||||
driver = goListDriver
|
||||
}
|
||||
return driver(cfg, patterns...)
|
||||
}
|
||||
|
||||
// A Package describes a loaded Go package.
|
||||
type Package struct {
|
||||
// ID is a unique identifier for a package,
|
||||
// in a syntax provided by the underlying build system.
|
||||
//
|
||||
// Because the syntax varies based on the build system,
|
||||
// clients should treat IDs as opaque and not attempt to
|
||||
// interpret them.
|
||||
ID string
|
||||
|
||||
// Name is the package name as it appears in the package source code.
|
||||
Name string
|
||||
|
||||
// PkgPath is the package path as used by the go/types package.
|
||||
PkgPath string
|
||||
|
||||
// Errors contains any errors encountered querying the metadata
|
||||
// of the package, or while parsing or type-checking its files.
|
||||
Errors []Error
|
||||
|
||||
// GoFiles lists the absolute file paths of the package's Go source files.
|
||||
GoFiles []string
|
||||
|
||||
// CompiledGoFiles lists the absolute file paths of the package's source
|
||||
// files that were presented to the compiler.
|
||||
// This may differ from GoFiles if files are processed before compilation.
|
||||
CompiledGoFiles []string
|
||||
|
||||
// OtherFiles lists the absolute file paths of the package's non-Go source files,
|
||||
// including assembly, C, C++, Fortran, Objective-C, SWIG, and so on.
|
||||
OtherFiles []string
|
||||
|
||||
// ExportFile is the absolute path to a file containing type
|
||||
// information for the package as provided by the build system.
|
||||
ExportFile string
|
||||
|
||||
// Imports maps import paths appearing in the package's Go source files
|
||||
// to corresponding loaded Packages.
|
||||
Imports map[string]*Package
|
||||
|
||||
// Types provides type information for the package.
|
||||
// Modes LoadTypes and above set this field for packages matching the
|
||||
// patterns; type information for dependencies may be missing or incomplete.
|
||||
// Mode LoadAllSyntax sets this field for all packages, including dependencies.
|
||||
Types *types.Package
|
||||
|
||||
// Fset provides position information for Types, TypesInfo, and Syntax.
|
||||
// It is set only when Types is set.
|
||||
Fset *token.FileSet
|
||||
|
||||
// IllTyped indicates whether the package or any dependency contains errors.
|
||||
// It is set only when Types is set.
|
||||
IllTyped bool
|
||||
|
||||
// Syntax is the package's syntax trees, for the files listed in CompiledGoFiles.
|
||||
//
|
||||
// Mode LoadSyntax sets this field for packages matching the patterns.
|
||||
// Mode LoadAllSyntax sets this field for all packages, including dependencies.
|
||||
Syntax []*ast.File
|
||||
|
||||
// TypesInfo provides type information about the package's syntax trees.
|
||||
// It is set only when Syntax is set.
|
||||
TypesInfo *types.Info
|
||||
}
|
||||
|
||||
// An Error describes a problem with a package's metadata, syntax, or types.
|
||||
type Error struct {
|
||||
Pos string // "file:line:col" or "file:line" or "" or "-"
|
||||
Msg string
|
||||
Kind ErrorKind
|
||||
}
|
||||
|
||||
// ErrorKind describes the source of the error, allowing the user to
|
||||
// differentiate between errors generated by the driver, the parser, or the
|
||||
// type-checker.
|
||||
type ErrorKind int
|
||||
|
||||
const (
|
||||
UnknownError ErrorKind = iota
|
||||
ListError
|
||||
ParseError
|
||||
TypeError
|
||||
)
|
||||
|
||||
func (err Error) Error() string {
|
||||
pos := err.Pos
|
||||
if pos == "" {
|
||||
pos = "-" // like token.Position{}.String()
|
||||
}
|
||||
return pos + ": " + err.Msg
|
||||
}
|
||||
|
||||
// flatPackage is the JSON form of Package
|
||||
// It drops all the type and syntax fields, and transforms the Imports
|
||||
//
|
||||
// TODO(adonovan): identify this struct with Package, effectively
|
||||
// publishing the JSON protocol.
|
||||
type flatPackage struct {
|
||||
ID string
|
||||
Name string `json:",omitempty"`
|
||||
PkgPath string `json:",omitempty"`
|
||||
Errors []Error `json:",omitempty"`
|
||||
GoFiles []string `json:",omitempty"`
|
||||
CompiledGoFiles []string `json:",omitempty"`
|
||||
OtherFiles []string `json:",omitempty"`
|
||||
ExportFile string `json:",omitempty"`
|
||||
Imports map[string]string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// MarshalJSON returns the Package in its JSON form.
|
||||
// For the most part, the structure fields are written out unmodified, and
|
||||
// the type and syntax fields are skipped.
|
||||
// The imports are written out as just a map of path to package id.
|
||||
// The errors are written using a custom type that tries to preserve the
|
||||
// structure of error types we know about.
|
||||
//
|
||||
// This method exists to enable support for additional build systems. It is
|
||||
// not intended for use by clients of the API and we may change the format.
|
||||
func (p *Package) MarshalJSON() ([]byte, error) {
|
||||
flat := &flatPackage{
|
||||
ID: p.ID,
|
||||
Name: p.Name,
|
||||
PkgPath: p.PkgPath,
|
||||
Errors: p.Errors,
|
||||
GoFiles: p.GoFiles,
|
||||
CompiledGoFiles: p.CompiledGoFiles,
|
||||
OtherFiles: p.OtherFiles,
|
||||
ExportFile: p.ExportFile,
|
||||
}
|
||||
if len(p.Imports) > 0 {
|
||||
flat.Imports = make(map[string]string, len(p.Imports))
|
||||
for path, ipkg := range p.Imports {
|
||||
flat.Imports[path] = ipkg.ID
|
||||
}
|
||||
}
|
||||
return json.Marshal(flat)
|
||||
}
|
||||
|
||||
// UnmarshalJSON reads in a Package from its JSON format.
|
||||
// See MarshalJSON for details about the format accepted.
|
||||
func (p *Package) UnmarshalJSON(b []byte) error {
|
||||
flat := &flatPackage{}
|
||||
if err := json.Unmarshal(b, &flat); err != nil {
|
||||
return err
|
||||
}
|
||||
*p = Package{
|
||||
ID: flat.ID,
|
||||
Name: flat.Name,
|
||||
PkgPath: flat.PkgPath,
|
||||
Errors: flat.Errors,
|
||||
GoFiles: flat.GoFiles,
|
||||
CompiledGoFiles: flat.CompiledGoFiles,
|
||||
OtherFiles: flat.OtherFiles,
|
||||
ExportFile: flat.ExportFile,
|
||||
}
|
||||
if len(flat.Imports) > 0 {
|
||||
p.Imports = make(map[string]*Package, len(flat.Imports))
|
||||
for path, id := range flat.Imports {
|
||||
p.Imports[path] = &Package{ID: id}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Package) String() string { return p.ID }
|
||||
|
||||
// loaderPackage augments Package with state used during the loading phase
|
||||
type loaderPackage struct {
|
||||
*Package
|
||||
importErrors map[string]error // maps each bad import to its error
|
||||
loadOnce sync.Once
|
||||
color uint8 // for cycle detection
|
||||
needsrc bool // load from source (Mode >= LoadTypes)
|
||||
needtypes bool // type information is either requested or depended on
|
||||
initial bool // package was matched by a pattern
|
||||
}
|
||||
|
||||
// loader holds the working state of a single call to load.
|
||||
type loader struct {
|
||||
pkgs map[string]*loaderPackage
|
||||
Config
|
||||
exportMu sync.Mutex // enforces mutual exclusion of exportdata operations
|
||||
}
|
||||
|
||||
func newLoader(cfg *Config) *loader {
|
||||
ld := &loader{}
|
||||
if cfg != nil {
|
||||
ld.Config = *cfg
|
||||
}
|
||||
if ld.Config.Env == nil {
|
||||
ld.Config.Env = os.Environ()
|
||||
}
|
||||
if ld.Context == nil {
|
||||
ld.Context = context.Background()
|
||||
}
|
||||
if ld.Dir == "" {
|
||||
if dir, err := os.Getwd(); err == nil {
|
||||
ld.Dir = dir
|
||||
}
|
||||
}
|
||||
|
||||
if ld.Mode >= LoadTypes {
|
||||
if ld.Fset == nil {
|
||||
ld.Fset = token.NewFileSet()
|
||||
}
|
||||
|
||||
// ParseFile is required even in LoadTypes mode
|
||||
// because we load source if export data is missing.
|
||||
if ld.ParseFile == nil {
|
||||
ld.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) {
|
||||
var isrc interface{}
|
||||
if src != nil {
|
||||
isrc = src
|
||||
}
|
||||
const mode = parser.AllErrors | parser.ParseComments
|
||||
return parser.ParseFile(fset, filename, isrc, mode)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ld
|
||||
}
|
||||
|
||||
// refine connects the supplied packages into a graph and then adds type and
|
||||
// and syntax information as requested by the LoadMode.
|
||||
func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
|
||||
isRoot := make(map[string]bool, len(roots))
|
||||
for _, root := range roots {
|
||||
isRoot[root] = true
|
||||
}
|
||||
ld.pkgs = make(map[string]*loaderPackage)
|
||||
// first pass, fixup and build the map and roots
|
||||
var initial []*loaderPackage
|
||||
for _, pkg := range list {
|
||||
lpkg := &loaderPackage{
|
||||
Package: pkg,
|
||||
needtypes: ld.Mode >= LoadAllSyntax ||
|
||||
ld.Mode >= LoadTypes && isRoot[pkg.ID],
|
||||
needsrc: ld.Mode >= LoadAllSyntax ||
|
||||
ld.Mode >= LoadSyntax && isRoot[pkg.ID] ||
|
||||
pkg.ExportFile == "" && pkg.PkgPath != "unsafe",
|
||||
}
|
||||
ld.pkgs[lpkg.ID] = lpkg
|
||||
if isRoot[lpkg.ID] {
|
||||
initial = append(initial, lpkg)
|
||||
lpkg.initial = true
|
||||
}
|
||||
}
|
||||
|
||||
// Materialize the import graph.
|
||||
|
||||
const (
|
||||
white = 0 // new
|
||||
grey = 1 // in progress
|
||||
black = 2 // complete
|
||||
)
|
||||
|
||||
// visit traverses the import graph, depth-first,
|
||||
// and materializes the graph as Packages.Imports.
|
||||
//
|
||||
// Valid imports are saved in the Packages.Import map.
|
||||
// Invalid imports (cycles and missing nodes) are saved in the importErrors map.
|
||||
// Thus, even in the presence of both kinds of errors, the Import graph remains a DAG.
|
||||
//
|
||||
// visit returns whether the package needs src or has a transitive
|
||||
// dependency on a package that does. These are the only packages
|
||||
// for which we load source code.
|
||||
var stack []*loaderPackage
|
||||
var visit func(lpkg *loaderPackage) bool
|
||||
var srcPkgs []*loaderPackage
|
||||
visit = func(lpkg *loaderPackage) bool {
|
||||
switch lpkg.color {
|
||||
case black:
|
||||
return lpkg.needsrc
|
||||
case grey:
|
||||
panic("internal error: grey node")
|
||||
}
|
||||
lpkg.color = grey
|
||||
stack = append(stack, lpkg) // push
|
||||
stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports
|
||||
lpkg.Imports = make(map[string]*Package, len(stubs))
|
||||
for importPath, ipkg := range stubs {
|
||||
var importErr error
|
||||
imp := ld.pkgs[ipkg.ID]
|
||||
if imp == nil {
|
||||
// (includes package "C" when DisableCgo)
|
||||
importErr = fmt.Errorf("missing package: %q", ipkg.ID)
|
||||
} else if imp.color == grey {
|
||||
importErr = fmt.Errorf("import cycle: %s", stack)
|
||||
}
|
||||
if importErr != nil {
|
||||
if lpkg.importErrors == nil {
|
||||
lpkg.importErrors = make(map[string]error)
|
||||
}
|
||||
lpkg.importErrors[importPath] = importErr
|
||||
continue
|
||||
}
|
||||
|
||||
if visit(imp) {
|
||||
lpkg.needsrc = true
|
||||
}
|
||||
lpkg.Imports[importPath] = imp.Package
|
||||
}
|
||||
if lpkg.needsrc {
|
||||
srcPkgs = append(srcPkgs, lpkg)
|
||||
}
|
||||
stack = stack[:len(stack)-1] // pop
|
||||
lpkg.color = black
|
||||
|
||||
return lpkg.needsrc
|
||||
}
|
||||
|
||||
if ld.Mode < LoadImports {
|
||||
//we do this to drop the stub import packages that we are not even going to try to resolve
|
||||
for _, lpkg := range initial {
|
||||
lpkg.Imports = nil
|
||||
}
|
||||
} else {
|
||||
// For each initial package, create its import DAG.
|
||||
for _, lpkg := range initial {
|
||||
visit(lpkg)
|
||||
}
|
||||
}
|
||||
for _, lpkg := range srcPkgs {
|
||||
// Complete type information is required for the
|
||||
// immediate dependencies of each source package.
|
||||
for _, ipkg := range lpkg.Imports {
|
||||
imp := ld.pkgs[ipkg.ID]
|
||||
imp.needtypes = true
|
||||
}
|
||||
}
|
||||
// Load type data if needed, starting at
|
||||
// the initial packages (roots of the import DAG).
|
||||
if ld.Mode >= LoadTypes {
|
||||
var wg sync.WaitGroup
|
||||
for _, lpkg := range initial {
|
||||
wg.Add(1)
|
||||
go func(lpkg *loaderPackage) {
|
||||
ld.loadRecursive(lpkg)
|
||||
wg.Done()
|
||||
}(lpkg)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
result := make([]*Package, len(initial))
|
||||
for i, lpkg := range initial {
|
||||
result[i] = lpkg.Package
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// loadRecursive loads the specified package and its dependencies,
|
||||
// recursively, in parallel, in topological order.
|
||||
// It is atomic and idempotent.
|
||||
// Precondition: ld.Mode >= LoadTypes.
|
||||
func (ld *loader) loadRecursive(lpkg *loaderPackage) {
|
||||
lpkg.loadOnce.Do(func() {
|
||||
// Load the direct dependencies, in parallel.
|
||||
var wg sync.WaitGroup
|
||||
for _, ipkg := range lpkg.Imports {
|
||||
imp := ld.pkgs[ipkg.ID]
|
||||
wg.Add(1)
|
||||
go func(imp *loaderPackage) {
|
||||
ld.loadRecursive(imp)
|
||||
wg.Done()
|
||||
}(imp)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
ld.loadPackage(lpkg)
|
||||
})
|
||||
}
|
||||
|
||||
// loadPackage loads the specified package.
|
||||
// It must be called only once per Package,
|
||||
// after immediate dependencies are loaded.
|
||||
// Precondition: ld.Mode >= LoadTypes.
|
||||
func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
||||
if lpkg.PkgPath == "unsafe" {
|
||||
// Fill in the blanks to avoid surprises.
|
||||
lpkg.Types = types.Unsafe
|
||||
lpkg.Fset = ld.Fset
|
||||
lpkg.Syntax = []*ast.File{}
|
||||
lpkg.TypesInfo = new(types.Info)
|
||||
return
|
||||
}
|
||||
|
||||
// Call NewPackage directly with explicit name.
|
||||
// This avoids skew between golist and go/types when the files'
|
||||
// package declarations are inconsistent.
|
||||
lpkg.Types = types.NewPackage(lpkg.PkgPath, lpkg.Name)
|
||||
lpkg.Fset = ld.Fset
|
||||
|
||||
// Subtle: we populate all Types fields with an empty Package
|
||||
// before loading export data so that export data processing
|
||||
// never has to create a types.Package for an indirect dependency,
|
||||
// which would then require that such created packages be explicitly
|
||||
// inserted back into the Import graph as a final step after export data loading.
|
||||
// The Diamond test exercises this case.
|
||||
if !lpkg.needtypes {
|
||||
return
|
||||
}
|
||||
if !lpkg.needsrc {
|
||||
ld.loadFromExportData(lpkg)
|
||||
return // not a source package, don't get syntax trees
|
||||
}
|
||||
|
||||
appendError := func(err error) {
|
||||
// Convert various error types into the one true Error.
|
||||
var errs []Error
|
||||
switch err := err.(type) {
|
||||
case Error:
|
||||
// from driver
|
||||
errs = append(errs, err)
|
||||
|
||||
case *os.PathError:
|
||||
// from parser
|
||||
errs = append(errs, Error{
|
||||
Pos: err.Path + ":1",
|
||||
Msg: err.Err.Error(),
|
||||
Kind: ParseError,
|
||||
})
|
||||
|
||||
case scanner.ErrorList:
|
||||
// from parser
|
||||
for _, err := range err {
|
||||
errs = append(errs, Error{
|
||||
Pos: err.Pos.String(),
|
||||
Msg: err.Msg,
|
||||
Kind: ParseError,
|
||||
})
|
||||
}
|
||||
|
||||
case types.Error:
|
||||
// from type checker
|
||||
errs = append(errs, Error{
|
||||
Pos: err.Fset.Position(err.Pos).String(),
|
||||
Msg: err.Msg,
|
||||
Kind: TypeError,
|
||||
})
|
||||
|
||||
default:
|
||||
// unexpected impoverished error from parser?
|
||||
errs = append(errs, Error{
|
||||
Pos: "-",
|
||||
Msg: err.Error(),
|
||||
Kind: UnknownError,
|
||||
})
|
||||
|
||||
// If you see this error message, please file a bug.
|
||||
log.Printf("internal error: error %q (%T) without position", err, err)
|
||||
}
|
||||
|
||||
lpkg.Errors = append(lpkg.Errors, errs...)
|
||||
}
|
||||
|
||||
files, errs := ld.parseFiles(lpkg.CompiledGoFiles)
|
||||
for _, err := range errs {
|
||||
appendError(err)
|
||||
}
|
||||
|
||||
lpkg.Syntax = files
|
||||
|
||||
lpkg.TypesInfo = &types.Info{
|
||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||
Defs: make(map[*ast.Ident]types.Object),
|
||||
Uses: make(map[*ast.Ident]types.Object),
|
||||
Implicits: make(map[ast.Node]types.Object),
|
||||
Scopes: make(map[ast.Node]*types.Scope),
|
||||
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
||||
}
|
||||
|
||||
importer := importerFunc(func(path string) (*types.Package, error) {
|
||||
if path == "unsafe" {
|
||||
return types.Unsafe, nil
|
||||
}
|
||||
|
||||
// The imports map is keyed by import path.
|
||||
ipkg := lpkg.Imports[path]
|
||||
if ipkg == nil {
|
||||
if err := lpkg.importErrors[path]; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// There was skew between the metadata and the
|
||||
// import declarations, likely due to an edit
|
||||
// race, or because the ParseFile feature was
|
||||
// used to supply alternative file contents.
|
||||
return nil, fmt.Errorf("no metadata for %s", path)
|
||||
}
|
||||
|
||||
if ipkg.Types != nil && ipkg.Types.Complete() {
|
||||
return ipkg.Types, nil
|
||||
}
|
||||
log.Fatalf("internal error: nil Pkg importing %q from %q", path, lpkg)
|
||||
panic("unreachable")
|
||||
})
|
||||
|
||||
// This is only an approximation.
|
||||
// TODO(adonovan): derive Sizes from the underlying build system.
|
||||
goarch := runtime.GOARCH
|
||||
const goarchPrefix = "GOARCH="
|
||||
for _, e := range ld.Config.Env {
|
||||
if strings.HasPrefix(e, goarchPrefix) {
|
||||
goarch = e[len(goarchPrefix):]
|
||||
}
|
||||
}
|
||||
sizes := types.SizesFor("gc", goarch)
|
||||
|
||||
// type-check
|
||||
tc := &types.Config{
|
||||
Importer: importer,
|
||||
|
||||
// Type-check bodies of functions only in non-initial packages.
|
||||
// Example: for import graph A->B->C and initial packages {A,C},
|
||||
// we can ignore function bodies in B.
|
||||
IgnoreFuncBodies: ld.Mode < LoadAllSyntax && !lpkg.initial,
|
||||
|
||||
Error: appendError,
|
||||
Sizes: sizes,
|
||||
}
|
||||
types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
|
||||
|
||||
lpkg.importErrors = nil // no longer needed
|
||||
|
||||
// If !Cgo, the type-checker uses FakeImportC mode, so
|
||||
// it doesn't invoke the importer for import "C",
|
||||
// nor report an error for the import,
|
||||
// or for any undefined C.f reference.
|
||||
// We must detect this explicitly and correctly
|
||||
// mark the package as IllTyped (by reporting an error).
|
||||
// TODO(adonovan): if these errors are annoying,
|
||||
// we could just set IllTyped quietly.
|
||||
if tc.FakeImportC {
|
||||
outer:
|
||||
for _, f := range lpkg.Syntax {
|
||||
for _, imp := range f.Imports {
|
||||
if imp.Path.Value == `"C"` {
|
||||
err := types.Error{Fset: ld.Fset, Pos: imp.Pos(), Msg: `import "C" ignored`}
|
||||
appendError(err)
|
||||
break outer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Record accumulated errors.
|
||||
illTyped := len(lpkg.Errors) > 0
|
||||
if !illTyped {
|
||||
for _, imp := range lpkg.Imports {
|
||||
if imp.IllTyped {
|
||||
illTyped = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
lpkg.IllTyped = illTyped
|
||||
}
|
||||
|
||||
// An importFunc is an implementation of the single-method
|
||||
// types.Importer interface based on a function value.
|
||||
type importerFunc func(path string) (*types.Package, error)
|
||||
|
||||
func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
|
||||
|
||||
// We use a counting semaphore to limit
|
||||
// the number of parallel I/O calls per process.
|
||||
var ioLimit = make(chan bool, 20)
|
||||
|
||||
// parseFiles reads and parses the Go source files and returns the ASTs
|
||||
// of the ones that could be at least partially parsed, along with a
|
||||
// list of I/O and parse errors encountered.
|
||||
//
|
||||
// Because files are scanned in parallel, the token.Pos
|
||||
// positions of the resulting ast.Files are not ordered.
|
||||
//
|
||||
func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) {
|
||||
var wg sync.WaitGroup
|
||||
n := len(filenames)
|
||||
parsed := make([]*ast.File, n)
|
||||
errors := make([]error, n)
|
||||
for i, file := range filenames {
|
||||
wg.Add(1)
|
||||
go func(i int, filename string) {
|
||||
ioLimit <- true // wait
|
||||
// ParseFile may return both an AST and an error.
|
||||
var src []byte
|
||||
for f, contents := range ld.Config.Overlay {
|
||||
if sameFile(f, filename) {
|
||||
src = contents
|
||||
}
|
||||
}
|
||||
var err error
|
||||
if src == nil {
|
||||
src, err = ioutil.ReadFile(filename)
|
||||
}
|
||||
if err != nil {
|
||||
parsed[i], errors[i] = nil, err
|
||||
} else {
|
||||
parsed[i], errors[i] = ld.ParseFile(ld.Fset, filename, src)
|
||||
}
|
||||
<-ioLimit // signal
|
||||
wg.Done()
|
||||
}(i, file)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// Eliminate nils, preserving order.
|
||||
var o int
|
||||
for _, f := range parsed {
|
||||
if f != nil {
|
||||
parsed[o] = f
|
||||
o++
|
||||
}
|
||||
}
|
||||
parsed = parsed[:o]
|
||||
|
||||
o = 0
|
||||
for _, err := range errors {
|
||||
if err != nil {
|
||||
errors[o] = err
|
||||
o++
|
||||
}
|
||||
}
|
||||
errors = errors[:o]
|
||||
|
||||
return parsed, errors
|
||||
}
|
||||
|
||||
// sameFile returns true if x and y have the same basename and denote
|
||||
// the same file.
|
||||
//
|
||||
func sameFile(x, y string) bool {
|
||||
if filepath.Base(x) == filepath.Base(y) { // (optimisation)
|
||||
if xi, err := os.Stat(x); err == nil {
|
||||
if yi, err := os.Stat(y); err == nil {
|
||||
return os.SameFile(xi, yi)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// loadFromExportData returns type information for the specified
|
||||
// package, loading it from an export data file on the first request.
|
||||
func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error) {
|
||||
if lpkg.PkgPath == "" {
|
||||
log.Fatalf("internal error: Package %s has no PkgPath", lpkg)
|
||||
}
|
||||
|
||||
// Because gcexportdata.Read has the potential to create or
|
||||
// modify the types.Package for each node in the transitive
|
||||
// closure of dependencies of lpkg, all exportdata operations
|
||||
// must be sequential. (Finer-grained locking would require
|
||||
// changes to the gcexportdata API.)
|
||||
//
|
||||
// The exportMu lock guards the Package.Pkg field and the
|
||||
// types.Package it points to, for each Package in the graph.
|
||||
//
|
||||
// Not all accesses to Package.Pkg need to be protected by exportMu:
|
||||
// graph ordering ensures that direct dependencies of source
|
||||
// packages are fully loaded before the importer reads their Pkg field.
|
||||
ld.exportMu.Lock()
|
||||
defer ld.exportMu.Unlock()
|
||||
|
||||
if tpkg := lpkg.Types; tpkg != nil && tpkg.Complete() {
|
||||
return tpkg, nil // cache hit
|
||||
}
|
||||
|
||||
lpkg.IllTyped = true // fail safe
|
||||
|
||||
if lpkg.ExportFile == "" {
|
||||
// Errors while building export data will have been printed to stderr.
|
||||
return nil, fmt.Errorf("no export data file")
|
||||
}
|
||||
f, err := os.Open(lpkg.ExportFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Read gc export data.
|
||||
//
|
||||
// We don't currently support gccgo export data because all
|
||||
// underlying workspaces use the gc toolchain. (Even build
|
||||
// systems that support gccgo don't use it for workspace
|
||||
// queries.)
|
||||
r, err := gcexportdata.NewReader(f)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading %s: %v", lpkg.ExportFile, err)
|
||||
}
|
||||
|
||||
// Build the view.
|
||||
//
|
||||
// The gcexportdata machinery has no concept of package ID.
|
||||
// It identifies packages by their PkgPath, which although not
|
||||
// globally unique is unique within the scope of one invocation
|
||||
// of the linker, type-checker, or gcexportdata.
|
||||
//
|
||||
// So, we must build a PkgPath-keyed view of the global
|
||||
// (conceptually ID-keyed) cache of packages and pass it to
|
||||
// gcexportdata. The view must contain every existing
|
||||
// package that might possibly be mentioned by the
|
||||
// current package---its transitive closure.
|
||||
//
|
||||
// In loadPackage, we unconditionally create a types.Package for
|
||||
// each dependency so that export data loading does not
|
||||
// create new ones.
|
||||
//
|
||||
// TODO(adonovan): it would be simpler and more efficient
|
||||
// if the export data machinery invoked a callback to
|
||||
// get-or-create a package instead of a map.
|
||||
//
|
||||
view := make(map[string]*types.Package) // view seen by gcexportdata
|
||||
seen := make(map[*loaderPackage]bool) // all visited packages
|
||||
var visit func(pkgs map[string]*Package)
|
||||
visit = func(pkgs map[string]*Package) {
|
||||
for _, p := range pkgs {
|
||||
lpkg := ld.pkgs[p.ID]
|
||||
if !seen[lpkg] {
|
||||
seen[lpkg] = true
|
||||
view[lpkg.PkgPath] = lpkg.Types
|
||||
visit(lpkg.Imports)
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(lpkg.Imports)
|
||||
|
||||
viewLen := len(view) + 1 // adding the self package
|
||||
// Parse the export data.
|
||||
// (May modify incomplete packages in view but not create new ones.)
|
||||
tpkg, err := gcexportdata.Read(r, ld.Fset, view, lpkg.PkgPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading %s: %v", lpkg.ExportFile, err)
|
||||
}
|
||||
if viewLen != len(view) {
|
||||
log.Fatalf("Unexpected package creation during export data loading")
|
||||
}
|
||||
|
||||
lpkg.Types = tpkg
|
||||
lpkg.IllTyped = false
|
||||
|
||||
return tpkg, nil
|
||||
}
|
||||
|
||||
func usesExportData(cfg *Config) bool {
|
||||
return LoadTypes <= cfg.Mode && cfg.Mode < LoadAllSyntax
|
||||
}
|
55
vendor/golang.org/x/tools/go/packages/visit.go
generated
vendored
Normal file
55
vendor/golang.org/x/tools/go/packages/visit.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
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) // for determinism
|
||||
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
|
||||
Visit(pkgs, nil, func(pkg *Package) {
|
||||
for _, err := range pkg.Errors {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
n++
|
||||
}
|
||||
})
|
||||
return n
|
||||
}
|
Reference in New Issue
Block a user