Source File
packages.go
Belonging Package
golang.org/x/tools/go/packages
// 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 (
)
// A LoadMode controls the amount of detail to return when loading.
// The bits below can be combined to specify which fields should be
// filled in the result packages.
//
// The zero value is a special case, equivalent to combining
// the NeedName, NeedFiles, and NeedCompiledGoFiles bits.
//
// ID and Errors (if present) will always be filled.
// [Load] may return more information than requested.
//
// The Mode flag is a union of several bits named NeedName,
// NeedFiles, and so on, each of which determines whether
// a given field of Package (Name, Files, etc) should be
// populated.
//
// For convenience, we provide named constants for the most
// common combinations of Need flags:
//
// [LoadFiles] lists of files in each package
// [LoadImports] ... plus imports
// [LoadTypes] ... plus type information
// [LoadSyntax] ... plus type-annotated syntax
// [LoadAllSyntax] ... for all dependencies
//
// Unfortunately there are a number of open bugs related to
// interactions among the LoadMode bits:
// - https://go.dev/issue/56633
// - https://go.dev/issue/56677
// - https://go.dev/issue/58726
// - https://go.dev/issue/63517
type LoadMode int
const (
// NeedName adds Name and PkgPath.
NeedName LoadMode = 1 << iota
// NeedFiles adds Dir, GoFiles, OtherFiles, and IgnoredFiles
NeedFiles
// NeedCompiledGoFiles adds CompiledGoFiles.
NeedCompiledGoFiles
// NeedImports adds Imports. If NeedDeps is not set, the Imports field will contain
// "placeholder" Packages with only the ID set.
NeedImports
// NeedDeps adds the fields requested by the LoadMode in the packages in Imports.
NeedDeps
// NeedExportFile adds ExportFile.
NeedExportFile
// NeedTypes adds Types, Fset, and IllTyped.
NeedTypes
// NeedSyntax adds Syntax and Fset.
NeedSyntax
// NeedTypesInfo adds TypesInfo and Fset.
NeedTypesInfo
// NeedTypesSizes adds TypesSizes.
NeedTypesSizes
// needInternalDepsErrors adds the internal deps errors field for use by gopls.
needInternalDepsErrors
// NeedForTest adds ForTest.
//
// Tests must also be set on the context for this field to be populated.
NeedForTest
// typecheckCgo enables full support for type checking cgo. Requires Go 1.15+.
// Modifies CompiledGoFiles and Types, and has no effect on its own.
typecheckCgo
// NeedModule adds Module.
NeedModule
// NeedEmbedFiles adds EmbedFiles.
NeedEmbedFiles
// NeedEmbedPatterns adds EmbedPatterns.
NeedEmbedPatterns
// NeedTarget adds Target.
NeedTarget
// Be sure to update loadmode_string.go when adding new items!
)
const (
// LoadFiles loads the name and file names for the initial packages.
LoadFiles = NeedName | NeedFiles | NeedCompiledGoFiles
// LoadImports loads the name, file names, and import mapping for the initial packages.
LoadImports = LoadFiles | NeedImports
// LoadTypes loads exported type information for the initial packages.
LoadTypes = LoadImports | NeedTypes | NeedTypesSizes
// LoadSyntax loads typed syntax for the initial packages.
LoadSyntax = LoadTypes | NeedSyntax | NeedTypesInfo
// LoadAllSyntax loads typed syntax for the initial packages and all dependencies.
LoadAllSyntax = LoadSyntax | NeedDeps
// Deprecated: NeedExportsFile is a historical misspelling of NeedExportFile.
//
//go:fix inline
NeedExportsFile = NeedExportFile
)
// A 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.
// Cancelling the context may cause [Load] to abort and
// return an error.
Context context.Context
// Logf is the logger for the config.
// If the user provides a logger, debug logging is enabled.
// If the GOPACKAGESDEBUG environment variable is set to true,
// but the logger is nil, default to log.Printf.
Logf func(format string, args ...any)
// 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, Load will use a new fileset, but preserve Fset's value.
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 is a mapping from absolute file paths to file contents.
//
// For each map entry, [Load] uses the alternative file
// contents provided by the overlay mapping instead of reading
// from the file system. This mechanism can be used to enable
// editor-integrated tools to correctly analyze the contents
// of modified but unsaved buffers, for example.
//
// The overlay mapping is passed to the build system's driver
// (see "The driver protocol") so that it too can report
// consistent package metadata about unsaved files. However,
// drivers may vary in their level of support for overlays.
Overlay map[string][]byte
}
// Load loads and returns the Go packages named by the given patterns.
//
// The cfg parameter specifies loading options; nil behaves the same as an empty [Config].
//
// The [Config.Mode] field is a set of bits that determine what kinds
// of information should be computed and returned. Modes that require
// more information tend to be slower. See [LoadMode] for details
// and important caveats. Its zero value is equivalent to
// [NeedName] | [NeedFiles] | [NeedCompiledGoFiles].
//
// Each call to Load returns a new set of [Package] instances.
// The Packages and their Imports form a directed acyclic graph.
//
// If the [NeedTypes] mode flag was set, each call to Load uses a new
// [types.Importer], so [types.Object] and [types.Type] values from
// different calls to Load must not be mixed as they will have
// inconsistent notions of type identity.
//
// If any of the patterns was invalid as defined by the
// underlying build system, Load returns an error.
// 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 ( *Config, ...string) ([]*Package, error) {
:= newLoader()
, , := defaultDriver(&.Config, ...)
if != nil {
return nil,
}
.sizes = types.SizesFor(.Compiler, .Arch)
if .sizes == nil && .Config.Mode&(NeedTypes|NeedTypesSizes|NeedTypesInfo) != 0 {
// Type size information is needed but unavailable.
if {
// An external driver may fail to populate the Compiler/GOARCH fields,
// especially since they are relatively new (see #63700).
// Provide a sensible fallback in this case.
.sizes = types.SizesFor("gc", runtime.GOARCH)
if .sizes == nil { // gccgo-only arch
.sizes = types.SizesFor("gc", "amd64")
}
} else {
// Go list should never fail to deliver accurate size information.
// Reject the whole Load since the error is the same for every package.
return nil, fmt.Errorf("can't determine type sizes for compiler %q on GOARCH %q",
.Compiler, .Arch)
}
}
return .refine()
}
// defaultDriver is a driver that implements go/packages' fallback behavior.
// It will try to request to an external driver, if one exists. If there's
// no external driver, or the driver returns a response with NotHandled set,
// defaultDriver will fall back to the go list driver.
// The boolean result indicates that an external driver handled the request.
func ( *Config, ...string) (*DriverResponse, bool, error) {
const (
// windowsArgMax specifies the maximum command line length for
// the Windows' CreateProcess function.
= 32767
// maxEnvSize is a very rough estimation of the maximum environment
// size of a user.
= 16384
// safeArgMax specifies the maximum safe command line length to use
// by the underlying driver excl. the environment. We choose the Windows'
// ARG_MAX as the starting point because it's one of the lowest ARG_MAX
// constants out of the different supported platforms,
// e.g., https://www.in-ulm.de/~mascheck/various/argmax/#results.
= -
)
, := splitIntoChunks(, )
if != nil {
return nil, false,
}
if := findExternalDriver(); != nil {
, := callDriverOnChunks(, , )
if != nil {
return nil, false,
} else if !.NotHandled {
return , true, nil
}
// not handled: fall through
}
// go list fallback
// Write overlays once, as there are many calls
// to 'go list' (one per chunk plus others too).
, , := gocommand.WriteOverlays(.Overlay)
if != nil {
return nil, false,
}
defer ()
var gocommand.Runner // (shared across many 'go list' calls)
:= func( *Config, []string) (*DriverResponse, error) {
return goListDriver(, &, , )
}
, := callDriverOnChunks(, , )
if != nil {
return nil, false,
}
return , false,
}
// splitIntoChunks chunks the slice so that the total number of characters
// in a chunk is no longer than argMax.
func ( []string, int) ([][]string, error) {
if <= 0 {
return nil, errors.New("failed to split patterns into chunks, negative safe argMax value")
}
var [][]string
:= 0
:= 0
for , := range {
:= len()
if > {
// a single pattern is longer than the maximum safe ARG_MAX, hardly should happen
return nil, errors.New("failed to split patterns into chunks, a pattern is too long")
}
+= + 1 // +1 is for a whitespace between patterns that has to be counted too
if > {
= append(, [:])
=
=
}
}
// add the last chunk
if < len() {
= append(, [:])
}
return , nil
}
func ( driver, *Config, [][]string) (*DriverResponse, error) {
if len() == 0 {
return (, nil)
}
:= make([]*DriverResponse, len())
:= errors.New("driver returned NotHandled")
var errgroup.Group
for , := range {
.Go(func() ( error) {
[], = (, )
if [] != nil && [].NotHandled {
=
}
return
})
}
if := .Wait(); != nil {
if errors.Is(, ) {
return &DriverResponse{NotHandled: true}, nil
}
return nil,
}
return mergeResponses(...), nil
}
func ( ...*DriverResponse) *DriverResponse {
if len() == 0 {
return nil
}
:= newDeduper()
.dr.NotHandled = false
.dr.Compiler = [0].Compiler
.dr.Arch = [0].Arch
.dr.GoVersion = [0].GoVersion
for , := range {
.addAll()
}
return .dr
}
// A Package describes a loaded Go package.
//
// It also defines part of the JSON schema of [DriverResponse].
// See the package documentation for an overview.
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
// Dir is the directory associated with the package, if it exists.
//
// For packages listed by the go command, this is the directory containing
// the package files.
Dir string
// Errors contains any errors encountered querying the metadata
// of the package, or while parsing or type-checking its files.
Errors []Error
// TypeErrors contains the subset of errors produced during type checking.
TypeErrors []types.Error
// GoFiles lists the absolute file paths of the package's Go source files.
// It may include files that should not be compiled, for example because
// they contain non-matching build tags, are documentary pseudo-files such as
// unsafe/unsafe.go or builtin/builtin.go, or are subject to cgo preprocessing.
GoFiles []string
// CompiledGoFiles lists the absolute file paths of the package's source
// files that are suitable for type checking.
// 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
// EmbedFiles lists the absolute file paths of the package's files
// embedded with go:embed.
EmbedFiles []string
// EmbedPatterns lists the absolute file patterns of the package's
// files embedded with go:embed.
EmbedPatterns []string
// IgnoredFiles lists source files that are not part of the package
// using the current build configuration but that might be part of
// the package using other build configurations.
IgnoredFiles []string
// ExportFile is the absolute path to a file containing type
// information for the package as provided by the build system.
ExportFile string
// Target is the absolute install path of the .a file, for libraries,
// and of the executable file, for binaries.
Target string
// Imports maps import paths appearing in the package's Go source files
// to corresponding loaded Packages.
Imports map[string]*Package
// Module is the module information for the package if it exists.
//
// Note: it may be missing for std and cmd; see Go issue #65816.
Module *Module
// -- The following fields are not part of the driver JSON schema. --
// Types provides type information for the package.
// The NeedTypes LoadMode bit sets this field for packages matching the
// patterns; type information for dependencies may be missing or incomplete,
// unless NeedDeps and NeedImports are also set.
//
// Each call to [Load] returns a consistent set of type
// symbols, as defined by the comment at [types.Identical].
// Avoid mixing type information from two or more calls to [Load].
Types *types.Package `json:"-"`
// Fset provides position information for Types, TypesInfo, and Syntax.
// It is set only when Types is set.
Fset *token.FileSet `json:"-"`
// IllTyped indicates whether the package or any dependency contains errors.
// It is set only when Types is set.
IllTyped bool `json:"-"`
// Syntax is the package's syntax trees, for the files listed in CompiledGoFiles.
//
// The NeedSyntax LoadMode bit populates this field for packages matching the patterns.
// If NeedDeps and NeedImports are also set, this field will also be populated
// for dependencies.
//
// Syntax is kept in the same order as CompiledGoFiles, with the caveat that nils are
// removed. If parsing returned nil, Syntax may be shorter than CompiledGoFiles.
Syntax []*ast.File `json:"-"`
// TypesInfo provides type information about the package's syntax trees.
// It is set only when Syntax is set.
TypesInfo *types.Info `json:"-"`
// TypesSizes provides the effective size function for types in TypesInfo.
TypesSizes types.Sizes `json:"-"`
// -- internal --
// ForTest is the package under test, if any.
ForTest string
// depsErrors is the DepsErrors field from the go list response, if any.
depsErrors []*packagesinternal.PackageError
}
// Module provides module information for a package.
//
// It also defines part of the JSON schema of [DriverResponse].
// See the package documentation for an overview.
type Module struct {
Path string // module path
Version string // module version
Replace *Module // replaced by this module
Time *time.Time // time version was created
Main bool // is this the main module?
Indirect bool // is this module only an indirect dependency of main module?
Dir string // directory holding files for this module, if any
GoMod string // path to go.mod file used when loading this module, if any
GoVersion string // go version used in module
Error *ModuleError // error loading module
}
// ModuleError holds errors loading a module.
type ModuleError struct {
Err string // the error itself
}
func () {
packagesinternal.GetDepsErrors = func( any) []*packagesinternal.PackageError {
return .(*Package).depsErrors
}
packagesinternal.TypecheckCgo = int(typecheckCgo)
packagesinternal.DepsErrors = int(needInternalDepsErrors)
}
// 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 ( Error) () string {
:= .Pos
if == "" {
= "-" // like token.Position{}.String()
}
return + ": " + .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"`
EmbedFiles []string `json:",omitempty"`
EmbedPatterns []string `json:",omitempty"`
IgnoredFiles []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 ( *Package) () ([]byte, error) {
:= &flatPackage{
ID: .ID,
Name: .Name,
PkgPath: .PkgPath,
Errors: .Errors,
GoFiles: .GoFiles,
CompiledGoFiles: .CompiledGoFiles,
OtherFiles: .OtherFiles,
EmbedFiles: .EmbedFiles,
EmbedPatterns: .EmbedPatterns,
IgnoredFiles: .IgnoredFiles,
ExportFile: .ExportFile,
}
if len(.Imports) > 0 {
.Imports = make(map[string]string, len(.Imports))
for , := range .Imports {
.Imports[] = .ID
}
}
return json.Marshal()
}
// UnmarshalJSON reads in a Package from its JSON format.
// See MarshalJSON for details about the format accepted.
func ( *Package) ( []byte) error {
:= &flatPackage{}
if := json.Unmarshal(, &); != nil {
return
}
* = Package{
ID: .ID,
Name: .Name,
PkgPath: .PkgPath,
Errors: .Errors,
GoFiles: .GoFiles,
CompiledGoFiles: .CompiledGoFiles,
OtherFiles: .OtherFiles,
EmbedFiles: .EmbedFiles,
EmbedPatterns: .EmbedPatterns,
IgnoredFiles: .IgnoredFiles,
ExportFile: .ExportFile,
}
if len(.Imports) > 0 {
.Imports = make(map[string]*Package, len(.Imports))
for , := range .Imports {
.Imports[] = &Package{ID: }
}
}
return nil
}
func ( *Package) () string { return .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
preds []*loaderPackage // packages that import this one
unfinishedSuccs atomic.Int32 // number of direct imports not yet loaded
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
goVersion int // minor version number of go command on PATH
}
// loader holds the working state of a single call to load.
type loader struct {
pkgs map[string]*loaderPackage // keyed by Package.ID
Config
sizes types.Sizes // non-nil if needed by mode
parseCache map[string]*parseValue
parseCacheMu sync.Mutex
exportMu sync.Mutex // enforces mutual exclusion of exportdata operations
// Config.Mode contains the implied mode (see impliedLoadMode).
// Implied mode contains all the fields we need the data for.
// In requestedMode there are the actually requested fields.
// We'll zero them out before returning packages to the user.
// This makes it easier for us to get the conditions where
// we need certain modes right.
requestedMode LoadMode
}
type parseValue struct {
f *ast.File
err error
ready chan struct{}
}
func ( *Config) *loader {
:= &loader{
parseCache: map[string]*parseValue{},
}
if != nil {
.Config = *
// If the user has provided a logger, use it.
.Config.Logf = .Logf
}
if .Config.Logf == nil {
// If the GOPACKAGESDEBUG environment variable is set to true,
// but the user has not provided a logger, default to log.Printf.
if debug {
.Config.Logf = log.Printf
} else {
.Config.Logf = func( string, ...any) {}
}
}
if .Config.Mode == 0 {
.Config.Mode = NeedName | NeedFiles | NeedCompiledGoFiles // Preserve zero behavior of Mode for backwards compatibility.
}
if .Config.Env == nil {
.Config.Env = os.Environ()
}
if .Context == nil {
.Context = context.Background()
}
if .Dir == "" {
if , := os.Getwd(); == nil {
.Dir =
}
}
// Save the actually requested fields. We'll zero them out before returning packages to the user.
.requestedMode = .Mode
.Mode = impliedLoadMode(.Mode)
if .Mode&(NeedSyntax|NeedTypes|NeedTypesInfo) != 0 {
if .Fset == nil {
.Fset = token.NewFileSet()
}
// ParseFile is required even in LoadTypes mode
// because we load source if export data is missing.
if .ParseFile == nil {
.ParseFile = func( *token.FileSet, string, []byte) (*ast.File, error) {
// We implicitly promise to keep doing ast.Object resolution. :(
const = parser.AllErrors | parser.ParseComments
return parser.ParseFile(, , , )
}
}
}
return
}
// refine connects the supplied packages into a graph and then adds type
// and syntax information as requested by the LoadMode.
func ( *loader) ( *DriverResponse) ([]*Package, error) {
:= .Roots
:= make(map[string]int, len())
for , := range {
[] =
}
.pkgs = make(map[string]*loaderPackage)
// first pass, fixup and build the map and roots
var = make([]*loaderPackage, len())
for , := range .Packages {
:= -1
if , := [.ID]; {
=
}
// Overlays can invalidate export data.
// TODO(matloob): make this check fine-grained based on dependencies on overlaid files
:= len(.Overlay) > 0 || .ExportFile == "" && .PkgPath != "unsafe"
// This package needs type information if the caller requested types and the package is
// either a root, or it's a non-root and the user requested dependencies ...
:= (.Mode&(NeedTypes|NeedTypesInfo) != 0 && ( >= 0 || .Mode&NeedDeps != 0))
// This package needs source if the call requested source (or types info, which implies source)
// and the package is either a root, or itas a non- root and the user requested dependencies...
:= ((.Mode&(NeedSyntax|NeedTypesInfo) != 0 && ( >= 0 || .Mode&NeedDeps != 0)) ||
// ... or if we need types and the exportData is invalid. We fall back to (incompletely)
// typechecking packages from source if they fail to compile.
(.Mode&(NeedTypes|NeedTypesInfo) != 0 && )) && .PkgPath != "unsafe"
:= &loaderPackage{
Package: ,
needtypes: ,
needsrc: ,
goVersion: .GoVersion,
}
.pkgs[.ID] =
if >= 0 {
[] =
.initial = true
}
}
for , := range {
if [] == nil {
return nil, fmt.Errorf("root package %v is missing", )
}
}
// Materialize the import graph if it is needed (NeedImports),
// or if we'll be using loadPackages (Need{Syntax|Types|TypesInfo}).
var []*loaderPackage // packages with no unfinished successors
if .Mode&(NeedImports|NeedSyntax|NeedTypes|NeedTypesInfo) != 0 {
const (
= 0 // new
= 1 // in progress
= 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 []*loaderPackage
var func(, *loaderPackage) bool
= func(, *loaderPackage) bool {
if .color == {
panic("internal error: grey node")
}
if .color == {
.color =
= append(, ) // push
:= .Imports // the structure form has only stubs with the ID in the Imports
.Imports = make(map[string]*Package, len())
for , := range {
var error
:= .pkgs[.ID]
if == nil {
// (includes package "C" when DisableCgo)
= fmt.Errorf("missing package: %q", .ID)
} else if .color == {
= fmt.Errorf("import cycle: %s", )
}
if != nil {
if .importErrors == nil {
.importErrors = make(map[string]error)
}
.importErrors[] =
continue
}
if (, ) {
.needsrc = true
}
.Imports[] = .Package
}
// -- postorder --
// Complete type information is required for the
// immediate dependencies of each source package.
if .needsrc && .Mode&NeedTypes != 0 {
for , := range .Imports {
.pkgs[.ID].needtypes = true
}
}
// NeedTypeSizes causes TypeSizes to be set even
// on packages for which types aren't needed.
if .Mode&NeedTypesSizes != 0 {
.TypesSizes = .sizes
}
// Add packages with no imports directly to the queue of leaves.
if len(.Imports) == 0 {
= append(, )
}
= [:len()-1] // pop
.color =
}
// Add edge from predecessor.
if != nil {
.unfinishedSuccs.Add(+1) // incref
.preds = append(.preds, )
}
return .needsrc
}
// For each initial package, create its import DAG.
for , := range {
(nil, )
}
} else {
// !NeedImports: drop the stub (ID-only) import packages
// that we are not even going to try to resolve.
for , := range {
.Imports = nil
}
}
// Load type data and syntax if needed, starting at
// the initial packages (roots of the import DAG).
if .Mode&(NeedSyntax|NeedTypes|NeedTypesInfo) != 0 {
// We avoid using g.SetLimit to limit concurrency as
// it makes g.Go stop accepting work, which prevents
// workers from enqeuing, and thus finishing, and thus
// allowing the group to make progress: deadlock.
//
// Instead we use the ioLimit and cpuLimit semaphores.
, := errgroup.WithContext(.Context)
// enqueues adds a package to the type-checking queue.
// It must have no unfinished successors.
var func(*loaderPackage)
= func( *loaderPackage) {
.Go(func() error {
// Parse and type-check.
.loadPackage()
// Notify each waiting predecessor,
// and enqueue it when it becomes a leaf.
for , := range .preds {
if .unfinishedSuccs.Add(-1) == 0 { // decref
()
}
}
return nil
})
}
// Load leaves first, adding new packages
// to the queue as they become leaves.
for , := range {
()
}
if := .Wait(); != nil {
return nil, // cancelled
}
}
// If the context is done, return its error and
// throw out [likely] incomplete packages.
if := .Context.Err(); != nil {
return nil,
}
:= make([]*Package, len())
for , := range {
[] = .Package
}
for := range .pkgs {
// Clear all unrequested fields,
// to catch programs that use more than they request.
if .requestedMode&NeedName == 0 {
.pkgs[].Name = ""
.pkgs[].PkgPath = ""
}
if .requestedMode&NeedFiles == 0 {
.pkgs[].GoFiles = nil
.pkgs[].OtherFiles = nil
.pkgs[].IgnoredFiles = nil
}
if .requestedMode&NeedEmbedFiles == 0 {
.pkgs[].EmbedFiles = nil
}
if .requestedMode&NeedEmbedPatterns == 0 {
.pkgs[].EmbedPatterns = nil
}
if .requestedMode&NeedCompiledGoFiles == 0 {
.pkgs[].CompiledGoFiles = nil
}
if .requestedMode&NeedImports == 0 {
.pkgs[].Imports = nil
}
if .requestedMode&NeedExportFile == 0 {
.pkgs[].ExportFile = ""
}
if .requestedMode&NeedTypes == 0 {
.pkgs[].Types = nil
.pkgs[].IllTyped = false
}
if .requestedMode&NeedSyntax == 0 {
.pkgs[].Syntax = nil
}
if .requestedMode&(NeedSyntax|NeedTypes|NeedTypesInfo) == 0 {
.pkgs[].Fset = nil
}
if .requestedMode&NeedTypesInfo == 0 {
.pkgs[].TypesInfo = nil
}
if .requestedMode&NeedTypesSizes == 0 {
.pkgs[].TypesSizes = nil
}
if .requestedMode&NeedModule == 0 {
.pkgs[].Module = nil
}
}
return , nil
}
// loadPackage loads/parses/typechecks the specified package.
// It must be called only once per Package,
// after immediate dependencies are loaded.
// Precondition: ld.Mode&(NeedSyntax|NeedTypes|NeedTypesInfo) != 0.
func ( *loader) ( *loaderPackage) {
if .PkgPath == "unsafe" {
// Fill in the blanks to avoid surprises.
.Types = types.Unsafe
.Fset = .Fset
.Syntax = []*ast.File{}
.TypesInfo = new(types.Info)
.TypesSizes = .sizes
return
}
// Call NewPackage directly with explicit name.
// This avoids skew between golist and go/types when the files'
// package declarations are inconsistent.
.Types = types.NewPackage(.PkgPath, .Name)
.Fset = .Fset
// Start shutting down if the context is done and do not load
// source or export data files.
// Packages that import this one will have ld.Context.Err() != nil.
// ld.Context.Err() will be returned later by refine.
if .Context.Err() != nil {
return
}
// 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.
// (Hence this return is after the Types assignment.)
// The Diamond test exercises this case.
if !.needtypes && !.needsrc {
return
}
// TODO(adonovan): this condition looks wrong:
// I think it should be lpkg.needtypes && !lpg.needsrc,
// so that NeedSyntax without NeedTypes can be satisfied by export data.
if !.needsrc {
if := .loadFromExportData(); != nil {
.Errors = append(.Errors, Error{
Pos: "-",
Msg: .Error(),
Kind: UnknownError, // e.g. can't find/open/parse export data
})
}
return // not a source package, don't get syntax trees
}
:= func( error) {
// Convert various error types into the one true Error.
var []Error
switch err := .(type) {
case Error:
// from driver
= append(, )
case *os.PathError:
// from parser
= append(, Error{
Pos: .Path + ":1",
Msg: .Err.Error(),
Kind: ParseError,
})
case scanner.ErrorList:
// from parser
for , := range {
= append(, Error{
Pos: .Pos.String(),
Msg: .Msg,
Kind: ParseError,
})
}
case types.Error:
// from type checker
.TypeErrors = append(.TypeErrors, )
= append(, Error{
Pos: .Fset.Position(.Pos).String(),
Msg: .Msg,
Kind: TypeError,
})
default:
// unexpected impoverished error from parser?
= append(, Error{
Pos: "-",
Msg: .Error(),
Kind: UnknownError,
})
// If you see this error message, please file a bug.
log.Printf("internal error: error %q (%T) without position", , )
}
.Errors = append(.Errors, ...)
}
// If the go command on the PATH is newer than the runtime,
// then the go/{scanner,ast,parser,types} packages from the
// standard library may be unable to process the files
// selected by go list.
//
// There is currently no way to downgrade the effective
// version of the go command (see issue 52078), so we proceed
// with the newer go command but, in case of parse or type
// errors, we emit an additional diagnostic.
//
// See:
// - golang.org/issue/52078 (flag to set release tags)
// - golang.org/issue/50825 (gopls legacy version support)
// - golang.org/issue/55883 (go/packages confusing error)
//
// Should we assert a hard minimum of (currently) go1.16 here?
var int
if , := fmt.Sscanf(runtime.Version(), "go1.%d", &); == nil && < .goVersion {
defer func() {
if len(.Errors) > 0 {
(Error{
Pos: "-",
Msg: fmt.Sprintf("This application uses version go1.%d of the source-processing packages but runs version go1.%d of 'go list'. It may fail to process source files that rely on newer language features. If so, rebuild the application using a newer version of Go.", , .goVersion),
Kind: UnknownError,
})
}
}()
}
if .Config.Mode&NeedTypes != 0 && len(.CompiledGoFiles) == 0 && .ExportFile != "" {
// The config requested loading sources and types, but sources are missing.
// Add an error to the package and fall back to loading from export data.
(Error{"-", fmt.Sprintf("sources missing for package %s", .ID), ParseError})
_ = .loadFromExportData() // ignore any secondary errors
return // can't get syntax trees for this package
}
, := .parseFiles(.CompiledGoFiles)
for , := range {
()
}
.Syntax =
if .Config.Mode&(NeedTypes|NeedTypesInfo) == 0 {
return
}
// Start shutting down if the context is done and do not type check.
// Packages that import this one will have ld.Context.Err() != nil.
// ld.Context.Err() will be returned later by refine.
if .Context.Err() != nil {
return
}
// Populate TypesInfo only if needed, as it
// causes the type checker to work much harder.
if .Config.Mode&NeedTypesInfo != 0 {
.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),
Instances: make(map[*ast.Ident]types.Instance),
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
FileVersions: make(map[*ast.File]string),
}
}
.TypesSizes = .sizes
:= importerFunc(func( string) (*types.Package, error) {
if == "unsafe" {
return types.Unsafe, nil
}
// The imports map is keyed by import path.
:= .Imports[]
if == nil {
if := .importErrors[]; != nil {
return nil,
}
// 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", )
}
if .Types != nil && .Types.Complete() {
return .Types, nil
}
log.Fatalf("internal error: package %q without types was imported from %q", , )
panic("unreachable")
})
// type-check
:= &types.Config{
Importer: ,
// Type-check bodies of functions only in initial packages.
// Example: for import graph A->B->C and initial packages {A,C},
// we can ignore function bodies in B.
IgnoreFuncBodies: .Mode&NeedDeps == 0 && !.initial,
Error: ,
Sizes: .sizes, // may be nil
}
if .Module != nil && .Module.GoVersion != "" {
.GoVersion = "go" + .Module.GoVersion
}
if (.Mode & typecheckCgo) != 0 {
if !typesinternal.SetUsesCgo() {
(Error{
Msg: "typecheckCgo requires Go 1.15+",
Kind: ListError,
})
return
}
}
// Type-checking is CPU intensive.
cpuLimit <- unit{} // acquire a token
defer func() { <-cpuLimit }() // release a token
:= types.NewChecker(, .Fset, .Types, .TypesInfo).Files(.Syntax)
.importErrors = nil // no longer needed
// In go/types go1.21 and go1.22, Checker.Files failed fast with a
// a "too new" error, without calling tc.Error and without
// proceeding to type-check the package (#66525).
// We rely on the runtimeVersion error to give the suggested remedy.
if != nil && len(.Errors) == 0 && len(.Syntax) > 0 {
if := .Error(); strings.HasPrefix(, "package requires newer Go version") {
(types.Error{
Fset: .Fset,
Pos: .Syntax[0].Package,
Msg: ,
})
}
}
// 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 .FakeImportC {
:
for , := range .Syntax {
for , := range .Imports {
if .Path.Value == `"C"` {
:= types.Error{Fset: .Fset, Pos: .Pos(), Msg: `import "C" ignored`}
()
break
}
}
}
}
// If types.Checker.Files had an error that was unreported,
// make sure to report the unknown error so the package is illTyped.
if != nil && len(.Errors) == 0 {
()
}
// Record accumulated errors.
:= len(.Errors) > 0
if ! {
for , := range .Imports {
if .IllTyped {
= true
break
}
}
}
.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 ( importerFunc) ( string) (*types.Package, error) { return () }
// We use a counting semaphore to limit
// the number of parallel I/O calls or CPU threads per process.
var (
ioLimit = make(chan unit, 20)
cpuLimit = make(chan unit, runtime.GOMAXPROCS(0))
)
func ( *loader) ( string) (*ast.File, error) {
.parseCacheMu.Lock()
, := .parseCache[]
if {
// cache hit
.parseCacheMu.Unlock()
<-.ready
} else {
// cache miss
= &parseValue{ready: make(chan struct{})}
.parseCache[] =
.parseCacheMu.Unlock()
var []byte
for , := range .Config.Overlay {
// TODO(adonovan): Inefficient for large overlays.
// Do an exact name-based map lookup
// (for nonexistent files) followed by a
// FileID-based map lookup (for existing ones).
if sameFile(, ) {
=
break
}
}
var error
if == nil {
ioLimit <- unit{} // acquire a token
, = os.ReadFile()
<-ioLimit // release a token
}
if != nil {
.err =
} else {
// Parsing is CPU intensive.
cpuLimit <- unit{} // acquire a token
.f, .err = .ParseFile(.Fset, , )
<-cpuLimit // release a token
}
close(.ready)
}
return .f, .err
}
// 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 ( *loader) ( []string) ([]*ast.File, []error) {
var (
= len()
= make([]*ast.File, )
= make([]error, )
)
var errgroup.Group
for , := range {
// This creates goroutines unnecessarily in the
// cache-hit case, but that case is uncommon.
.Go(func() error {
[], [] = .parseFile()
return nil
})
}
.Wait()
// Eliminate nils, preserving order.
var int
for , := range {
if != nil {
[] =
++
}
}
= [:]
= 0
for , := range {
if != nil {
[] =
++
}
}
= [:]
return ,
}
// sameFile returns true if x and y have the same basename and denote
// the same file.
func (, string) bool {
if == {
// It could be the case that y doesn't exist.
// For instance, it may be an overlay file that
// hasn't been written to disk. To handle that case
// let x == y through. (We added the exact absolute path
// string to the CompiledGoFiles list, so the unwritten
// overlay case implies x==y.)
return true
}
if strings.EqualFold(filepath.Base(), filepath.Base()) { // (optimisation)
if , := os.Stat(); == nil {
if , := os.Stat(); == nil {
return os.SameFile(, )
}
}
}
return false
}
// loadFromExportData ensures that type information is present for the specified
// package, loading it from an export data file on the first request.
// On success it sets lpkg.Types to a new Package.
func ( *loader) ( *loaderPackage) error {
if .PkgPath == "" {
log.Fatalf("internal error: Package %s has no PkgPath", )
}
// 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 lpkg.Types field and the
// types.Package it points to, for each loaderPackage 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.
.exportMu.Lock()
defer .exportMu.Unlock()
if := .Types; != nil && .Complete() {
return nil // cache hit
}
.IllTyped = true // fail safe
if .ExportFile == "" {
// Errors while building export data will have been printed to stderr.
return fmt.Errorf("no export data file")
}
, := os.Open(.ExportFile)
if != nil {
return
}
defer .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.)
, := gcexportdata.NewReader()
if != nil {
return fmt.Errorf("reading %s: %v", .ExportFile, )
}
// 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.
//
:= make(map[string]*types.Package) // view seen by gcexportdata
:= make(map[*loaderPackage]bool) // all visited packages
var func( map[string]*Package)
= func( map[string]*Package) {
for , := range {
:= .pkgs[.ID]
if ![] {
[] = true
[.PkgPath] = .Types
(.Imports)
}
}
}
(.Imports)
:= len() + 1 // adding the self package
// Parse the export data.
// (May modify incomplete packages in view but not create new ones.)
, := gcexportdata.Read(, .Fset, , .PkgPath)
if != nil {
return fmt.Errorf("reading %s: %v", .ExportFile, )
}
if , := ["go.shape"]; {
// Account for the pseudopackage "go.shape" that gets
// created by generic code.
++
}
if != len() {
log.Panicf("golang.org/x/tools/go/packages: unexpected new packages during load of %s", .PkgPath)
}
.Types =
.IllTyped = false
return nil
}
// impliedLoadMode returns loadMode with its dependencies.
func ( LoadMode) LoadMode {
if &(NeedDeps|NeedTypes|NeedTypesInfo) != 0 {
// All these things require knowing the import graph.
|= NeedImports
}
if &NeedTypes != 0 {
// Types require the GoVersion from Module.
|= NeedModule
}
return
}
func ( *Config) bool {
return .Mode&NeedExportFile != 0 || .Mode&NeedTypes != 0 && .Mode&NeedDeps == 0
}
type unit struct{}
The pages are generated with Golds v0.7.6. (GOOS=linux GOARCH=amd64)