update dependencies (#6267)

Signed-off-by: hongming <coder.scala@gmail.com>
(cherry picked from commit cfebd96a1f)
This commit is contained in:
hongming
2025-03-11 14:19:32 +08:00
parent 742c1e52db
commit 39eab5ee5c
4246 changed files with 341171 additions and 131193 deletions

4
vendor/golang.org/x/tools/LICENSE generated vendored
View File

@@ -1,4 +1,4 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
@@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

View File

@@ -1,657 +0,0 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer
// interface. Given the name of a (signed or unsigned) integer type T that has constants
// defined, stringer will create a new self-contained Go source file implementing
//
// func (t T) String() string
//
// The file is created in the same package and directory as the package that defines T.
// It has helpful defaults designed for use with go generate.
//
// Stringer works best with constants that are consecutive values such as created using iota,
// but creates good code regardless. In the future it might also provide custom support for
// constant sets that are bit patterns.
//
// For example, given this snippet,
//
// package painkiller
//
// type Pill int
//
// const (
// Placebo Pill = iota
// Aspirin
// Ibuprofen
// Paracetamol
// Acetaminophen = Paracetamol
// )
//
// running this command
//
// stringer -type=Pill
//
// in the same directory will create the file pill_string.go, in package painkiller,
// containing a definition of
//
// func (Pill) String() string
//
// That method will translate the value of a Pill constant to the string representation
// of the respective constant name, so that the call fmt.Print(painkiller.Aspirin) will
// print the string "Aspirin".
//
// Typically this process would be run using go generate, like this:
//
// //go:generate stringer -type=Pill
//
// If multiple constants have the same value, the lexically first matching name will
// be used (in the example, Acetaminophen will print as "Paracetamol").
//
// With no arguments, it processes the package in the current directory.
// Otherwise, the arguments must name a single directory holding a Go package
// or a set of Go source files that represent a single Go package.
//
// The -type flag accepts a comma-separated list of types so a single run can
// generate methods for multiple types. The default output file is t_string.go,
// where t is the lower-cased name of the first type listed. It can be overridden
// with the -output flag.
//
// The -linecomment flag tells stringer to generate the text of any line comment, trimmed
// of leading spaces, instead of the constant name. For instance, if the constants above had a
// Pill prefix, one could write
//
// PillAspirin // Aspirin
//
// to suppress it in the output.
package main // import "golang.org/x/tools/cmd/stringer"
import (
"bytes"
"flag"
"fmt"
"go/ast"
"go/constant"
"go/format"
"go/token"
"go/types"
"log"
"os"
"path/filepath"
"sort"
"strings"
"golang.org/x/tools/go/packages"
)
var (
typeNames = flag.String("type", "", "comma-separated list of type names; must be set")
output = flag.String("output", "", "output file name; default srcdir/<type>_string.go")
trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names")
linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present")
buildTags = flag.String("tags", "", "comma-separated list of build tags to apply")
)
// Usage is a replacement usage function for the flags package.
func Usage() {
fmt.Fprintf(os.Stderr, "Usage of stringer:\n")
fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T [directory]\n")
fmt.Fprintf(os.Stderr, "\tstringer [flags] -type T files... # Must be a single package\n")
fmt.Fprintf(os.Stderr, "For more information, see:\n")
fmt.Fprintf(os.Stderr, "\thttps://pkg.go.dev/golang.org/x/tools/cmd/stringer\n")
fmt.Fprintf(os.Stderr, "Flags:\n")
flag.PrintDefaults()
}
func main() {
log.SetFlags(0)
log.SetPrefix("stringer: ")
flag.Usage = Usage
flag.Parse()
if len(*typeNames) == 0 {
flag.Usage()
os.Exit(2)
}
types := strings.Split(*typeNames, ",")
var tags []string
if len(*buildTags) > 0 {
tags = strings.Split(*buildTags, ",")
}
// We accept either one directory or a list of files. Which do we have?
args := flag.Args()
if len(args) == 0 {
// Default: process whole package in current directory.
args = []string{"."}
}
// Parse the package once.
var dir string
g := Generator{
trimPrefix: *trimprefix,
lineComment: *linecomment,
}
// TODO(suzmue): accept other patterns for packages (directories, list of files, import paths, etc).
if len(args) == 1 && isDirectory(args[0]) {
dir = args[0]
} else {
if len(tags) != 0 {
log.Fatal("-tags option applies only to directories, not when files are specified")
}
dir = filepath.Dir(args[0])
}
g.parsePackage(args, tags)
// Print the header and package clause.
g.Printf("// Code generated by \"stringer %s\"; DO NOT EDIT.\n", strings.Join(os.Args[1:], " "))
g.Printf("\n")
g.Printf("package %s", g.pkg.name)
g.Printf("\n")
g.Printf("import \"strconv\"\n") // Used by all methods.
// Run generate for each type.
for _, typeName := range types {
g.generate(typeName)
}
// Format the output.
src := g.format()
// Write to file.
outputName := *output
if outputName == "" {
baseName := fmt.Sprintf("%s_string.go", types[0])
outputName = filepath.Join(dir, strings.ToLower(baseName))
}
err := os.WriteFile(outputName, src, 0644)
if err != nil {
log.Fatalf("writing output: %s", err)
}
}
// isDirectory reports whether the named file is a directory.
func isDirectory(name string) bool {
info, err := os.Stat(name)
if err != nil {
log.Fatal(err)
}
return info.IsDir()
}
// Generator holds the state of the analysis. Primarily used to buffer
// the output for format.Source.
type Generator struct {
buf bytes.Buffer // Accumulated output.
pkg *Package // Package we are scanning.
trimPrefix string
lineComment bool
}
func (g *Generator) Printf(format string, args ...interface{}) {
fmt.Fprintf(&g.buf, format, args...)
}
// File holds a single parsed file and associated data.
type File struct {
pkg *Package // Package to which this file belongs.
file *ast.File // Parsed AST.
// These fields are reset for each type being generated.
typeName string // Name of the constant type.
values []Value // Accumulator for constant values of that type.
trimPrefix string
lineComment bool
}
type Package struct {
name string
defs map[*ast.Ident]types.Object
files []*File
}
// parsePackage analyzes the single package constructed from the patterns and tags.
// parsePackage exits if there is an error.
func (g *Generator) parsePackage(patterns []string, tags []string) {
cfg := &packages.Config{
Mode: packages.NeedName | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax,
// TODO: Need to think about constants in test files. Maybe write type_string_test.go
// in a separate pass? For later.
Tests: false,
BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))},
}
pkgs, err := packages.Load(cfg, patterns...)
if err != nil {
log.Fatal(err)
}
if len(pkgs) != 1 {
log.Fatalf("error: %d packages found", len(pkgs))
}
g.addPackage(pkgs[0])
}
// addPackage adds a type checked Package and its syntax files to the generator.
func (g *Generator) addPackage(pkg *packages.Package) {
g.pkg = &Package{
name: pkg.Name,
defs: pkg.TypesInfo.Defs,
files: make([]*File, len(pkg.Syntax)),
}
for i, file := range pkg.Syntax {
g.pkg.files[i] = &File{
file: file,
pkg: g.pkg,
trimPrefix: g.trimPrefix,
lineComment: g.lineComment,
}
}
}
// generate produces the String method for the named type.
func (g *Generator) generate(typeName string) {
values := make([]Value, 0, 100)
for _, file := range g.pkg.files {
// Set the state for this run of the walker.
file.typeName = typeName
file.values = nil
if file.file != nil {
ast.Inspect(file.file, file.genDecl)
values = append(values, file.values...)
}
}
if len(values) == 0 {
log.Fatalf("no values defined for type %s", typeName)
}
// Generate code that will fail if the constants change value.
g.Printf("func _() {\n")
g.Printf("\t// An \"invalid array index\" compiler error signifies that the constant values have changed.\n")
g.Printf("\t// Re-run the stringer command to generate them again.\n")
g.Printf("\tvar x [1]struct{}\n")
for _, v := range values {
g.Printf("\t_ = x[%s - %s]\n", v.originalName, v.str)
}
g.Printf("}\n")
runs := splitIntoRuns(values)
// The decision of which pattern to use depends on the number of
// runs in the numbers. If there's only one, it's easy. For more than
// one, there's a tradeoff between complexity and size of the data
// and code vs. the simplicity of a map. A map takes more space,
// but so does the code. The decision here (crossover at 10) is
// arbitrary, but considers that for large numbers of runs the cost
// of the linear scan in the switch might become important, and
// rather than use yet another algorithm such as binary search,
// we punt and use a map. In any case, the likelihood of a map
// being necessary for any realistic example other than bitmasks
// is very low. And bitmasks probably deserve their own analysis,
// to be done some other day.
switch {
case len(runs) == 1:
g.buildOneRun(runs, typeName)
case len(runs) <= 10:
g.buildMultipleRuns(runs, typeName)
default:
g.buildMap(runs, typeName)
}
}
// splitIntoRuns breaks the values into runs of contiguous sequences.
// For example, given 1,2,3,5,6,7 it returns {1,2,3},{5,6,7}.
// The input slice is known to be non-empty.
func splitIntoRuns(values []Value) [][]Value {
// We use stable sort so the lexically first name is chosen for equal elements.
sort.Stable(byValue(values))
// Remove duplicates. Stable sort has put the one we want to print first,
// so use that one. The String method won't care about which named constant
// was the argument, so the first name for the given value is the only one to keep.
// We need to do this because identical values would cause the switch or map
// to fail to compile.
j := 1
for i := 1; i < len(values); i++ {
if values[i].value != values[i-1].value {
values[j] = values[i]
j++
}
}
values = values[:j]
runs := make([][]Value, 0, 10)
for len(values) > 0 {
// One contiguous sequence per outer loop.
i := 1
for i < len(values) && values[i].value == values[i-1].value+1 {
i++
}
runs = append(runs, values[:i])
values = values[i:]
}
return runs
}
// format returns the gofmt-ed contents of the Generator's buffer.
func (g *Generator) format() []byte {
src, err := format.Source(g.buf.Bytes())
if err != nil {
// Should never happen, but can arise when developing this code.
// The user can compile the output to see the error.
log.Printf("warning: internal error: invalid Go generated: %s", err)
log.Printf("warning: compile the package to analyze the error")
return g.buf.Bytes()
}
return src
}
// Value represents a declared constant.
type Value struct {
originalName string // The name of the constant.
name string // The name with trimmed prefix.
// The value is stored as a bit pattern alone. The boolean tells us
// whether to interpret it as an int64 or a uint64; the only place
// this matters is when sorting.
// Much of the time the str field is all we need; it is printed
// by Value.String.
value uint64 // Will be converted to int64 when needed.
signed bool // Whether the constant is a signed type.
str string // The string representation given by the "go/constant" package.
}
func (v *Value) String() string {
return v.str
}
// byValue lets us sort the constants into increasing order.
// We take care in the Less method to sort in signed or unsigned order,
// as appropriate.
type byValue []Value
func (b byValue) Len() int { return len(b) }
func (b byValue) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b byValue) Less(i, j int) bool {
if b[i].signed {
return int64(b[i].value) < int64(b[j].value)
}
return b[i].value < b[j].value
}
// genDecl processes one declaration clause.
func (f *File) genDecl(node ast.Node) bool {
decl, ok := node.(*ast.GenDecl)
if !ok || decl.Tok != token.CONST {
// We only care about const declarations.
return true
}
// The name of the type of the constants we are declaring.
// Can change if this is a multi-element declaration.
typ := ""
// Loop over the elements of the declaration. Each element is a ValueSpec:
// a list of names possibly followed by a type, possibly followed by values.
// If the type and value are both missing, we carry down the type (and value,
// but the "go/types" package takes care of that).
for _, spec := range decl.Specs {
vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST.
if vspec.Type == nil && len(vspec.Values) > 0 {
// "X = 1". With no type but a value. If the constant is untyped,
// skip this vspec and reset the remembered type.
typ = ""
// If this is a simple type conversion, remember the type.
// We don't mind if this is actually a call; a qualified call won't
// be matched (that will be SelectorExpr, not Ident), and only unusual
// situations will result in a function call that appears to be
// a type conversion.
ce, ok := vspec.Values[0].(*ast.CallExpr)
if !ok {
continue
}
id, ok := ce.Fun.(*ast.Ident)
if !ok {
continue
}
typ = id.Name
}
if vspec.Type != nil {
// "X T". We have a type. Remember it.
ident, ok := vspec.Type.(*ast.Ident)
if !ok {
continue
}
typ = ident.Name
}
if typ != f.typeName {
// This is not the type we're looking for.
continue
}
// We now have a list of names (from one line of source code) all being
// declared with the desired type.
// Grab their names and actual values and store them in f.values.
for _, name := range vspec.Names {
if name.Name == "_" {
continue
}
// This dance lets the type checker find the values for us. It's a
// bit tricky: look up the object declared by the name, find its
// types.Const, and extract its value.
obj, ok := f.pkg.defs[name]
if !ok {
log.Fatalf("no value for constant %s", name)
}
info := obj.Type().Underlying().(*types.Basic).Info()
if info&types.IsInteger == 0 {
log.Fatalf("can't handle non-integer constant type %s", typ)
}
value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST.
if value.Kind() != constant.Int {
log.Fatalf("can't happen: constant is not an integer %s", name)
}
i64, isInt := constant.Int64Val(value)
u64, isUint := constant.Uint64Val(value)
if !isInt && !isUint {
log.Fatalf("internal error: value of %s is not an integer: %s", name, value.String())
}
if !isInt {
u64 = uint64(i64)
}
v := Value{
originalName: name.Name,
value: u64,
signed: info&types.IsUnsigned == 0,
str: value.String(),
}
if c := vspec.Comment; f.lineComment && c != nil && len(c.List) == 1 {
v.name = strings.TrimSpace(c.Text())
} else {
v.name = strings.TrimPrefix(v.originalName, f.trimPrefix)
}
f.values = append(f.values, v)
}
}
return false
}
// Helpers
// usize returns the number of bits of the smallest unsigned integer
// type that will hold n. Used to create the smallest possible slice of
// integers to use as indexes into the concatenated strings.
func usize(n int) int {
switch {
case n < 1<<8:
return 8
case n < 1<<16:
return 16
default:
// 2^32 is enough constants for anyone.
return 32
}
}
// declareIndexAndNameVars declares the index slices and concatenated names
// strings representing the runs of values.
func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) {
var indexes, names []string
for i, run := range runs {
index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i))
if len(run) != 1 {
indexes = append(indexes, index)
}
names = append(names, name)
}
g.Printf("const (\n")
for _, name := range names {
g.Printf("\t%s\n", name)
}
g.Printf(")\n\n")
if len(indexes) > 0 {
g.Printf("var (")
for _, index := range indexes {
g.Printf("\t%s\n", index)
}
g.Printf(")\n\n")
}
}
// declareIndexAndNameVar is the single-run version of declareIndexAndNameVars
func (g *Generator) declareIndexAndNameVar(run []Value, typeName string) {
index, name := g.createIndexAndNameDecl(run, typeName, "")
g.Printf("const %s\n", name)
g.Printf("var %s\n", index)
}
// createIndexAndNameDecl returns the pair of declarations for the run. The caller will add "const" and "var".
func (g *Generator) createIndexAndNameDecl(run []Value, typeName string, suffix string) (string, string) {
b := new(bytes.Buffer)
indexes := make([]int, len(run))
for i := range run {
b.WriteString(run[i].name)
indexes[i] = b.Len()
}
nameConst := fmt.Sprintf("_%s_name%s = %q", typeName, suffix, b.String())
nameLen := b.Len()
b.Reset()
fmt.Fprintf(b, "_%s_index%s = [...]uint%d{0, ", typeName, suffix, usize(nameLen))
for i, v := range indexes {
if i > 0 {
fmt.Fprintf(b, ", ")
}
fmt.Fprintf(b, "%d", v)
}
fmt.Fprintf(b, "}")
return b.String(), nameConst
}
// declareNameVars declares the concatenated names string representing all the values in the runs.
func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix string) {
g.Printf("const _%s_name%s = \"", typeName, suffix)
for _, run := range runs {
for i := range run {
g.Printf("%s", run[i].name)
}
}
g.Printf("\"\n")
}
// buildOneRun generates the variables and String method for a single run of contiguous values.
func (g *Generator) buildOneRun(runs [][]Value, typeName string) {
values := runs[0]
g.Printf("\n")
g.declareIndexAndNameVar(values, typeName)
// The generated code is simple enough to write as a Printf format.
lessThanZero := ""
if values[0].signed {
lessThanZero = "i < 0 || "
}
if values[0].value == 0 { // Signed or unsigned, 0 is still 0.
g.Printf(stringOneRun, typeName, usize(len(values)), lessThanZero)
} else {
g.Printf(stringOneRunWithOffset, typeName, values[0].String(), usize(len(values)), lessThanZero)
}
}
// Arguments to format are:
//
// [1]: type name
// [2]: size of index element (8 for uint8 etc.)
// [3]: less than zero check (for signed types)
const stringOneRun = `func (i %[1]s) String() string {
if %[3]si >= %[1]s(len(_%[1]s_index)-1) {
return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _%[1]s_name[_%[1]s_index[i]:_%[1]s_index[i+1]]
}
`
// Arguments to format are:
// [1]: type name
// [2]: lowest defined value for type, as a string
// [3]: size of index element (8 for uint8 etc.)
// [4]: less than zero check (for signed types)
/*
*/
const stringOneRunWithOffset = `func (i %[1]s) String() string {
i -= %[2]s
if %[4]si >= %[1]s(len(_%[1]s_index)-1) {
return "%[1]s(" + strconv.FormatInt(int64(i + %[2]s), 10) + ")"
}
return _%[1]s_name[_%[1]s_index[i] : _%[1]s_index[i+1]]
}
`
// buildMultipleRuns generates the variables and String method for multiple runs of contiguous values.
// For this pattern, a single Printf format won't do.
func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) {
g.Printf("\n")
g.declareIndexAndNameVars(runs, typeName)
g.Printf("func (i %s) String() string {\n", typeName)
g.Printf("\tswitch {\n")
for i, values := range runs {
if len(values) == 1 {
g.Printf("\tcase i == %s:\n", &values[0])
g.Printf("\t\treturn _%s_name_%d\n", typeName, i)
continue
}
if values[0].value == 0 && !values[0].signed {
// For an unsigned lower bound of 0, "0 <= i" would be redundant.
g.Printf("\tcase i <= %s:\n", &values[len(values)-1])
} else {
g.Printf("\tcase %s <= i && i <= %s:\n", &values[0], &values[len(values)-1])
}
if values[0].value != 0 {
g.Printf("\t\ti -= %s\n", &values[0])
}
g.Printf("\t\treturn _%s_name_%d[_%s_index_%d[i]:_%s_index_%d[i+1]]\n",
typeName, i, typeName, i, typeName, i)
}
g.Printf("\tdefault:\n")
g.Printf("\t\treturn \"%s(\" + strconv.FormatInt(int64(i), 10) + \")\"\n", typeName)
g.Printf("\t}\n")
g.Printf("}\n")
}
// buildMap handles the case where the space is so sparse a map is a reasonable fallback.
// It's a rare situation but has simple code.
func (g *Generator) buildMap(runs [][]Value, typeName string) {
g.Printf("\n")
g.declareNameVars(runs, typeName, "")
g.Printf("\nvar _%s_map = map[%s]string{\n", typeName, typeName)
n := 0
for _, values := range runs {
for _, value := range values {
g.Printf("\t%s: _%s_name[%d:%d],\n", &value, typeName, n, n+len(value.name))
n += len(value.name)
}
}
g.Printf("}\n\n")
g.Printf(stringMap, typeName)
}
// Argument to format is the type name.
const stringMap = `func (i %[1]s) String() string {
if str, ok := _%[1]s_map[i]; ok {
return str
}
return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")"
}
`

266
vendor/golang.org/x/tools/cover/profile.go generated vendored Normal file
View File

@@ -0,0 +1,266 @@
// Copyright 2013 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 cover provides support for parsing coverage profiles
// generated by "go test -coverprofile=cover.out".
package cover // import "golang.org/x/tools/cover"
import (
"bufio"
"errors"
"fmt"
"io"
"math"
"os"
"sort"
"strconv"
"strings"
)
// Profile represents the profiling data for a specific file.
type Profile struct {
FileName string
Mode string
Blocks []ProfileBlock
}
// ProfileBlock represents a single block of profiling data.
type ProfileBlock struct {
StartLine, StartCol int
EndLine, EndCol int
NumStmt, Count int
}
type byFileName []*Profile
func (p byFileName) Len() int { return len(p) }
func (p byFileName) Less(i, j int) bool { return p[i].FileName < p[j].FileName }
func (p byFileName) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// ParseProfiles parses profile data in the specified file and returns a
// Profile for each source file described therein.
func ParseProfiles(fileName string) ([]*Profile, error) {
pf, err := os.Open(fileName)
if err != nil {
return nil, err
}
defer pf.Close()
return ParseProfilesFromReader(pf)
}
// ParseProfilesFromReader parses profile data from the Reader and
// returns a Profile for each source file described therein.
func ParseProfilesFromReader(rd io.Reader) ([]*Profile, error) {
// First line is "mode: foo", where foo is "set", "count", or "atomic".
// Rest of file is in the format
// encoding/base64/base64.go:34.44,37.40 3 1
// where the fields are: name.go:line.column,line.column numberOfStatements count
files := make(map[string]*Profile)
s := bufio.NewScanner(rd)
mode := ""
for s.Scan() {
line := s.Text()
if mode == "" {
const p = "mode: "
if !strings.HasPrefix(line, p) || line == p {
return nil, fmt.Errorf("bad mode line: %v", line)
}
mode = line[len(p):]
continue
}
fn, b, err := parseLine(line)
if err != nil {
return nil, fmt.Errorf("line %q doesn't match expected format: %v", line, err)
}
p := files[fn]
if p == nil {
p = &Profile{
FileName: fn,
Mode: mode,
}
files[fn] = p
}
p.Blocks = append(p.Blocks, b)
}
if err := s.Err(); err != nil {
return nil, err
}
for _, p := range files {
sort.Sort(blocksByStart(p.Blocks))
// Merge samples from the same location.
j := 1
for i := 1; i < len(p.Blocks); i++ {
b := p.Blocks[i]
last := p.Blocks[j-1]
if b.StartLine == last.StartLine &&
b.StartCol == last.StartCol &&
b.EndLine == last.EndLine &&
b.EndCol == last.EndCol {
if b.NumStmt != last.NumStmt {
return nil, fmt.Errorf("inconsistent NumStmt: changed from %d to %d", last.NumStmt, b.NumStmt)
}
if mode == "set" {
p.Blocks[j-1].Count |= b.Count
} else {
p.Blocks[j-1].Count += b.Count
}
continue
}
p.Blocks[j] = b
j++
}
p.Blocks = p.Blocks[:j]
}
// Generate a sorted slice.
profiles := make([]*Profile, 0, len(files))
for _, profile := range files {
profiles = append(profiles, profile)
}
sort.Sort(byFileName(profiles))
return profiles, nil
}
// parseLine parses a line from a coverage file.
// It is equivalent to the regex
// ^(.+):([0-9]+)\.([0-9]+),([0-9]+)\.([0-9]+) ([0-9]+) ([0-9]+)$
//
// However, it is much faster: https://golang.org/cl/179377
func parseLine(l string) (fileName string, block ProfileBlock, err error) {
end := len(l)
b := ProfileBlock{}
b.Count, end, err = seekBack(l, ' ', end, "Count")
if err != nil {
return "", b, err
}
b.NumStmt, end, err = seekBack(l, ' ', end, "NumStmt")
if err != nil {
return "", b, err
}
b.EndCol, end, err = seekBack(l, '.', end, "EndCol")
if err != nil {
return "", b, err
}
b.EndLine, end, err = seekBack(l, ',', end, "EndLine")
if err != nil {
return "", b, err
}
b.StartCol, end, err = seekBack(l, '.', end, "StartCol")
if err != nil {
return "", b, err
}
b.StartLine, end, err = seekBack(l, ':', end, "StartLine")
if err != nil {
return "", b, err
}
fn := l[0:end]
if fn == "" {
return "", b, errors.New("a FileName cannot be blank")
}
return fn, b, nil
}
// seekBack searches backwards from end to find sep in l, then returns the
// value between sep and end as an integer.
// If seekBack fails, the returned error will reference what.
func seekBack(l string, sep byte, end int, what string) (value int, nextSep int, err error) {
// Since we're seeking backwards and we know only ASCII is legal for these values,
// we can ignore the possibility of non-ASCII characters.
for start := end - 1; start >= 0; start-- {
if l[start] == sep {
i, err := strconv.Atoi(l[start+1 : end])
if err != nil {
return 0, 0, fmt.Errorf("couldn't parse %q: %v", what, err)
}
if i < 0 {
return 0, 0, fmt.Errorf("negative values are not allowed for %s, found %d", what, i)
}
return i, start, nil
}
}
return 0, 0, fmt.Errorf("couldn't find a %s before %s", string(sep), what)
}
type blocksByStart []ProfileBlock
func (b blocksByStart) Len() int { return len(b) }
func (b blocksByStart) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b blocksByStart) Less(i, j int) bool {
bi, bj := b[i], b[j]
return bi.StartLine < bj.StartLine || bi.StartLine == bj.StartLine && bi.StartCol < bj.StartCol
}
// Boundary represents the position in a source file of the beginning or end of a
// block as reported by the coverage profile. In HTML mode, it will correspond to
// the opening or closing of a <span> tag and will be used to colorize the source
type Boundary struct {
Offset int // Location as a byte offset in the source file.
Start bool // Is this the start of a block?
Count int // Event count from the cover profile.
Norm float64 // Count normalized to [0..1].
Index int // Order in input file.
}
// Boundaries returns a Profile as a set of Boundary objects within the provided src.
func (p *Profile) Boundaries(src []byte) (boundaries []Boundary) {
// Find maximum count.
max := 0
for _, b := range p.Blocks {
if b.Count > max {
max = b.Count
}
}
// Divisor for normalization.
divisor := math.Log(float64(max))
// boundary returns a Boundary, populating the Norm field with a normalized Count.
index := 0
boundary := func(offset int, start bool, count int) Boundary {
b := Boundary{Offset: offset, Start: start, Count: count, Index: index}
index++
if !start || count == 0 {
return b
}
if max <= 1 {
b.Norm = 0.8 // Profile is in"set" mode; we want a heat map. Use cov8 in the CSS.
} else if count > 0 {
b.Norm = math.Log(float64(count)) / divisor
}
return b
}
line, col := 1, 2 // TODO: Why is this 2?
for si, bi := 0, 0; si < len(src) && bi < len(p.Blocks); {
b := p.Blocks[bi]
if b.StartLine == line && b.StartCol == col {
boundaries = append(boundaries, boundary(si, true, b.Count))
}
if b.EndLine == line && b.EndCol == col || line > b.EndLine {
boundaries = append(boundaries, boundary(si, false, 0))
bi++
continue // Don't advance through src; maybe the next block starts here.
}
if src[si] == '\n' {
line++
col = 0
}
col++
si++
}
sort.Sort(boundariesByPos(boundaries))
return
}
type boundariesByPos []Boundary
func (b boundariesByPos) Len() int { return len(b) }
func (b boundariesByPos) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b boundariesByPos) Less(i, j int) bool {
if b[i].Offset == b[j].Offset {
// Boundaries at the same offset should be ordered according to
// their original position.
return b[i].Index < b[j].Index
}
return b[i].Offset < b[j].Offset
}

View File

@@ -11,8 +11,6 @@ import (
"go/ast"
"go/token"
"sort"
"golang.org/x/tools/internal/typeparams"
)
// PathEnclosingInterval returns the node that encloses the source
@@ -108,8 +106,21 @@ func PathEnclosingInterval(root *ast.File, start, end token.Pos) (path []ast.Nod
// Does augmented child strictly contain [start, end)?
if augPos <= start && end <= augEnd {
_, isToken := child.(tokenNode)
return isToken || visit(child)
if is[tokenNode](child) {
return true
}
// childrenOf elides the FuncType node beneath FuncDecl.
// Add it back here for TypeParams, Params, Results,
// all FieldLists). But we don't add it back for the "func" token
// even though it is is the tree at FuncDecl.Type.Func.
if decl, ok := node.(*ast.FuncDecl); ok {
if fields, ok := child.(*ast.FieldList); ok && fields != decl.Recv {
path = append(path, decl.Type)
}
}
return visit(child)
}
// Does [start, end) overlap multiple children?
@@ -315,6 +326,8 @@ func childrenOf(n ast.Node) []ast.Node {
//
// As a workaround, we inline the case for FuncType
// here and order things correctly.
// We also need to insert the elided FuncType just
// before the 'visit' recursion.
//
children = nil // discard ast.Walk(FuncDecl) info subtrees
children = append(children, tok(n.Type.Func, len("func")))
@@ -322,7 +335,7 @@ func childrenOf(n ast.Node) []ast.Node {
children = append(children, n.Recv)
}
children = append(children, n.Name)
if tparams := typeparams.ForFuncType(n.Type); tparams != nil {
if tparams := n.Type.TypeParams; tparams != nil {
children = append(children, tparams)
}
if n.Type.Params != nil {
@@ -377,7 +390,7 @@ func childrenOf(n ast.Node) []ast.Node {
tok(n.Lbrack, len("[")),
tok(n.Rbrack, len("]")))
case *typeparams.IndexListExpr:
case *ast.IndexListExpr:
children = append(children,
tok(n.Lbrack, len("[")),
tok(n.Rbrack, len("]")))
@@ -588,7 +601,7 @@ func NodeDescription(n ast.Node) string {
return "decrement statement"
case *ast.IndexExpr:
return "index expression"
case *typeparams.IndexListExpr:
case *ast.IndexListExpr:
return "index list expression"
case *ast.InterfaceType:
return "interface type"
@@ -634,3 +647,8 @@ func NodeDescription(n ast.Node) string {
}
panic(fmt.Sprintf("unexpected node type: %T", n))
}
func is[T any](x any) bool {
_, ok := x.(T)
return ok
}

View File

@@ -9,8 +9,6 @@ import (
"go/ast"
"reflect"
"sort"
"golang.org/x/tools/internal/typeparams"
)
// An ApplyFunc is invoked by Apply for each node n, even if n is nil,
@@ -252,7 +250,7 @@ func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast.
a.apply(n, "X", nil, n.X)
a.apply(n, "Index", nil, n.Index)
case *typeparams.IndexListExpr:
case *ast.IndexListExpr:
a.apply(n, "X", nil, n.X)
a.applyList(n, "Indices")
@@ -293,7 +291,7 @@ func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast.
a.apply(n, "Fields", nil, n.Fields)
case *ast.FuncType:
if tparams := typeparams.ForFuncType(n); tparams != nil {
if tparams := n.TypeParams; tparams != nil {
a.apply(n, "TypeParams", nil, tparams)
}
a.apply(n, "Params", nil, n.Params)
@@ -408,7 +406,7 @@ func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast.
case *ast.TypeSpec:
a.apply(n, "Doc", nil, n.Doc)
a.apply(n, "Name", nil, n.Name)
if tparams := typeparams.ForTypeSpec(n); tparams != nil {
if tparams := n.TypeParams; tparams != nil {
a.apply(n, "TypeParams", nil, tparams)
}
a.apply(n, "Type", nil, n.Type)

View File

@@ -7,12 +7,5 @@ package astutil
import "go/ast"
// Unparen returns e with any enclosing parentheses stripped.
func Unparen(e ast.Expr) ast.Expr {
for {
p, ok := e.(*ast.ParenExpr)
if !ok {
return e
}
e = p.X
}
}
// Deprecated: use [ast.Unparen].
func Unparen(e ast.Expr) ast.Expr { return ast.Unparen(e) }

View File

@@ -64,14 +64,24 @@ type event struct {
// depth-first order. It calls f(n) for each node n before it visits
// n's children.
//
// The complete traversal sequence is determined by ast.Inspect.
// The types argument, if non-empty, enables type-based filtering of
// events. The function f if is called only for nodes whose type
// events. The function f is called only for nodes whose type
// matches an element of the types slice.
func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) {
// Because it avoids postorder calls to f, and the pruning
// check, Preorder is almost twice as fast as Nodes. The two
// features seem to contribute similar slowdowns (~1.4x each).
// This function is equivalent to the PreorderSeq call below,
// but to avoid the additional dynamic call (which adds 13-35%
// to the benchmarks), we expand it out.
//
// in.PreorderSeq(types...)(func(n ast.Node) bool {
// f(n)
// return true
// })
mask := maskOf(types)
for i := 0; i < len(in.events); {
ev := in.events[i]
@@ -97,6 +107,7 @@ func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) {
// of the non-nil children of the node, followed by a call of
// f(n, false).
//
// The complete traversal sequence is determined by ast.Inspect.
// The types argument, if non-empty, enables type-based filtering of
// events. The function f if is called only for nodes whose type
// matches an element of the types slice.

85
vendor/golang.org/x/tools/go/ast/inspector/iter.go generated vendored Normal file
View File

@@ -0,0 +1,85 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.23
package inspector
import (
"go/ast"
"iter"
)
// PreorderSeq returns an iterator that visits all the
// nodes of the files supplied to New in depth-first order.
// It visits each node n before n's children.
// The complete traversal sequence is determined by ast.Inspect.
//
// The types argument, if non-empty, enables type-based
// filtering of events: only nodes whose type matches an
// element of the types slice are included in the sequence.
func (in *Inspector) PreorderSeq(types ...ast.Node) iter.Seq[ast.Node] {
// This implementation is identical to Preorder,
// except that it supports breaking out of the loop.
return func(yield func(ast.Node) bool) {
mask := maskOf(types)
for i := 0; i < len(in.events); {
ev := in.events[i]
if ev.index > i {
// push
if ev.typ&mask != 0 {
if !yield(ev.node) {
break
}
}
pop := ev.index
if in.events[pop].typ&mask == 0 {
// Subtrees do not contain types: skip them and pop.
i = pop + 1
continue
}
}
i++
}
}
}
// All[N] returns an iterator over all the nodes of type N.
// N must be a pointer-to-struct type that implements ast.Node.
//
// Example:
//
// for call := range All[*ast.CallExpr](in) { ... }
func All[N interface {
*S
ast.Node
}, S any](in *Inspector) iter.Seq[N] {
// To avoid additional dynamic call overheads,
// we duplicate rather than call the logic of PreorderSeq.
mask := typeOf((N)(nil))
return func(yield func(N) bool) {
for i := 0; i < len(in.events); {
ev := in.events[i]
if ev.index > i {
// push
if ev.typ&mask != 0 {
if !yield(ev.node.(N)) {
break
}
}
pop := ev.index
if in.events[pop].typ&mask == 0 {
// Subtrees do not contain types: skip them and pop.
i = pop + 1
continue
}
}
i++
}
}
}

View File

@@ -12,8 +12,6 @@ package inspector
import (
"go/ast"
"math"
"golang.org/x/tools/internal/typeparams"
)
const (
@@ -171,7 +169,7 @@ func typeOf(n ast.Node) uint64 {
return 1 << nIncDecStmt
case *ast.IndexExpr:
return 1 << nIndexExpr
case *typeparams.IndexListExpr:
case *ast.IndexListExpr:
return 1 << nIndexListExpr
case *ast.InterfaceType:
return 1 << nInterfaceType

View File

@@ -47,7 +47,7 @@ import (
func Find(importPath, srcDir string) (filename, path string) {
cmd := exec.Command("go", "list", "-json", "-export", "--", importPath)
cmd.Dir = srcDir
out, err := cmd.CombinedOutput()
out, err := cmd.Output()
if err != nil {
return "", ""
}
@@ -128,15 +128,14 @@ func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package,
// (from "version"). Select appropriate importer.
if len(data) > 0 {
switch data[0] {
case 'i':
case 'v', 'c', 'd': // binary, till go1.10
return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0])
case 'i': // indexed, till go1.19
_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
return pkg, err
case 'v', 'c', 'd':
_, pkg, err := gcimporter.BImportData(fset, imports, data, path)
return pkg, err
case 'u':
case 'u': // unified, from go1.20
_, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path)
return pkg, err

View File

@@ -1,49 +0,0 @@
// 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 packagesdriver fetches type sizes for go/packages and go/analysis.
package packagesdriver
import (
"context"
"fmt"
"go/types"
"strings"
"golang.org/x/tools/internal/gocommand"
)
var debug = false
func GetSizesGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (types.Sizes, error) {
inv.Verb = "list"
inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"}
stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv)
var goarch, compiler string
if rawErr != nil {
if rawErrMsg := rawErr.Error(); strings.Contains(rawErrMsg, "cannot find main module") || strings.Contains(rawErrMsg, "go.mod file not found") {
// User's running outside of a module. All bets are off. Get GOARCH and guess compiler is gc.
// TODO(matloob): Is this a problem in practice?
inv.Verb = "env"
inv.Args = []string{"GOARCH"}
envout, enverr := gocmdRunner.Run(ctx, inv)
if enverr != nil {
return nil, enverr
}
goarch = strings.TrimSpace(envout.String())
compiler = "gc"
} else {
return nil, friendlyErr
}
} else {
fields := strings.Fields(stdout.String())
if len(fields) < 2 {
return nil, fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>",
stdout.String(), stderr.String())
}
goarch = fields[0]
compiler = fields[1]
}
return types.SizesFor(compiler, goarch), nil
}

View File

@@ -5,12 +5,20 @@
/*
Package packages loads Go packages for inspection and analysis.
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.
The [Load] function takes as input a list of patterns and returns a
list of [Package] values describing individual packages matched by those
patterns.
A [Config] specifies configuration options, the most important of which is
the [LoadMode], which controls the amount of detail in the loaded packages.
Load passes most patterns directly to the underlying build tool,
but all patterns with the prefix "query=", where query is a
Load passes most patterns directly to the underlying build tool.
The default build tool is the go command.
Its supported patterns are described at
https://pkg.go.dev/cmd/go#hdr-Package_lists_and_patterns.
Other build systems may be supported by providing a "driver";
see [The driver protocol].
All patterns with the prefix "query=", where query is a
non-empty string of letters from [a-z], are reserved and may be
interpreted as query operators.
@@ -35,7 +43,7 @@ The Package struct provides basic information about the package, including
- 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.
- TypesInfo, the result of a complete type-check of the package syntax trees.
(See the documentation for type Package for the complete list of fields
and more detailed descriptions.)
@@ -56,7 +64,7 @@ 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.
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
@@ -64,9 +72,40 @@ reported about the loaded packages. See the documentation for type LoadMode
for details.
Most tools should pass their command-line arguments (after any flags)
uninterpreted to the loader, so that the loader can interpret them
uninterpreted to Load, so that it can interpret them
according to the conventions of the underlying build system.
See the Example function for typical usage.
# The driver protocol
Load may be used to load Go packages even in Go projects that use
alternative build systems, by installing an appropriate "driver"
program for the build system and specifying its location in the
GOPACKAGESDRIVER environment variable.
For example,
https://github.com/bazelbuild/rules_go/wiki/Editor-and-tool-integration
explains how to use the driver for Bazel.
The driver program is responsible for interpreting patterns in its
preferred notation and reporting information about the packages that
those patterns identify. Drivers must also support the special "file="
and "pattern=" patterns described above.
The patterns are provided as positional command-line arguments. A
JSON-encoded [DriverRequest] message providing additional information
is written to the driver's standard input. The driver must write a
JSON-encoded [DriverResponse] message to its standard output. (This
message differs from the JSON schema produced by 'go list'.)
The value of the PWD environment variable seen by the driver process
is the preferred name of its working directory. (The working directory
may have other aliases due to symbolic links; see the comment on the
Dir field of [exec.Cmd] for related information.)
When the driver process emits in its response the name of a file
that is a descendant of this directory, it must use an absolute path
that has the value of PWD as a prefix, to ensure that the returned
filenames satisfy the original query.
*/
package packages // import "golang.org/x/tools/go/packages"
@@ -168,14 +207,6 @@ 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 Overlay field in the Config allows providing alternate contents
for Go source files, by providing a mapping from file path to contents.
go/packages will pull in new imports added in overlay files when go/packages
is run in LoadImports mode or greater.
Overlay support for the go list driver isn't complete yet: if the file doesn't
exist on disk, it will only be recognized in an overlay if it is a non-test file
and the package would be reported even without the overlay.
Questions & Tasks
- Add GOARCH/GOOS?

View File

@@ -2,48 +2,87 @@
// 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
// This file defines the protocol that enables an external "driver"
// tool to supply package metadata in place of 'go list'.
import (
"bytes"
"encoding/json"
"fmt"
exec "golang.org/x/sys/execabs"
"os"
"os/exec"
"strings"
)
// The Driver Protocol
// DriverRequest defines the schema of a request for package metadata
// from an external driver program. The JSON-encoded DriverRequest
// message is provided to the driver program's standard input. The
// query patterns are provided as command-line arguments.
//
// The driver, given the inputs to a call to Load, returns metadata about the packages specified.
// This allows for different build systems to support go/packages by telling go/packages how the
// packages' source is organized.
// The driver is a binary, either specified by the GOPACKAGESDRIVER environment variable or in
// the path as gopackagesdriver. It's given the inputs to load in its argv. See the package
// documentation in doc.go for the full description of the patterns that need to be supported.
// A driver receives as a JSON-serialized driverRequest struct in standard input and will
// produce a JSON-serialized driverResponse (see definition in packages.go) in its standard output.
// driverRequest is used to provide the portion of Load's Config that is needed by a driver.
type driverRequest struct {
// See the package documentation for an overview.
type DriverRequest struct {
Mode LoadMode `json:"mode"`
// Env specifies the environment the underlying build system should be run in.
Env []string `json:"env"`
// BuildFlags are flags that should be passed to the underlying build system.
BuildFlags []string `json:"build_flags"`
// Tests specifies whether the patterns should also return test packages.
Tests bool `json:"tests"`
// Overlay maps file paths (relative to the driver's working directory) to the byte contents
// of overlay files.
// Overlay maps file paths (relative to the driver's working directory)
// to the contents of overlay files (see Config.Overlay).
Overlay map[string][]byte `json:"overlay"`
}
// DriverResponse defines the schema of a response from an external
// driver program, providing the results of a query for package
// metadata. The driver program must write a JSON-encoded
// DriverResponse message to its standard output.
//
// See the package documentation for an overview.
type DriverResponse struct {
// NotHandled is returned if the request can't be handled by the current
// driver. If an external driver returns a response with NotHandled, the
// rest of the DriverResponse is ignored, and go/packages will fallback
// to the next driver. If go/packages is extended in the future to support
// lists of multiple drivers, go/packages will fall back to the next driver.
NotHandled bool
// Compiler and Arch are the arguments pass of types.SizesFor
// to get a types.Sizes to use when type checking.
Compiler string
Arch string
// Roots is the set of package IDs that make up the root packages.
// We have to encode this separately because when we encode a single package
// we cannot know if it is one of the roots as that requires knowledge of the
// graph it is part of.
Roots []string `json:",omitempty"`
// Packages is the full set of packages in the graph.
// The packages are not connected into a graph.
// The Imports if populated will be stubs that only have their ID set.
// Imports will be connected and then type and syntax information added in a
// later pass (see refine).
Packages []*Package
// GoVersion is the minor version number used by the driver
// (e.g. the go command on the PATH) when selecting .go files.
// Zero means unknown.
GoVersion int
}
// driver is the type for functions that query the build system for the
// packages named by the patterns.
type driver func(cfg *Config, patterns ...string) (*DriverResponse, error)
// findExternalDriver returns the file path of a tool that supplies
// the build system package structure, or "" if not found."
// 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 {
@@ -64,8 +103,8 @@ func findExternalDriver(cfg *Config) driver {
return nil
}
}
return func(cfg *Config, words ...string) (*driverResponse, error) {
req, err := json.Marshal(driverRequest{
return func(cfg *Config, words ...string) (*DriverResponse, error) {
req, err := json.Marshal(DriverRequest{
Mode: cfg.Mode,
Env: cfg.Env,
BuildFlags: cfg.BuildFlags,
@@ -80,7 +119,19 @@ func findExternalDriver(cfg *Config) driver {
stderr := new(bytes.Buffer)
cmd := exec.CommandContext(cfg.Context, tool, words...)
cmd.Dir = cfg.Dir
cmd.Env = cfg.Env
// The cwd gets resolved to the real path. On Darwin, where
// /tmp is a symlink, this breaks anything that expects the
// working directory to keep the original path, including the
// go command when dealing with modules.
//
// os.Getwd stdlib has a special feature where if the
// cwd and the PWD are the same node then it trusts
// the PWD, so by setting it in the env for the child
// process we fix up all the paths returned by the go
// command.
//
// (See similar trick in Invocation.run in ../../internal/gocommand/invoke.go)
cmd.Env = append(slicesClip(cfg.Env), "PWD="+cfg.Dir)
cmd.Stdin = bytes.NewReader(req)
cmd.Stdout = buf
cmd.Stderr = stderr
@@ -92,10 +143,14 @@ func findExternalDriver(cfg *Config) driver {
fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd), stderr)
}
var response driverResponse
var response DriverResponse
if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
return nil, err
}
return &response, nil
}
}
// slicesClip removes unused capacity from the slice, returning s[:len(s):len(s)].
// TODO(adonovan): use go1.21 slices.Clip.
func slicesClip[S ~[]E, E any](s S) S { return s[:len(s):len(s)] }

View File

@@ -9,10 +9,9 @@ import (
"context"
"encoding/json"
"fmt"
"go/types"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"reflect"
@@ -22,8 +21,6 @@ import (
"sync"
"unicode"
exec "golang.org/x/sys/execabs"
"golang.org/x/tools/go/internal/packagesdriver"
"golang.org/x/tools/internal/gocommand"
"golang.org/x/tools/internal/packagesinternal"
)
@@ -37,23 +34,23 @@ type goTooOldError struct {
error
}
// responseDeduper wraps a driverResponse, deduplicating its contents.
// responseDeduper wraps a DriverResponse, deduplicating its contents.
type responseDeduper struct {
seenRoots map[string]bool
seenPackages map[string]*Package
dr *driverResponse
dr *DriverResponse
}
func newDeduper() *responseDeduper {
return &responseDeduper{
dr: &driverResponse{},
dr: &DriverResponse{},
seenRoots: map[string]bool{},
seenPackages: map[string]*Package{},
}
}
// addAll fills in r with a driverResponse.
func (r *responseDeduper) addAll(dr *driverResponse) {
// addAll fills in r with a DriverResponse.
func (r *responseDeduper) addAll(dr *DriverResponse) {
for _, pkg := range dr.Packages {
r.addPackage(pkg)
}
@@ -130,7 +127,7 @@ func (state *golistState) mustGetEnv() map[string]string {
// 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) {
func goListDriver(cfg *Config, patterns ...string) (_ *DriverResponse, err error) {
// Make sure that any asynchronous go commands are killed when we return.
parentCtx := cfg.Context
if parentCtx == nil {
@@ -148,16 +145,18 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
}
// Fill in response.Sizes asynchronously if necessary.
var sizeserr error
var sizeswg sync.WaitGroup
if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
sizeswg.Add(1)
errCh := make(chan error)
go func() {
var sizes types.Sizes
sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner)
// types.SizesFor always returns nil or a *types.StdSizes.
response.dr.Sizes, _ = sizes.(*types.StdSizes)
sizeswg.Done()
compiler, arch, err := getSizesForArgs(ctx, state.cfgInvocation(), cfg.gocmdRunner)
response.dr.Compiler = compiler
response.dr.Arch = arch
errCh <- err
}()
defer func() {
if sizesErr := <-errCh; sizesErr != nil {
err = sizesErr
}
}()
}
@@ -210,87 +209,10 @@ extractQueries:
}
}
// Only use go/packages' overlay processing if we're using a Go version
// below 1.16. Otherwise, go list handles it.
if goVersion, err := state.getGoVersion(); err == nil && goVersion < 16 {
modifiedPkgs, needPkgs, err := state.processGolistOverlay(response)
if err != nil {
return nil, err
}
var containsCandidates []string
if len(containFiles) > 0 {
containsCandidates = append(containsCandidates, modifiedPkgs...)
containsCandidates = append(containsCandidates, needPkgs...)
}
if err := state.addNeededOverlayPackages(response, needPkgs); err != nil {
return nil, err
}
// Check candidate packages for containFiles.
if len(containFiles) > 0 {
for _, id := range containsCandidates {
pkg, ok := response.seenPackages[id]
if !ok {
response.addPackage(&Package{
ID: id,
Errors: []Error{{
Kind: ListError,
Msg: fmt.Sprintf("package %s expected but not seen", id),
}},
})
continue
}
for _, f := range containFiles {
for _, g := range pkg.GoFiles {
if sameFile(f, g) {
response.addRoot(id)
}
}
}
}
}
// Add root for any package that matches a pattern. This applies only to
// packages that are modified by overlays, since they are not added as
// roots automatically.
for _, pattern := range restPatterns {
match := matchPattern(pattern)
for _, pkgID := range modifiedPkgs {
pkg, ok := response.seenPackages[pkgID]
if !ok {
continue
}
if match(pkg.PkgPath) {
response.addRoot(pkg.ID)
}
}
}
}
sizeswg.Wait()
if sizeserr != nil {
return nil, sizeserr
}
// (We may yet return an error due to defer.)
return response.dr, nil
}
func (state *golistState) addNeededOverlayPackages(response *responseDeduper, pkgs []string) error {
if len(pkgs) == 0 {
return nil
}
dr, err := state.createDriverResponse(pkgs...)
if err != nil {
return err
}
for _, pkg := range dr.Packages {
response.addPackage(pkg)
}
_, needPkgs, err := state.processGolistOverlay(response)
if err != nil {
return err
}
return state.addNeededOverlayPackages(response, needPkgs)
}
func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error {
for _, query := range queries {
// TODO(matloob): Do only one query per directory.
@@ -342,7 +264,7 @@ func (state *golistState) runContainsQueries(response *responseDeduper, queries
// adhocPackage attempts to load or construct an ad-hoc package for a given
// query, if the original call to the driver produced inadequate results.
func (state *golistState) adhocPackage(pattern, query string) (*driverResponse, error) {
func (state *golistState) adhocPackage(pattern, query string) (*DriverResponse, error) {
response, err := state.createDriverResponse(query)
if err != nil {
return nil, err
@@ -433,7 +355,7 @@ func otherFiles(p *jsonPackage) [][]string {
// createDriverResponse uses the "go list" command to expand the pattern
// words and return a response for the specified packages.
func (state *golistState) createDriverResponse(words ...string) (*driverResponse, error) {
func (state *golistState) createDriverResponse(words ...string) (*DriverResponse, error) {
// go list uses the following identifiers in ImportPath and Imports:
//
// "p" -- importable package or main (command)
@@ -460,7 +382,7 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
pkgs := make(map[string]*Package)
additionalErrors := make(map[string][]Error)
// Decode the JSON and convert it to Package form.
response := &driverResponse{
response := &DriverResponse{
GoVersion: goVersion,
}
for dec := json.NewDecoder(buf); dec.More(); {
@@ -625,7 +547,12 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
}
if pkg.PkgPath == "unsafe" {
pkg.GoFiles = nil // ignore fake unsafe.go file
pkg.CompiledGoFiles = nil // ignore fake unsafe.go file (#59929)
} else if len(pkg.CompiledGoFiles) == 0 {
// Work around for pre-go.1.11 versions of go list.
// TODO(matloob): they should be handled by the fallback.
// Can we delete this?
pkg.CompiledGoFiles = pkg.GoFiles
}
// Assume go list emits only absolute paths for Dir.
@@ -663,16 +590,12 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
response.Roots = append(response.Roots, pkg.ID)
}
// Work around for pre-go.1.11 versions of go list.
// TODO(matloob): they should be handled by the fallback.
// Can we delete this?
if len(pkg.CompiledGoFiles) == 0 {
pkg.CompiledGoFiles = pkg.GoFiles
}
// Temporary work-around for golang/go#39986. Parse filenames out of
// error messages. This happens if there are unrecoverable syntax
// errors in the source, so we can't match on a specific error message.
//
// TODO(rfindley): remove this heuristic, in favor of considering
// InvalidGoFiles from the list driver.
if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) {
addFilenameFromPos := func(pos string) bool {
split := strings.Split(pos, ":")
@@ -891,6 +814,15 @@ func golistargs(cfg *Config, words []string, goVersion int) []string {
// probably because you'd just get the TestMain.
fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0 && !usesExportData(cfg)),
}
// golang/go#60456: with go1.21 and later, go list serves pgo variants, which
// can be costly to compute and may result in redundant processing for the
// caller. Disable these variants. If someone wants to add e.g. a NeedPGO
// mode flag, that should be a separate proposal.
if goVersion >= 21 {
fullargs = append(fullargs, "-pgo=off")
}
fullargs = append(fullargs, cfg.BuildFlags...)
fullargs = append(fullargs, "--")
fullargs = append(fullargs, words...)
@@ -908,6 +840,7 @@ func (state *golistState) cfgInvocation() gocommand.Invocation {
Env: cfg.Env,
Logf: cfg.Logf,
WorkingDir: cfg.Dir,
Overlay: cfg.goListOverlayFile,
}
}
@@ -916,26 +849,6 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer,
cfg := state.cfg
inv := state.cfgInvocation()
// For Go versions 1.16 and above, `go list` accepts overlays directly via
// the -overlay flag. Set it, if it's available.
//
// The check for "list" is not necessarily required, but we should avoid
// getting the go version if possible.
if verb == "list" {
goVersion, err := state.getGoVersion()
if err != nil {
return nil, err
}
if goVersion >= 16 {
filename, cleanup, err := state.writeOverlays()
if err != nil {
return nil, err
}
defer cleanup()
inv.Overlay = filename
}
}
inv.Verb = verb
inv.Args = args
gocmdRunner := cfg.gocmdRunner
@@ -1082,67 +995,6 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer,
return stdout, nil
}
// OverlayJSON is the format overlay files are expected to be in.
// The Replace map maps from overlaid paths to replacement paths:
// the Go command will forward all reads trying to open
// each overlaid path to its replacement path, or consider the overlaid
// path not to exist if the replacement path is empty.
//
// From golang/go#39958.
type OverlayJSON struct {
Replace map[string]string `json:"replace,omitempty"`
}
// writeOverlays writes out files for go list's -overlay flag, as described
// above.
func (state *golistState) writeOverlays() (filename string, cleanup func(), err error) {
// Do nothing if there are no overlays in the config.
if len(state.cfg.Overlay) == 0 {
return "", func() {}, nil
}
dir, err := ioutil.TempDir("", "gopackages-*")
if err != nil {
return "", nil, err
}
// The caller must clean up this directory, unless this function returns an
// error.
cleanup = func() {
os.RemoveAll(dir)
}
defer func() {
if err != nil {
cleanup()
}
}()
overlays := map[string]string{}
for k, v := range state.cfg.Overlay {
// Create a unique filename for the overlaid files, to avoid
// creating nested directories.
noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "")
f, err := ioutil.TempFile(dir, fmt.Sprintf("*-%s", noSeparator))
if err != nil {
return "", func() {}, err
}
if _, err := f.Write(v); err != nil {
return "", func() {}, err
}
if err := f.Close(); err != nil {
return "", func() {}, err
}
overlays[k] = f.Name()
}
b, err := json.Marshal(OverlayJSON{Replace: overlays})
if err != nil {
return "", func() {}, err
}
// Write out the overlay file that contains the filepath mappings.
filename = filepath.Join(dir, "overlay.json")
if err := ioutil.WriteFile(filename, b, 0665); err != nil {
return "", func() {}, err
}
return filename, cleanup, nil
}
func containsGoFile(s []string) bool {
for _, f := range s {
if strings.HasSuffix(f, ".go") {
@@ -1171,3 +1023,44 @@ func cmdDebugStr(cmd *exec.Cmd) string {
}
return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], strings.Join(args, " "))
}
// getSizesForArgs queries 'go list' for the appropriate
// Compiler and GOARCH arguments to pass to [types.SizesFor].
func getSizesForArgs(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (string, string, error) {
inv.Verb = "list"
inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"}
stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv)
var goarch, compiler string
if rawErr != nil {
rawErrMsg := rawErr.Error()
if strings.Contains(rawErrMsg, "cannot find main module") ||
strings.Contains(rawErrMsg, "go.mod file not found") {
// User's running outside of a module.
// All bets are off. Get GOARCH and guess compiler is gc.
// TODO(matloob): Is this a problem in practice?
inv.Verb = "env"
inv.Args = []string{"GOARCH"}
envout, enverr := gocmdRunner.Run(ctx, inv)
if enverr != nil {
return "", "", enverr
}
goarch = strings.TrimSpace(envout.String())
compiler = "gc"
} else if friendlyErr != nil {
return "", "", friendlyErr
} else {
// This should be unreachable, but be defensive
// in case RunRaw's error results are inconsistent.
return "", "", rawErr
}
} else {
fields := strings.Fields(stdout.String())
if len(fields) < 2 {
return "", "", fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>",
stdout.String(), stderr.String())
}
goarch = fields[0]
compiler = fields[1]
}
return compiler, goarch, nil
}

View File

@@ -6,314 +6,11 @@ package packages
import (
"encoding/json"
"fmt"
"go/parser"
"go/token"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"golang.org/x/tools/internal/gocommand"
)
// processGolistOverlay provides rudimentary support for adding
// files that don't exist on disk to an overlay. The results can be
// sometimes incorrect.
// TODO(matloob): Handle unsupported cases, including the following:
// - determining the correct package to add given a new import path
func (state *golistState) processGolistOverlay(response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) {
havePkgs := make(map[string]string) // importPath -> non-test package ID
needPkgsSet := make(map[string]bool)
modifiedPkgsSet := make(map[string]bool)
pkgOfDir := make(map[string][]*Package)
for _, pkg := range response.dr.Packages {
// This is an approximation of import path to id. This can be
// wrong for tests, vendored packages, and a number of other cases.
havePkgs[pkg.PkgPath] = pkg.ID
dir, err := commonDir(pkg.GoFiles)
if err != nil {
return nil, nil, err
}
if dir != "" {
pkgOfDir[dir] = append(pkgOfDir[dir], pkg)
}
}
// If no new imports are added, it is safe to avoid loading any needPkgs.
// Otherwise, it's hard to tell which package is actually being loaded
// (due to vendoring) and whether any modified package will show up
// in the transitive set of dependencies (because new imports are added,
// potentially modifying the transitive set of dependencies).
var overlayAddsImports bool
// If both a package and its test package are created by the overlay, we
// need the real package first. Process all non-test files before test
// files, and make the whole process deterministic while we're at it.
var overlayFiles []string
for opath := range state.cfg.Overlay {
overlayFiles = append(overlayFiles, opath)
}
sort.Slice(overlayFiles, func(i, j int) bool {
iTest := strings.HasSuffix(overlayFiles[i], "_test.go")
jTest := strings.HasSuffix(overlayFiles[j], "_test.go")
if iTest != jTest {
return !iTest // non-tests are before tests.
}
return overlayFiles[i] < overlayFiles[j]
})
for _, opath := range overlayFiles {
contents := state.cfg.Overlay[opath]
base := filepath.Base(opath)
dir := filepath.Dir(opath)
var pkg *Package // if opath belongs to both a package and its test variant, this will be the test variant
var testVariantOf *Package // if opath is a test file, this is the package it is testing
var fileExists bool
isTestFile := strings.HasSuffix(opath, "_test.go")
pkgName, ok := extractPackageName(opath, contents)
if !ok {
// Don't bother adding a file that doesn't even have a parsable package statement
// to the overlay.
continue
}
// If all the overlay files belong to a different package, change the
// package name to that package.
maybeFixPackageName(pkgName, isTestFile, pkgOfDir[dir])
nextPackage:
for _, p := range response.dr.Packages {
if pkgName != p.Name && p.ID != "command-line-arguments" {
continue
}
for _, f := range p.GoFiles {
if !sameFile(filepath.Dir(f), dir) {
continue
}
// Make sure to capture information on the package's test variant, if needed.
if isTestFile && !hasTestFiles(p) {
// TODO(matloob): Are there packages other than the 'production' variant
// of a package that this can match? This shouldn't match the test main package
// because the file is generated in another directory.
testVariantOf = p
continue nextPackage
} else if !isTestFile && hasTestFiles(p) {
// We're examining a test variant, but the overlaid file is
// a non-test file. Because the overlay implementation
// (currently) only adds a file to one package, skip this
// package, so that we can add the file to the production
// variant of the package. (https://golang.org/issue/36857
// tracks handling overlays on both the production and test
// variant of a package).
continue nextPackage
}
if pkg != nil && p != pkg && pkg.PkgPath == p.PkgPath {
// We have already seen the production version of the
// for which p is a test variant.
if hasTestFiles(p) {
testVariantOf = pkg
}
}
pkg = p
if filepath.Base(f) == base {
fileExists = true
}
}
}
// The overlay could have included an entirely new package or an
// ad-hoc package. An ad-hoc package is one that we have manually
// constructed from inadequate `go list` results for a file= query.
// It will have the ID command-line-arguments.
if pkg == nil || pkg.ID == "command-line-arguments" {
// Try to find the module or gopath dir the file is contained in.
// Then for modules, add the module opath to the beginning.
pkgPath, ok, err := state.getPkgPath(dir)
if err != nil {
return nil, nil, err
}
if !ok {
break
}
var forTest string // only set for x tests
isXTest := strings.HasSuffix(pkgName, "_test")
if isXTest {
forTest = pkgPath
pkgPath += "_test"
}
id := pkgPath
if isTestFile {
if isXTest {
id = fmt.Sprintf("%s [%s.test]", pkgPath, forTest)
} else {
id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath)
}
}
if pkg != nil {
// TODO(rstambler): We should change the package's path and ID
// here. The only issue is that this messes with the roots.
} else {
// Try to reclaim a package with the same ID, if it exists in the response.
for _, p := range response.dr.Packages {
if reclaimPackage(p, id, opath, contents) {
pkg = p
break
}
}
// Otherwise, create a new package.
if pkg == nil {
pkg = &Package{
PkgPath: pkgPath,
ID: id,
Name: pkgName,
Imports: make(map[string]*Package),
}
response.addPackage(pkg)
havePkgs[pkg.PkgPath] = id
// Add the production package's sources for a test variant.
if isTestFile && !isXTest && testVariantOf != nil {
pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...)
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...)
// Add the package under test and its imports to the test variant.
pkg.forTest = testVariantOf.PkgPath
for k, v := range testVariantOf.Imports {
pkg.Imports[k] = &Package{ID: v.ID}
}
}
if isXTest {
pkg.forTest = forTest
}
}
}
}
if !fileExists {
pkg.GoFiles = append(pkg.GoFiles, opath)
// TODO(matloob): Adding the file to CompiledGoFiles can exhibit the wrong behavior
// if the file will be ignored due to its build tags.
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, opath)
modifiedPkgsSet[pkg.ID] = true
}
imports, err := extractImports(opath, contents)
if err != nil {
// Let the parser or type checker report errors later.
continue
}
for _, imp := range imports {
// TODO(rstambler): If the package is an x test and the import has
// a test variant, make sure to replace it.
if _, found := pkg.Imports[imp]; found {
continue
}
overlayAddsImports = true
id, ok := havePkgs[imp]
if !ok {
var err error
id, err = state.resolveImport(dir, imp)
if err != nil {
return nil, nil, err
}
}
pkg.Imports[imp] = &Package{ID: id}
// Add dependencies to the non-test variant version of this package as well.
if testVariantOf != nil {
testVariantOf.Imports[imp] = &Package{ID: id}
}
}
}
// toPkgPath guesses the package path given the id.
toPkgPath := func(sourceDir, id string) (string, error) {
if i := strings.IndexByte(id, ' '); i >= 0 {
return state.resolveImport(sourceDir, id[:i])
}
return state.resolveImport(sourceDir, id)
}
// Now that new packages have been created, do another pass to determine
// the new set of missing packages.
for _, pkg := range response.dr.Packages {
for _, imp := range pkg.Imports {
if len(pkg.GoFiles) == 0 {
return nil, nil, fmt.Errorf("cannot resolve imports for package %q with no Go files", pkg.PkgPath)
}
pkgPath, err := toPkgPath(filepath.Dir(pkg.GoFiles[0]), imp.ID)
if err != nil {
return nil, nil, err
}
if _, ok := havePkgs[pkgPath]; !ok {
needPkgsSet[pkgPath] = true
}
}
}
if overlayAddsImports {
needPkgs = make([]string, 0, len(needPkgsSet))
for pkg := range needPkgsSet {
needPkgs = append(needPkgs, pkg)
}
}
modifiedPkgs = make([]string, 0, len(modifiedPkgsSet))
for pkg := range modifiedPkgsSet {
modifiedPkgs = append(modifiedPkgs, pkg)
}
return modifiedPkgs, needPkgs, err
}
// resolveImport finds the ID of a package given its import path.
// In particular, it will find the right vendored copy when in GOPATH mode.
func (state *golistState) resolveImport(sourceDir, importPath string) (string, error) {
env, err := state.getEnv()
if err != nil {
return "", err
}
if env["GOMOD"] != "" {
return importPath, nil
}
searchDir := sourceDir
for {
vendorDir := filepath.Join(searchDir, "vendor")
exists, ok := state.vendorDirs[vendorDir]
if !ok {
info, err := os.Stat(vendorDir)
exists = err == nil && info.IsDir()
state.vendorDirs[vendorDir] = exists
}
if exists {
vendoredPath := filepath.Join(vendorDir, importPath)
if info, err := os.Stat(vendoredPath); err == nil && info.IsDir() {
// We should probably check for .go files here, but shame on anyone who fools us.
path, ok, err := state.getPkgPath(vendoredPath)
if err != nil {
return "", err
}
if ok {
return path, nil
}
}
}
// We know we've hit the top of the filesystem when we Dir / and get /,
// or C:\ and get C:\, etc.
next := filepath.Dir(searchDir)
if next == searchDir {
break
}
searchDir = next
}
return importPath, nil
}
func hasTestFiles(p *Package) bool {
for _, f := range p.GoFiles {
if strings.HasSuffix(f, "_test.go") {
return true
}
}
return false
}
// determineRootDirs returns a mapping from absolute directories that could
// contain code to their corresponding import path prefixes.
func (state *golistState) determineRootDirs() (map[string]string, error) {
@@ -384,192 +81,3 @@ func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) {
}
return m, nil
}
func extractImports(filename string, contents []byte) ([]string, error) {
f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.ImportsOnly) // TODO(matloob): reuse fileset?
if err != nil {
return nil, err
}
var res []string
for _, imp := range f.Imports {
quotedPath := imp.Path.Value
path, err := strconv.Unquote(quotedPath)
if err != nil {
return nil, err
}
res = append(res, path)
}
return res, nil
}
// reclaimPackage attempts to reuse a package that failed to load in an overlay.
//
// If the package has errors and has no Name, GoFiles, or Imports,
// then it's possible that it doesn't yet exist on disk.
func reclaimPackage(pkg *Package, id string, filename string, contents []byte) bool {
// TODO(rstambler): Check the message of the actual error?
// It differs between $GOPATH and module mode.
if pkg.ID != id {
return false
}
if len(pkg.Errors) != 1 {
return false
}
if pkg.Name != "" || pkg.ExportFile != "" {
return false
}
if len(pkg.GoFiles) > 0 || len(pkg.CompiledGoFiles) > 0 || len(pkg.OtherFiles) > 0 {
return false
}
if len(pkg.Imports) > 0 {
return false
}
pkgName, ok := extractPackageName(filename, contents)
if !ok {
return false
}
pkg.Name = pkgName
pkg.Errors = nil
return true
}
func extractPackageName(filename string, contents []byte) (string, bool) {
// TODO(rstambler): Check the message of the actual error?
// It differs between $GOPATH and module mode.
f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.PackageClauseOnly) // TODO(matloob): reuse fileset?
if err != nil {
return "", false
}
return f.Name.Name, true
}
// commonDir returns the directory that all files are in, "" if files is empty,
// or an error if they aren't in the same directory.
func commonDir(files []string) (string, error) {
seen := make(map[string]bool)
for _, f := range files {
seen[filepath.Dir(f)] = true
}
if len(seen) > 1 {
return "", fmt.Errorf("files (%v) are in more than one directory: %v", files, seen)
}
for k := range seen {
// seen has only one element; return it.
return k, nil
}
return "", nil // no files
}
// It is possible that the files in the disk directory dir have a different package
// name from newName, which is deduced from the overlays. If they all have a different
// package name, and they all have the same package name, then that name becomes
// the package name.
// It returns true if it changes the package name, false otherwise.
func maybeFixPackageName(newName string, isTestFile bool, pkgsOfDir []*Package) {
names := make(map[string]int)
for _, p := range pkgsOfDir {
names[p.Name]++
}
if len(names) != 1 {
// some files are in different packages
return
}
var oldName string
for k := range names {
oldName = k
}
if newName == oldName {
return
}
// We might have a case where all of the package names in the directory are
// the same, but the overlay file is for an x test, which belongs to its
// own package. If the x test does not yet exist on disk, we may not yet
// have its package name on disk, but we should not rename the packages.
//
// We use a heuristic to determine if this file belongs to an x test:
// The test file should have a package name whose package name has a _test
// suffix or looks like "newName_test".
maybeXTest := strings.HasPrefix(oldName+"_test", newName) || strings.HasSuffix(newName, "_test")
if isTestFile && maybeXTest {
return
}
for _, p := range pkgsOfDir {
p.Name = newName
}
}
// This function is copy-pasted from
// https://github.com/golang/go/blob/9706f510a5e2754595d716bd64be8375997311fb/src/cmd/go/internal/search/search.go#L360.
// It should be deleted when we remove support for overlays from go/packages.
//
// NOTE: This does not handle any ./... or ./ style queries, as this function
// doesn't know the working directory.
//
// matchPattern(pattern)(name) reports whether
// name matches pattern. Pattern is a limited glob
// pattern in which '...' means 'any string' and there
// is no other special syntax.
// Unfortunately, there are two special cases. Quoting "go help packages":
//
// First, /... at the end of the pattern can match an empty string,
// so that net/... matches both net and packages in its subdirectories, like net/http.
// Second, any slash-separated pattern element containing a wildcard never
// participates in a match of the "vendor" element in the path of a vendored
// package, so that ./... does not match packages in subdirectories of
// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
// Note, however, that a directory named vendor that itself contains code
// is not a vendored package: cmd/vendor would be a command named vendor,
// and the pattern cmd/... matches it.
func matchPattern(pattern string) func(name string) bool {
// Convert pattern to regular expression.
// The strategy for the trailing /... is to nest it in an explicit ? expression.
// The strategy for the vendor exclusion is to change the unmatchable
// vendor strings to a disallowed code point (vendorChar) and to use
// "(anything but that codepoint)*" as the implementation of the ... wildcard.
// This is a bit complicated but the obvious alternative,
// namely a hand-written search like in most shell glob matchers,
// is too easy to make accidentally exponential.
// Using package regexp guarantees linear-time matching.
const vendorChar = "\x00"
if strings.Contains(pattern, vendorChar) {
return func(name string) bool { return false }
}
re := regexp.QuoteMeta(pattern)
re = replaceVendor(re, vendorChar)
switch {
case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`):
re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)`
case re == vendorChar+`/\.\.\.`:
re = `(/vendor|/` + vendorChar + `/\.\.\.)`
case strings.HasSuffix(re, `/\.\.\.`):
re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?`
}
re = strings.ReplaceAll(re, `\.\.\.`, `[^`+vendorChar+`]*`)
reg := regexp.MustCompile(`^` + re + `$`)
return func(name string) bool {
if strings.Contains(name, vendorChar) {
return false
}
return reg.MatchString(replaceVendor(name, vendorChar))
}
}
// replaceVendor returns the result of replacing
// non-trailing vendor path elements in x with repl.
func replaceVendor(x, repl string) string {
if !strings.Contains(x, "vendor") {
return x
}
elem := strings.Split(x, "/")
for i := 0; i < len(elem)-1; i++ {
if elem[i] == "vendor" {
elem[i] = repl
}
}
return strings.Join(elem, "/")
}

View File

@@ -9,49 +9,46 @@ import (
"strings"
)
var allModes = []LoadMode{
NeedName,
NeedFiles,
NeedCompiledGoFiles,
NeedImports,
NeedDeps,
NeedExportFile,
NeedTypes,
NeedSyntax,
NeedTypesInfo,
NeedTypesSizes,
var modes = [...]struct {
mode LoadMode
name string
}{
{NeedName, "NeedName"},
{NeedFiles, "NeedFiles"},
{NeedCompiledGoFiles, "NeedCompiledGoFiles"},
{NeedImports, "NeedImports"},
{NeedDeps, "NeedDeps"},
{NeedExportFile, "NeedExportFile"},
{NeedTypes, "NeedTypes"},
{NeedSyntax, "NeedSyntax"},
{NeedTypesInfo, "NeedTypesInfo"},
{NeedTypesSizes, "NeedTypesSizes"},
{NeedModule, "NeedModule"},
{NeedEmbedFiles, "NeedEmbedFiles"},
{NeedEmbedPatterns, "NeedEmbedPatterns"},
}
var modeStrings = []string{
"NeedName",
"NeedFiles",
"NeedCompiledGoFiles",
"NeedImports",
"NeedDeps",
"NeedExportFile",
"NeedTypes",
"NeedSyntax",
"NeedTypesInfo",
"NeedTypesSizes",
}
func (mod LoadMode) String() string {
m := mod
if m == 0 {
func (mode LoadMode) String() string {
if mode == 0 {
return "LoadMode(0)"
}
var out []string
for i, x := range allModes {
if x > m {
break
}
if (m & x) != 0 {
out = append(out, modeStrings[i])
m = m ^ x
// named bits
for _, item := range modes {
if (mode & item.mode) != 0 {
mode ^= item.mode
out = append(out, item.name)
}
}
if m != 0 {
out = append(out, "Unknown")
// unnamed residue
if mode != 0 {
if out == nil {
return fmt.Sprintf("LoadMode(%#x)", int(mode))
}
out = append(out, fmt.Sprintf("%#x", int(mode)))
}
return fmt.Sprintf("LoadMode(%s)", strings.Join(out, "|"))
if len(out) == 1 {
return out[0]
}
return "(" + strings.Join(out, "|") + ")"
}

View File

@@ -9,6 +9,7 @@ package packages
import (
"context"
"encoding/json"
"errors"
"fmt"
"go/ast"
"go/parser"
@@ -16,7 +17,6 @@ import (
"go/token"
"go/types"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
@@ -25,20 +25,31 @@ import (
"sync"
"time"
"golang.org/x/sync/errgroup"
"golang.org/x/tools/go/gcexportdata"
"golang.org/x/tools/internal/gocommand"
"golang.org/x/tools/internal/packagesinternal"
"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/typesinternal"
"golang.org/x/tools/internal/versions"
)
// 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.
// [Load] may return more information than requested.
//
// Unfortunately there are a number of open bugs related to
// interactions among the LoadMode bits:
// - https://github.com/golang/go/issues/56633
// - https://github.com/golang/go/issues/56677
// - https://github.com/golang/go/issues/58726
// - https://github.com/golang/go/issues/63517
type LoadMode int
const (
@@ -64,7 +75,7 @@ const (
// NeedTypes adds Types, Fset, and IllTyped.
NeedTypes
// NeedSyntax adds Syntax.
// NeedSyntax adds Syntax and Fset.
NeedSyntax
// NeedTypesInfo adds TypesInfo.
@@ -92,25 +103,37 @@ const (
// NeedEmbedPatterns adds EmbedPatterns.
NeedEmbedPatterns
// Be sure to update loadmode_string.go when adding new items!
)
const (
// LoadFiles loads the name and file names for the initial packages.
//
// Deprecated: LoadFiles exists for historical compatibility
// and should not be used. Please directly specify the needed fields using the Need values.
LoadFiles = NeedName | NeedFiles | NeedCompiledGoFiles
// LoadImports loads the name, file names, and import mapping for the initial packages.
//
// Deprecated: LoadImports exists for historical compatibility
// and should not be used. Please directly specify the needed fields using the Need values.
LoadImports = LoadFiles | NeedImports
// LoadTypes loads exported type information for the initial packages.
//
// Deprecated: LoadTypes exists for historical compatibility
// and should not be used. Please directly specify the needed fields using the Need values.
LoadTypes = LoadImports | NeedTypes | NeedTypesSizes
// LoadSyntax loads typed syntax for the initial packages.
//
// Deprecated: LoadSyntax exists for historical compatibility
// and should not be used. Please directly specify the needed fields using the Need values.
LoadSyntax = LoadTypes | NeedSyntax | NeedTypesInfo
// LoadAllSyntax loads typed syntax for the initial packages and all dependencies.
//
// Deprecated: LoadAllSyntax exists for historical compatibility
// and should not be used. Please directly specify the needed fields using the Need values.
LoadAllSyntax = LoadSyntax | NeedDeps
@@ -121,15 +144,21 @@ const (
// 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.
//
// TODO(adonovan): #67702: this is currently false: in fact,
// calls to [Load] do not modify the public fields of this struct, but
// may modify hidden fields, so concurrent calls to [Load] must not
// use the same Config. But perhaps we should reestablish the
// documented invariant.
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.
// Cancelling the context may cause [Load] to abort and
// return an error.
Context context.Context
// Logf is the logger for the config.
@@ -198,93 +227,209 @@ type Config struct {
// 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.
// Overlay is a mapping from absolute file paths to file contents.
//
// Overlays provide incomplete support for when a given file doesn't
// already exist on disk. See the package doc above for more details.
// 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
}
// 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 {
// NotHandled is returned if the request can't be handled by the current
// driver. If an external driver returns a response with NotHandled, the
// rest of the driverResponse is ignored, and go/packages will fallback
// to the next driver. If go/packages is extended in the future to support
// lists of multiple drivers, go/packages will fall back to the next driver.
NotHandled bool
// Sizes, if not nil, is the types.Sizes to use when type checking.
Sizes *types.StdSizes
// Roots is the set of package IDs that make up the root packages.
// We have to encode this separately because when we encode a single package
// we cannot know if it is one of the roots as that requires knowledge of the
// graph it is part of.
Roots []string `json:",omitempty"`
// Packages is the full set of packages in the graph.
// The packages are not connected into a graph.
// The Imports if populated will be stubs that only have their ID set.
// Imports will be connected and then type and syntax information added in a
// later pass (see refine).
Packages []*Package
// GoVersion is the minor version number used by the driver
// (e.g. the go command on the PATH) when selecting .go files.
// Zero means unknown.
GoVersion int
// goListOverlayFile is the JSON file that encodes the Overlay
// mapping, used by 'go list -overlay=...'
goListOverlayFile string
}
// Load loads and returns the Go packages named by the given patterns.
//
// Config specifies loading options;
// nil behaves the same as an empty Config.
// The cfg parameter 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.
// 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
// 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...)
ld := newLoader(cfg)
response, external, err := defaultDriver(&ld.Config, patterns...)
if err != nil {
return nil, err
}
l.sizes = response.Sizes
return l.refine(response)
ld.sizes = types.SizesFor(response.Compiler, response.Arch)
if ld.sizes == nil && ld.Config.Mode&(NeedTypes|NeedTypesSizes|NeedTypesInfo) != 0 {
// Type size information is needed but unavailable.
if external {
// An external driver may fail to populate the Compiler/GOARCH fields,
// especially since they are relatively new (see #63700).
// Provide a sensible fallback in this case.
ld.sizes = types.SizesFor("gc", runtime.GOARCH)
if ld.sizes == nil { // gccgo-only arch
ld.sizes = types.SizesFor("gc", "amd64")
}
} else {
// Go list should never fail to deliver accurate size information.
// Reject the whole Load since the error is the same for every package.
return nil, fmt.Errorf("can't determine type sizes for compiler %q on GOARCH %q",
response.Compiler, response.Arch)
}
}
return ld.refine(response)
}
// defaultDriver is a driver that implements go/packages' fallback behavior.
// 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.
func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
driver := findExternalDriver(cfg)
if driver == nil {
driver = goListDriver
}
response, err := driver(cfg, patterns...)
// The boolean result indicates that an external driver handled the request.
func defaultDriver(cfg *Config, patterns ...string) (*DriverResponse, bool, error) {
const (
// windowsArgMax specifies the maximum command line length for
// the Windows' CreateProcess function.
windowsArgMax = 32767
// maxEnvSize is a very rough estimation of the maximum environment
// size of a user.
maxEnvSize = 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.
safeArgMax = windowsArgMax - maxEnvSize
)
chunks, err := splitIntoChunks(patterns, safeArgMax)
if err != nil {
return response, err
} else if response.NotHandled {
return goListDriver(cfg, patterns...)
return nil, false, err
}
return response, nil
if driver := findExternalDriver(cfg); driver != nil {
response, err := callDriverOnChunks(driver, cfg, chunks)
if err != nil {
return nil, false, err
} else if !response.NotHandled {
return response, true, nil
}
// (fall through)
}
// go list fallback
//
// Write overlays once, as there are many calls
// to 'go list' (one per chunk plus others too).
overlay, cleanupOverlay, err := gocommand.WriteOverlays(cfg.Overlay)
if err != nil {
return nil, false, err
}
defer cleanupOverlay()
cfg.goListOverlayFile = overlay
response, err := callDriverOnChunks(goListDriver, cfg, chunks)
if err != nil {
return nil, false, err
}
return response, false, err
}
// splitIntoChunks chunks the slice so that the total number of characters
// in a chunk is no longer than argMax.
func splitIntoChunks(patterns []string, argMax int) ([][]string, error) {
if argMax <= 0 {
return nil, errors.New("failed to split patterns into chunks, negative safe argMax value")
}
var chunks [][]string
charsInChunk := 0
nextChunkStart := 0
for i, v := range patterns {
vChars := len(v)
if vChars > argMax {
// 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")
}
charsInChunk += vChars + 1 // +1 is for a whitespace between patterns that has to be counted too
if charsInChunk > argMax {
chunks = append(chunks, patterns[nextChunkStart:i])
nextChunkStart = i
charsInChunk = vChars
}
}
// add the last chunk
if nextChunkStart < len(patterns) {
chunks = append(chunks, patterns[nextChunkStart:])
}
return chunks, nil
}
func callDriverOnChunks(driver driver, cfg *Config, chunks [][]string) (*DriverResponse, error) {
if len(chunks) == 0 {
return driver(cfg)
}
responses := make([]*DriverResponse, len(chunks))
errNotHandled := errors.New("driver returned NotHandled")
var g errgroup.Group
for i, chunk := range chunks {
i := i
chunk := chunk
g.Go(func() (err error) {
responses[i], err = driver(cfg, chunk...)
if responses[i] != nil && responses[i].NotHandled {
err = errNotHandled
}
return err
})
}
if err := g.Wait(); err != nil {
if errors.Is(err, errNotHandled) {
return &DriverResponse{NotHandled: true}, nil
}
return nil, err
}
return mergeResponses(responses...), nil
}
func mergeResponses(responses ...*DriverResponse) *DriverResponse {
if len(responses) == 0 {
return nil
}
response := newDeduper()
response.dr.NotHandled = false
response.dr.Compiler = responses[0].Compiler
response.dr.Arch = responses[0].Arch
response.dr.GoVersion = responses[0].GoVersion
for _, v := range responses {
response.addAll(v)
}
return response.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.
@@ -308,6 +453,9 @@ type Package struct {
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
@@ -340,19 +488,30 @@ type Package struct {
// 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.
Types *types.Package
//
// 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
Fset *token.FileSet `json:"-"`
// IllTyped indicates whether the package or any dependency contains errors.
// It is set only when Types is set.
IllTyped bool
IllTyped bool `json:"-"`
// Syntax is the package's syntax trees, for the files listed in CompiledGoFiles.
//
@@ -362,26 +521,28 @@ type Package struct {
//
// 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
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
TypesInfo *types.Info `json:"-"`
// TypesSizes provides the effective size function for types in TypesInfo.
TypesSizes types.Sizes
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 is the module information for the package if it exists.
Module *Module
}
// 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
@@ -407,12 +568,6 @@ func init() {
packagesinternal.GetDepsErrors = func(p interface{}) []*packagesinternal.PackageError {
return p.(*Package).depsErrors
}
packagesinternal.GetGoCmdRunner = func(config interface{}) *gocommand.Runner {
return config.(*Config).gocmdRunner
}
packagesinternal.SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {
config.(*Config).gocmdRunner = runner
}
packagesinternal.SetModFile = func(config interface{}, value string) {
config.(*Config).modFile = value
}
@@ -520,6 +675,7 @@ func (p *Package) UnmarshalJSON(b []byte) error {
OtherFiles: flat.OtherFiles,
EmbedFiles: flat.EmbedFiles,
EmbedPatterns: flat.EmbedPatterns,
IgnoredFiles: flat.IgnoredFiles,
ExportFile: flat.ExportFile,
}
if len(flat.Imports) > 0 {
@@ -549,7 +705,7 @@ type loaderPackage struct {
type loader struct {
pkgs map[string]*loaderPackage
Config
sizes types.Sizes
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
@@ -618,6 +774,7 @@ func newLoader(cfg *Config) *loader {
// 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) {
// We implicitly promise to keep doing ast.Object resolution. :(
const mode = parser.AllErrors | parser.ParseComments
return parser.ParseFile(fset, filename, src, mode)
}
@@ -627,9 +784,9 @@ func newLoader(cfg *Config) *loader {
return ld
}
// refine connects the supplied packages into a graph and then adds type and
// refine connects the supplied packages into a graph and then adds type
// and syntax information as requested by the LoadMode.
func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
roots := response.Roots
rootMap := make(map[string]int, len(roots))
for i, root := range roots {
@@ -674,39 +831,38 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
}
}
// Materialize the import graph.
if ld.Mode&NeedImports != 0 {
// Materialize the import graph.
const (
white = 0 // new
grey = 1 // in progress
black = 2 // complete
)
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
// If NeedImports isn't set, the imports fields will all be zeroed out.
if ld.Mode&NeedImports != 0 {
// 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
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
@@ -730,40 +886,39 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
}
lpkg.Imports[importPath] = imp.Package
}
}
if lpkg.needsrc {
srcPkgs = append(srcPkgs, lpkg)
}
if ld.Mode&NeedTypesSizes != 0 {
lpkg.TypesSizes = ld.sizes
}
stack = stack[:len(stack)-1] // pop
lpkg.color = black
return lpkg.needsrc
}
// Complete type information is required for the
// immediate dependencies of each source package.
if lpkg.needsrc && ld.Mode&NeedTypes != 0 {
for _, ipkg := range lpkg.Imports {
ld.pkgs[ipkg.ID].needtypes = true
}
}
if ld.Mode&NeedImports == 0 {
// 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
// NeedTypeSizes causes TypeSizes to be set even
// on packages for which types aren't needed.
if ld.Mode&NeedTypesSizes != 0 {
lpkg.TypesSizes = ld.sizes
}
stack = stack[:len(stack)-1] // pop
lpkg.color = black
return lpkg.needsrc
}
} else {
// For each initial package, create its import DAG.
for _, lpkg := range initial {
visit(lpkg)
}
}
if ld.Mode&NeedImports != 0 && ld.Mode&NeedTypes != 0 {
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
}
} else {
// !NeedImports: drop the stub (ID-only) import packages
// that we are not even going to try to resolve.
for _, lpkg := range initial {
lpkg.Imports = nil
}
}
// Load type data and syntax if needed, starting at
// the initial packages (roots of the import DAG).
if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 {
@@ -778,6 +933,12 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
wg.Wait()
}
// If the context is done, return its error and
// throw out [likely] incomplete packages.
if err := ld.Context.Err(); err != nil {
return nil, err
}
result := make([]*Package, len(initial))
for i, lpkg := range initial {
result[i] = lpkg.Package
@@ -811,12 +972,14 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
}
if ld.requestedMode&NeedTypes == 0 {
ld.pkgs[i].Types = nil
ld.pkgs[i].Fset = nil
ld.pkgs[i].IllTyped = false
}
if ld.requestedMode&NeedSyntax == 0 {
ld.pkgs[i].Syntax = nil
}
if ld.requestedMode&NeedTypes == 0 && ld.requestedMode&NeedSyntax == 0 {
ld.pkgs[i].Fset = nil
}
if ld.requestedMode&NeedTypesInfo == 0 {
ld.pkgs[i].TypesInfo = nil
}
@@ -873,6 +1036,14 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
lpkg.Types = types.NewPackage(lpkg.PkgPath, lpkg.Name)
lpkg.Fset = ld.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 ld.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,
@@ -992,15 +1163,23 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
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 ld.Context.Err() != nil {
return
}
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),
Instances: make(map[*ast.Ident]types.Instance),
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
}
typeparams.InitInstanceInfo(lpkg.TypesInfo)
versions.InitFileVersions(lpkg.TypesInfo)
lpkg.TypesSizes = ld.sizes
importer := importerFunc(func(path string) (*types.Package, error) {
@@ -1038,7 +1217,10 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
IgnoreFuncBodies: ld.Mode&NeedDeps == 0 && !lpkg.initial,
Error: appendError,
Sizes: ld.sizes,
Sizes: ld.sizes, // may be nil
}
if lpkg.Module != nil && lpkg.Module.GoVersion != "" {
tc.GoVersion = "go" + lpkg.Module.GoVersion
}
if (ld.Mode & typecheckCgo) != 0 {
if !typesinternal.SetUsesCgo(tc) {
@@ -1049,10 +1231,24 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
return
}
}
types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
typErr := types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
lpkg.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 typErr != nil && len(lpkg.Errors) == 0 && len(lpkg.Syntax) > 0 {
if msg := typErr.Error(); strings.HasPrefix(msg, "package requires newer Go version") {
appendError(types.Error{
Fset: ld.Fset,
Pos: lpkg.Syntax[0].Package,
Msg: 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,
@@ -1074,6 +1270,12 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
}
}
// If types.Checker.Files had an error that was unreported,
// make sure to report the unknown error so the package is illTyped.
if typErr != nil && len(lpkg.Errors) == 0 {
appendError(typErr)
}
// Record accumulated errors.
illTyped := len(lpkg.Errors) > 0
if !illTyped {
@@ -1119,7 +1321,7 @@ func (ld *loader) parseFile(filename string) (*ast.File, error) {
var err error
if src == nil {
ioLimit <- true // wait
src, err = ioutil.ReadFile(filename)
src, err = os.ReadFile(filename)
<-ioLimit // signal
}
if err != nil {
@@ -1145,11 +1347,6 @@ func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) {
parsed := make([]*ast.File, n)
errors := make([]error, n)
for i, file := range filenames {
if ld.Config.Context.Err() != nil {
parsed[i] = nil
errors[i] = ld.Config.Context.Err()
continue
}
wg.Add(1)
go func(i int, filename string) {
parsed[i], errors[i] = ld.parseFile(filename)
@@ -1315,6 +1512,10 @@ func impliedLoadMode(loadMode LoadMode) LoadMode {
// All these things require knowing the import graph.
loadMode |= NeedImports
}
if loadMode&NeedTypes != 0 {
// Types require the GoVersion from Module.
loadMode |= NeedModule
}
return loadMode
}

View File

@@ -49,11 +49,20 @@ func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) {
// PrintErrors returns the number of errors printed.
func PrintErrors(pkgs []*Package) int {
var n int
errModules := make(map[*Module]bool)
Visit(pkgs, nil, func(pkg *Package) {
for _, err := range pkg.Errors {
fmt.Fprintln(os.Stderr, err)
n++
}
// Print pkg.Module.Error once if present.
mod := pkg.Module
if mod != nil && mod.Error != nil && !errModules[mod] {
errModules[mod] = true
fmt.Fprintln(os.Stderr, mod.Error.Err)
n++
}
})
return n
}

View File

@@ -0,0 +1,788 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package objectpath defines a naming scheme for types.Objects
// (that is, named entities in Go programs) relative to their enclosing
// package.
//
// Type-checker objects are canonical, so they are usually identified by
// their address in memory (a pointer), but a pointer has meaning only
// within one address space. By contrast, objectpath names allow the
// identity of an object to be sent from one program to another,
// establishing a correspondence between types.Object variables that are
// distinct but logically equivalent.
//
// A single object may have multiple paths. In this example,
//
// type A struct{ X int }
// type B A
//
// the field X has two paths due to its membership of both A and B.
// The For(obj) function always returns one of these paths, arbitrarily
// but consistently.
package objectpath
import (
"fmt"
"go/types"
"strconv"
"strings"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/typesinternal"
)
// TODO(adonovan): think about generic aliases.
// A Path is an opaque name that identifies a types.Object
// relative to its package. Conceptually, the name consists of a
// sequence of destructuring operations applied to the package scope
// to obtain the original object.
// The name does not include the package itself.
type Path string
// Encoding
//
// An object path is a textual and (with training) human-readable encoding
// of a sequence of destructuring operators, starting from a types.Package.
// The sequences represent a path through the package/object/type graph.
// We classify these operators by their type:
//
// PO package->object Package.Scope.Lookup
// OT object->type Object.Type
// TT type->type Type.{Elem,Key,{,{,Recv}Type}Params,Results,Underlying,Rhs} [EKPRUTrCa]
// TO type->object Type.{At,Field,Method,Obj} [AFMO]
//
// All valid paths start with a package and end at an object
// and thus may be defined by the regular language:
//
// objectpath = PO (OT TT* TO)*
//
// The concrete encoding follows directly:
// - The only PO operator is Package.Scope.Lookup, which requires an identifier.
// - The only OT operator is Object.Type,
// which we encode as '.' because dot cannot appear in an identifier.
// - The TT operators are encoded as [EKPRUTrCa];
// two of these ({,Recv}TypeParams) require an integer operand,
// which is encoded as a string of decimal digits.
// - The TO operators are encoded as [AFMO];
// three of these (At,Field,Method) require an integer operand,
// which is encoded as a string of decimal digits.
// These indices are stable across different representations
// of the same package, even source and export data.
// The indices used are implementation specific and may not correspond to
// the argument to the go/types function.
//
// In the example below,
//
// package p
//
// type T interface {
// f() (a string, b struct{ X int })
// }
//
// field X has the path "T.UM0.RA1.F0",
// representing the following sequence of operations:
//
// p.Lookup("T") T
// .Type().Underlying().Method(0). f
// .Type().Results().At(1) b
// .Type().Field(0) X
//
// The encoding is not maximally compact---every R or P is
// followed by an A, for example---but this simplifies the
// encoder and decoder.
const (
// object->type operators
opType = '.' // .Type() (Object)
// type->type operators
opElem = 'E' // .Elem() (Pointer, Slice, Array, Chan, Map)
opKey = 'K' // .Key() (Map)
opParams = 'P' // .Params() (Signature)
opResults = 'R' // .Results() (Signature)
opUnderlying = 'U' // .Underlying() (Named)
opTypeParam = 'T' // .TypeParams.At(i) (Named, Signature)
opRecvTypeParam = 'r' // .RecvTypeParams.At(i) (Signature)
opConstraint = 'C' // .Constraint() (TypeParam)
opRhs = 'a' // .Rhs() (Alias)
// type->object operators
opAt = 'A' // .At(i) (Tuple)
opField = 'F' // .Field(i) (Struct)
opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored)
opObj = 'O' // .Obj() (Named, TypeParam)
)
// For is equivalent to new(Encoder).For(obj).
//
// It may be more efficient to reuse a single Encoder across several calls.
func For(obj types.Object) (Path, error) {
return new(Encoder).For(obj)
}
// An Encoder amortizes the cost of encoding the paths of multiple objects.
// The zero value of an Encoder is ready to use.
type Encoder struct {
scopeMemo map[*types.Scope][]types.Object // memoization of scopeObjects
}
// For returns the path to an object relative to its package,
// or an error if the object is not accessible from the package's Scope.
//
// The For function guarantees to return a path only for the following objects:
// - package-level types
// - exported package-level non-types
// - methods
// - parameter and result variables
// - struct fields
// These objects are sufficient to define the API of their package.
// The objects described by a package's export data are drawn from this set.
//
// The set of objects accessible from a package's Scope depends on
// whether the package was produced by type-checking syntax, or
// reading export data; the latter may have a smaller Scope since
// export data trims objects that are not reachable from an exported
// declaration. For example, the For function will return a path for
// an exported method of an unexported type that is not reachable
// from any public declaration; this path will cause the Object
// function to fail if called on a package loaded from export data.
// TODO(adonovan): is this a bug or feature? Should this package
// compute accessibility in the same way?
//
// For does not return a path for predeclared names, imported package
// names, local names, and unexported package-level names (except
// types).
//
// Example: given this definition,
//
// package p
//
// type T interface {
// f() (a string, b struct{ X int })
// }
//
// For(X) would return a path that denotes the following sequence of operations:
//
// p.Scope().Lookup("T") (TypeName T)
// .Type().Underlying().Method(0). (method Func f)
// .Type().Results().At(1) (field Var b)
// .Type().Field(0) (field Var X)
//
// where p is the package (*types.Package) to which X belongs.
func (enc *Encoder) For(obj types.Object) (Path, error) {
pkg := obj.Pkg()
// This table lists the cases of interest.
//
// Object Action
// ------ ------
// nil reject
// builtin reject
// pkgname reject
// label reject
// var
// package-level accept
// func param/result accept
// local reject
// struct field accept
// const
// package-level accept
// local reject
// func
// package-level accept
// init functions reject
// concrete method accept
// interface method accept
// type
// package-level accept
// local reject
//
// The only accessible package-level objects are members of pkg itself.
//
// The cases are handled in four steps:
//
// 1. reject nil and builtin
// 2. accept package-level objects
// 3. reject obviously invalid objects
// 4. search the API for the path to the param/result/field/method.
// 1. reference to nil or builtin?
if pkg == nil {
return "", fmt.Errorf("predeclared %s has no path", obj)
}
scope := pkg.Scope()
// 2. package-level object?
if scope.Lookup(obj.Name()) == obj {
// Only exported objects (and non-exported types) have a path.
// Non-exported types may be referenced by other objects.
if _, ok := obj.(*types.TypeName); !ok && !obj.Exported() {
return "", fmt.Errorf("no path for non-exported %v", obj)
}
return Path(obj.Name()), nil
}
// 3. Not a package-level object.
// Reject obviously non-viable cases.
switch obj := obj.(type) {
case *types.TypeName:
if _, ok := types.Unalias(obj.Type()).(*types.TypeParam); !ok {
// With the exception of type parameters, only package-level type names
// have a path.
return "", fmt.Errorf("no path for %v", obj)
}
case *types.Const, // Only package-level constants have a path.
*types.Label, // Labels are function-local.
*types.PkgName: // PkgNames are file-local.
return "", fmt.Errorf("no path for %v", obj)
case *types.Var:
// Could be:
// - a field (obj.IsField())
// - a func parameter or result
// - a local var.
// Sadly there is no way to distinguish
// a param/result from a local
// so we must proceed to the find.
case *types.Func:
// A func, if not package-level, must be a method.
if recv := obj.Type().(*types.Signature).Recv(); recv == nil {
return "", fmt.Errorf("func is not a method: %v", obj)
}
if path, ok := enc.concreteMethod(obj); ok {
// Fast path for concrete methods that avoids looping over scope.
return path, nil
}
default:
panic(obj)
}
// 4. Search the API for the path to the var (field/param/result) or method.
// First inspect package-level named types.
// In the presence of path aliases, these give
// the best paths because non-types may
// refer to types, but not the reverse.
empty := make([]byte, 0, 48) // initial space
objs := enc.scopeObjects(scope)
for _, o := range objs {
tname, ok := o.(*types.TypeName)
if !ok {
continue // handle non-types in second pass
}
path := append(empty, o.Name()...)
path = append(path, opType)
T := o.Type()
if alias, ok := T.(*types.Alias); ok {
if r := findTypeParam(obj, aliases.TypeParams(alias), path, opTypeParam, nil); r != nil {
return Path(r), nil
}
if r := find(obj, aliases.Rhs(alias), append(path, opRhs), nil); r != nil {
return Path(r), nil
}
} else if tname.IsAlias() {
// legacy alias
if r := find(obj, T, path, nil); r != nil {
return Path(r), nil
}
} else if named, ok := T.(*types.Named); ok {
// defined (named) type
if r := findTypeParam(obj, named.TypeParams(), path, opTypeParam, nil); r != nil {
return Path(r), nil
}
if r := find(obj, named.Underlying(), append(path, opUnderlying), nil); r != nil {
return Path(r), nil
}
}
}
// Then inspect everything else:
// non-types, and declared methods of defined types.
for _, o := range objs {
path := append(empty, o.Name()...)
if _, ok := o.(*types.TypeName); !ok {
if o.Exported() {
// exported non-type (const, var, func)
if r := find(obj, o.Type(), append(path, opType), nil); r != nil {
return Path(r), nil
}
}
continue
}
// Inspect declared methods of defined types.
if T, ok := types.Unalias(o.Type()).(*types.Named); ok {
path = append(path, opType)
// The method index here is always with respect
// to the underlying go/types data structures,
// which ultimately derives from source order
// and must be preserved by export data.
for i := 0; i < T.NumMethods(); i++ {
m := T.Method(i)
path2 := appendOpArg(path, opMethod, i)
if m == obj {
return Path(path2), nil // found declared method
}
if r := find(obj, m.Type(), append(path2, opType), nil); r != nil {
return Path(r), nil
}
}
}
}
return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path())
}
func appendOpArg(path []byte, op byte, arg int) []byte {
path = append(path, op)
path = strconv.AppendInt(path, int64(arg), 10)
return path
}
// concreteMethod returns the path for meth, which must have a non-nil receiver.
// The second return value indicates success and may be false if the method is
// an interface method or if it is an instantiated method.
//
// This function is just an optimization that avoids the general scope walking
// approach. You are expected to fall back to the general approach if this
// function fails.
func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
// Concrete methods can only be declared on package-scoped named types. For
// that reason we can skip the expensive walk over the package scope: the
// path will always be package -> named type -> method. We can trivially get
// the type name from the receiver, and only have to look over the type's
// methods to find the method index.
//
// Methods on generic types require special consideration, however. Consider
// the following package:
//
// L1: type S[T any] struct{}
// L2: func (recv S[A]) Foo() { recv.Bar() }
// L3: func (recv S[B]) Bar() { }
// L4: type Alias = S[int]
// L5: func _[T any]() { var s S[int]; s.Foo() }
//
// The receivers of methods on generic types are instantiations. L2 and L3
// instantiate S with the type-parameters A and B, which are scoped to the
// respective methods. L4 and L5 each instantiate S with int. Each of these
// instantiations has its own method set, full of methods (and thus objects)
// with receivers whose types are the respective instantiations. In other
// words, we have
//
// S[A].Foo, S[A].Bar
// S[B].Foo, S[B].Bar
// S[int].Foo, S[int].Bar
//
// We may thus be trying to produce object paths for any of these objects.
//
// S[A].Foo and S[B].Bar are the origin methods, and their paths are S.Foo
// and S.Bar, which are the paths that this function naturally produces.
//
// S[A].Bar, S[B].Foo, and both methods on S[int] are instantiations that
// don't correspond to the origin methods. For S[int], this is significant.
// The most precise object path for S[int].Foo, for example, is Alias.Foo,
// not S.Foo. Our function, however, would produce S.Foo, which would
// resolve to a different object.
//
// For S[A].Bar and S[B].Foo it could be argued that S.Bar and S.Foo are
// still the correct paths, since only the origin methods have meaningful
// paths. But this is likely only true for trivial cases and has edge cases.
// Since this function is only an optimization, we err on the side of giving
// up, deferring to the slower but definitely correct algorithm. Most users
// of objectpath will only be giving us origin methods, anyway, as referring
// to instantiated methods is usually not useful.
if meth.Origin() != meth {
return "", false
}
_, named := typesinternal.ReceiverNamed(meth.Type().(*types.Signature).Recv())
if named == nil {
return "", false
}
if types.IsInterface(named) {
// Named interfaces don't have to be package-scoped
//
// TODO(dominikh): opt: if scope.Lookup(name) == named, then we can apply this optimization to interface
// methods, too, I think.
return "", false
}
// Preallocate space for the name, opType, opMethod, and some digits.
name := named.Obj().Name()
path := make([]byte, 0, len(name)+8)
path = append(path, name...)
path = append(path, opType)
// Method indices are w.r.t. the go/types data structures,
// ultimately deriving from source order,
// which is preserved by export data.
for i := 0; i < named.NumMethods(); i++ {
if named.Method(i) == meth {
path = appendOpArg(path, opMethod, i)
return Path(path), true
}
}
// Due to golang/go#59944, go/types fails to associate the receiver with
// certain methods on cgo types.
//
// TODO(rfindley): replace this panic once golang/go#59944 is fixed in all Go
// versions gopls supports.
return "", false
// panic(fmt.Sprintf("couldn't find method %s on type %s; methods: %#v", meth, named, enc.namedMethods(named)))
}
// find finds obj within type T, returning the path to it, or nil if not found.
//
// The seen map is used to short circuit cycles through type parameters. If
// nil, it will be allocated as necessary.
func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte {
switch T := T.(type) {
case *types.Alias:
return find(obj, types.Unalias(T), path, seen)
case *types.Basic, *types.Named:
// Named types belonging to pkg were handled already,
// so T must belong to another package. No path.
return nil
case *types.Pointer:
return find(obj, T.Elem(), append(path, opElem), seen)
case *types.Slice:
return find(obj, T.Elem(), append(path, opElem), seen)
case *types.Array:
return find(obj, T.Elem(), append(path, opElem), seen)
case *types.Chan:
return find(obj, T.Elem(), append(path, opElem), seen)
case *types.Map:
if r := find(obj, T.Key(), append(path, opKey), seen); r != nil {
return r
}
return find(obj, T.Elem(), append(path, opElem), seen)
case *types.Signature:
if r := findTypeParam(obj, T.RecvTypeParams(), path, opRecvTypeParam, nil); r != nil {
return r
}
if r := findTypeParam(obj, T.TypeParams(), path, opTypeParam, seen); r != nil {
return r
}
if r := find(obj, T.Params(), append(path, opParams), seen); r != nil {
return r
}
return find(obj, T.Results(), append(path, opResults), seen)
case *types.Struct:
for i := 0; i < T.NumFields(); i++ {
fld := T.Field(i)
path2 := appendOpArg(path, opField, i)
if fld == obj {
return path2 // found field var
}
if r := find(obj, fld.Type(), append(path2, opType), seen); r != nil {
return r
}
}
return nil
case *types.Tuple:
for i := 0; i < T.Len(); i++ {
v := T.At(i)
path2 := appendOpArg(path, opAt, i)
if v == obj {
return path2 // found param/result var
}
if r := find(obj, v.Type(), append(path2, opType), seen); r != nil {
return r
}
}
return nil
case *types.Interface:
for i := 0; i < T.NumMethods(); i++ {
m := T.Method(i)
path2 := appendOpArg(path, opMethod, i)
if m == obj {
return path2 // found interface method
}
if r := find(obj, m.Type(), append(path2, opType), seen); r != nil {
return r
}
}
return nil
case *types.TypeParam:
name := T.Obj()
if name == obj {
return append(path, opObj)
}
if seen[name] {
return nil
}
if seen == nil {
seen = make(map[*types.TypeName]bool)
}
seen[name] = true
if r := find(obj, T.Constraint(), append(path, opConstraint), seen); r != nil {
return r
}
return nil
}
panic(T)
}
func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, op byte, seen map[*types.TypeName]bool) []byte {
for i := 0; i < list.Len(); i++ {
tparam := list.At(i)
path2 := appendOpArg(path, op, i)
if r := find(obj, tparam, path2, seen); r != nil {
return r
}
}
return nil
}
// Object returns the object denoted by path p within the package pkg.
func Object(pkg *types.Package, p Path) (types.Object, error) {
pathstr := string(p)
if pathstr == "" {
return nil, fmt.Errorf("empty path")
}
var pkgobj, suffix string
if dot := strings.IndexByte(pathstr, opType); dot < 0 {
pkgobj = pathstr
} else {
pkgobj = pathstr[:dot]
suffix = pathstr[dot:] // suffix starts with "."
}
obj := pkg.Scope().Lookup(pkgobj)
if obj == nil {
return nil, fmt.Errorf("package %s does not contain %q", pkg.Path(), pkgobj)
}
// abstraction of *types.{Pointer,Slice,Array,Chan,Map}
type hasElem interface {
Elem() types.Type
}
// abstraction of *types.{Named,Signature}
type hasTypeParams interface {
TypeParams() *types.TypeParamList
}
// abstraction of *types.{Named,TypeParam}
type hasObj interface {
Obj() *types.TypeName
}
// The loop state is the pair (t, obj),
// exactly one of which is non-nil, initially obj.
// All suffixes start with '.' (the only object->type operation),
// followed by optional type->type operations,
// then a type->object operation.
// The cycle then repeats.
var t types.Type
for suffix != "" {
code := suffix[0]
suffix = suffix[1:]
// Codes [AFMTr] have an integer operand.
var index int
switch code {
case opAt, opField, opMethod, opTypeParam, opRecvTypeParam:
rest := strings.TrimLeft(suffix, "0123456789")
numerals := suffix[:len(suffix)-len(rest)]
suffix = rest
i, err := strconv.Atoi(numerals)
if err != nil {
return nil, fmt.Errorf("invalid path: bad numeric operand %q for code %q", numerals, code)
}
index = int(i)
case opObj:
// no operand
default:
// The suffix must end with a type->object operation.
if suffix == "" {
return nil, fmt.Errorf("invalid path: ends with %q, want [AFMO]", code)
}
}
if code == opType {
if t != nil {
return nil, fmt.Errorf("invalid path: unexpected %q in type context", opType)
}
t = obj.Type()
obj = nil
continue
}
if t == nil {
return nil, fmt.Errorf("invalid path: code %q in object context", code)
}
// Inv: t != nil, obj == nil
t = types.Unalias(t)
switch code {
case opElem:
hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want pointer, slice, array, chan or map)", code, t, t)
}
t = hasElem.Elem()
case opKey:
mapType, ok := t.(*types.Map)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want map)", code, t, t)
}
t = mapType.Key()
case opParams:
sig, ok := t.(*types.Signature)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
}
t = sig.Params()
case opResults:
sig, ok := t.(*types.Signature)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
}
t = sig.Results()
case opUnderlying:
named, ok := t.(*types.Named)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named)", code, t, t)
}
t = named.Underlying()
case opRhs:
if alias, ok := t.(*types.Alias); ok {
t = aliases.Rhs(alias)
} else if false && aliases.Enabled() {
// The Enabled check is too expensive, so for now we
// simply assume that aliases are not enabled.
// TODO(adonovan): replace with "if true {" when go1.24 is assured.
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want alias)", code, t, t)
}
case opTypeParam:
hasTypeParams, ok := t.(hasTypeParams) // Named, Signature
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or signature)", code, t, t)
}
tparams := hasTypeParams.TypeParams()
if n := tparams.Len(); index >= n {
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
}
t = tparams.At(index)
case opRecvTypeParam:
sig, ok := t.(*types.Signature) // Signature
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
}
rtparams := sig.RecvTypeParams()
if n := rtparams.Len(); index >= n {
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
}
t = rtparams.At(index)
case opConstraint:
tparam, ok := t.(*types.TypeParam)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want type parameter)", code, t, t)
}
t = tparam.Constraint()
case opAt:
tuple, ok := t.(*types.Tuple)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want tuple)", code, t, t)
}
if n := tuple.Len(); index >= n {
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
}
obj = tuple.At(index)
t = nil
case opField:
structType, ok := t.(*types.Struct)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want struct)", code, t, t)
}
if n := structType.NumFields(); index >= n {
return nil, fmt.Errorf("field index %d out of range [0-%d)", index, n)
}
obj = structType.Field(index)
t = nil
case opMethod:
switch t := t.(type) {
case *types.Interface:
if index >= t.NumMethods() {
return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods())
}
obj = t.Method(index) // Id-ordered
case *types.Named:
if index >= t.NumMethods() {
return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods())
}
obj = t.Method(index)
default:
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want interface or named)", code, t, t)
}
t = nil
case opObj:
hasObj, ok := t.(hasObj)
if !ok {
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or type param)", code, t, t)
}
obj = hasObj.Obj()
t = nil
default:
return nil, fmt.Errorf("invalid path: unknown code %q", code)
}
}
if obj == nil {
panic(p) // path does not end in an object-valued operator
}
if obj.Pkg() != pkg {
return nil, fmt.Errorf("path denotes %s, which belongs to a different package", obj)
}
return obj, nil // success
}
// scopeObjects is a memoization of scope objects.
// Callers must not modify the result.
func (enc *Encoder) scopeObjects(scope *types.Scope) []types.Object {
m := enc.scopeMemo
if m == nil {
m = make(map[*types.Scope][]types.Object)
enc.scopeMemo = m
}
objs, ok := m[scope]
if !ok {
names := scope.Names() // allocates and sorts
objs = make([]types.Object, len(names))
for i, name := range names {
objs[i] = scope.Lookup(name)
}
m[scope] = objs
}
return objs
}

68
vendor/golang.org/x/tools/go/types/typeutil/callee.go generated vendored Normal file
View File

@@ -0,0 +1,68 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package typeutil
import (
"go/ast"
"go/types"
"golang.org/x/tools/internal/typeparams"
)
// Callee returns the named target of a function call, if any:
// a function, method, builtin, or variable.
//
// Functions and methods may potentially have type parameters.
func Callee(info *types.Info, call *ast.CallExpr) types.Object {
fun := ast.Unparen(call.Fun)
// Look through type instantiation if necessary.
isInstance := false
switch fun.(type) {
case *ast.IndexExpr, *ast.IndexListExpr:
// When extracting the callee from an *IndexExpr, we need to check that
// it is a *types.Func and not a *types.Var.
// Example: Don't match a slice m within the expression `m[0]()`.
isInstance = true
fun, _, _, _ = typeparams.UnpackIndexExpr(fun)
}
var obj types.Object
switch fun := fun.(type) {
case *ast.Ident:
obj = info.Uses[fun] // type, var, builtin, or declared func
case *ast.SelectorExpr:
if sel, ok := info.Selections[fun]; ok {
obj = sel.Obj() // method or field
} else {
obj = info.Uses[fun.Sel] // qualified identifier?
}
}
if _, ok := obj.(*types.TypeName); ok {
return nil // T(x) is a conversion, not a call
}
// A Func is required to match instantiations.
if _, ok := obj.(*types.Func); isInstance && !ok {
return nil // Was not a Func.
}
return obj
}
// StaticCallee returns the target (function or method) of a static function
// call, if any. It returns nil for calls to builtins.
//
// Note: for calls of instantiated functions and methods, StaticCallee returns
// the corresponding generic function or method on the generic type.
func StaticCallee(info *types.Info, call *ast.CallExpr) *types.Func {
if f, ok := Callee(info, call).(*types.Func); ok && !interfaceMethod(f) {
return f
}
return nil
}
func interfaceMethod(f *types.Func) bool {
recv := f.Type().(*types.Signature).Recv()
return recv != nil && types.IsInterface(recv.Type())
}

30
vendor/golang.org/x/tools/go/types/typeutil/imports.go generated vendored Normal file
View File

@@ -0,0 +1,30 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package typeutil
import "go/types"
// Dependencies returns all dependencies of the specified packages.
//
// Dependent packages appear in topological order: if package P imports
// package Q, Q appears earlier than P in the result.
// The algorithm follows import statements in the order they
// appear in the source code, so the result is a total order.
func Dependencies(pkgs ...*types.Package) []*types.Package {
var result []*types.Package
seen := make(map[*types.Package]bool)
var visit func(pkgs []*types.Package)
visit = func(pkgs []*types.Package) {
for _, p := range pkgs {
if !seen[p] {
seen[p] = true
visit(p.Imports())
result = append(result, p)
}
}
}
visit(pkgs)
return result
}

517
vendor/golang.org/x/tools/go/types/typeutil/map.go generated vendored Normal file
View File

@@ -0,0 +1,517 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package typeutil defines various utilities for types, such as Map,
// a mapping from types.Type to any values.
package typeutil // import "golang.org/x/tools/go/types/typeutil"
import (
"bytes"
"fmt"
"go/types"
"reflect"
"golang.org/x/tools/internal/typeparams"
)
// Map is a hash-table-based mapping from types (types.Type) to
// arbitrary any values. The concrete types that implement
// the Type interface are pointers. Since they are not canonicalized,
// == cannot be used to check for equivalence, and thus we cannot
// simply use a Go map.
//
// Just as with map[K]V, a nil *Map is a valid empty map.
//
// Not thread-safe.
type Map struct {
hasher Hasher // shared by many Maps
table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused
length int // number of map entries
}
// entry is an entry (key/value association) in a hash bucket.
type entry struct {
key types.Type
value any
}
// SetHasher sets the hasher used by Map.
//
// All Hashers are functionally equivalent but contain internal state
// used to cache the results of hashing previously seen types.
//
// A single Hasher created by MakeHasher() may be shared among many
// Maps. This is recommended if the instances have many keys in
// common, as it will amortize the cost of hash computation.
//
// A Hasher may grow without bound as new types are seen. Even when a
// type is deleted from the map, the Hasher never shrinks, since other
// types in the map may reference the deleted type indirectly.
//
// Hashers are not thread-safe, and read-only operations such as
// Map.Lookup require updates to the hasher, so a full Mutex lock (not a
// read-lock) is require around all Map operations if a shared
// hasher is accessed from multiple threads.
//
// If SetHasher is not called, the Map will create a private hasher at
// the first call to Insert.
func (m *Map) SetHasher(hasher Hasher) {
m.hasher = hasher
}
// Delete removes the entry with the given key, if any.
// It returns true if the entry was found.
func (m *Map) Delete(key types.Type) bool {
if m != nil && m.table != nil {
hash := m.hasher.Hash(key)
bucket := m.table[hash]
for i, e := range bucket {
if e.key != nil && types.Identical(key, e.key) {
// We can't compact the bucket as it
// would disturb iterators.
bucket[i] = entry{}
m.length--
return true
}
}
}
return false
}
// At returns the map entry for the given key.
// The result is nil if the entry is not present.
func (m *Map) At(key types.Type) any {
if m != nil && m.table != nil {
for _, e := range m.table[m.hasher.Hash(key)] {
if e.key != nil && types.Identical(key, e.key) {
return e.value
}
}
}
return nil
}
// Set sets the map entry for key to val,
// and returns the previous entry, if any.
func (m *Map) Set(key types.Type, value any) (prev any) {
if m.table != nil {
hash := m.hasher.Hash(key)
bucket := m.table[hash]
var hole *entry
for i, e := range bucket {
if e.key == nil {
hole = &bucket[i]
} else if types.Identical(key, e.key) {
prev = e.value
bucket[i].value = value
return
}
}
if hole != nil {
*hole = entry{key, value} // overwrite deleted entry
} else {
m.table[hash] = append(bucket, entry{key, value})
}
} else {
if m.hasher.memo == nil {
m.hasher = MakeHasher()
}
hash := m.hasher.Hash(key)
m.table = map[uint32][]entry{hash: {entry{key, value}}}
}
m.length++
return
}
// Len returns the number of map entries.
func (m *Map) Len() int {
if m != nil {
return m.length
}
return 0
}
// Iterate calls function f on each entry in the map in unspecified order.
//
// If f should mutate the map, Iterate provides the same guarantees as
// Go maps: if f deletes a map entry that Iterate has not yet reached,
// f will not be invoked for it, but if f inserts a map entry that
// Iterate has not yet reached, whether or not f will be invoked for
// it is unspecified.
func (m *Map) Iterate(f func(key types.Type, value any)) {
if m != nil {
for _, bucket := range m.table {
for _, e := range bucket {
if e.key != nil {
f(e.key, e.value)
}
}
}
}
}
// Keys returns a new slice containing the set of map keys.
// The order is unspecified.
func (m *Map) Keys() []types.Type {
keys := make([]types.Type, 0, m.Len())
m.Iterate(func(key types.Type, _ any) {
keys = append(keys, key)
})
return keys
}
func (m *Map) toString(values bool) string {
if m == nil {
return "{}"
}
var buf bytes.Buffer
fmt.Fprint(&buf, "{")
sep := ""
m.Iterate(func(key types.Type, value any) {
fmt.Fprint(&buf, sep)
sep = ", "
fmt.Fprint(&buf, key)
if values {
fmt.Fprintf(&buf, ": %q", value)
}
})
fmt.Fprint(&buf, "}")
return buf.String()
}
// String returns a string representation of the map's entries.
// Values are printed using fmt.Sprintf("%v", v).
// Order is unspecified.
func (m *Map) String() string {
return m.toString(true)
}
// KeysString returns a string representation of the map's key set.
// Order is unspecified.
func (m *Map) KeysString() string {
return m.toString(false)
}
////////////////////////////////////////////////////////////////////////
// Hasher
// A Hasher maps each type to its hash value.
// For efficiency, a hasher uses memoization; thus its memory
// footprint grows monotonically over time.
// Hashers are not thread-safe.
// Hashers have reference semantics.
// Call MakeHasher to create a Hasher.
type Hasher struct {
memo map[types.Type]uint32
// ptrMap records pointer identity.
ptrMap map[any]uint32
// sigTParams holds type parameters from the signature being hashed.
// Signatures are considered identical modulo renaming of type parameters, so
// within the scope of a signature type the identity of the signature's type
// parameters is just their index.
//
// Since the language does not currently support referring to uninstantiated
// generic types or functions, and instantiated signatures do not have type
// parameter lists, we should never encounter a second non-empty type
// parameter list when hashing a generic signature.
sigTParams *types.TypeParamList
}
// MakeHasher returns a new Hasher instance.
func MakeHasher() Hasher {
return Hasher{
memo: make(map[types.Type]uint32),
ptrMap: make(map[any]uint32),
sigTParams: nil,
}
}
// Hash computes a hash value for the given type t such that
// Identical(t, t') => Hash(t) == Hash(t').
func (h Hasher) Hash(t types.Type) uint32 {
hash, ok := h.memo[t]
if !ok {
hash = h.hashFor(t)
h.memo[t] = hash
}
return hash
}
// hashString computes the FowlerNollVo hash of s.
func hashString(s string) uint32 {
var h uint32
for i := 0; i < len(s); i++ {
h ^= uint32(s[i])
h *= 16777619
}
return h
}
// hashFor computes the hash of t.
func (h Hasher) hashFor(t types.Type) uint32 {
// See Identical for rationale.
switch t := t.(type) {
case *types.Basic:
return uint32(t.Kind())
case *types.Alias:
return h.Hash(types.Unalias(t))
case *types.Array:
return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem())
case *types.Slice:
return 9049 + 2*h.Hash(t.Elem())
case *types.Struct:
var hash uint32 = 9059
for i, n := 0, t.NumFields(); i < n; i++ {
f := t.Field(i)
if f.Anonymous() {
hash += 8861
}
hash += hashString(t.Tag(i))
hash += hashString(f.Name()) // (ignore f.Pkg)
hash += h.Hash(f.Type())
}
return hash
case *types.Pointer:
return 9067 + 2*h.Hash(t.Elem())
case *types.Signature:
var hash uint32 = 9091
if t.Variadic() {
hash *= 8863
}
// Use a separate hasher for types inside of the signature, where type
// parameter identity is modified to be (index, constraint). We must use a
// new memo for this hasher as type identity may be affected by this
// masking. For example, in func[T any](*T), the identity of *T depends on
// whether we are mapping the argument in isolation, or recursively as part
// of hashing the signature.
//
// We should never encounter a generic signature while hashing another
// generic signature, but defensively set sigTParams only if h.mask is
// unset.
tparams := t.TypeParams()
if h.sigTParams == nil && tparams.Len() != 0 {
h = Hasher{
// There may be something more efficient than discarding the existing
// memo, but it would require detecting whether types are 'tainted' by
// references to type parameters.
memo: make(map[types.Type]uint32),
// Re-using ptrMap ensures that pointer identity is preserved in this
// hasher.
ptrMap: h.ptrMap,
sigTParams: tparams,
}
}
for i := 0; i < tparams.Len(); i++ {
tparam := tparams.At(i)
hash += 7 * h.Hash(tparam.Constraint())
}
return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
case *types.Union:
return h.hashUnion(t)
case *types.Interface:
// Interfaces are identical if they have the same set of methods, with
// identical names and types, and they have the same set of type
// restrictions. See go/types.identical for more details.
var hash uint32 = 9103
// Hash methods.
for i, n := 0, t.NumMethods(); i < n; i++ {
// Method order is not significant.
// Ignore m.Pkg().
m := t.Method(i)
// Use shallow hash on method signature to
// avoid anonymous interface cycles.
hash += 3*hashString(m.Name()) + 5*h.shallowHash(m.Type())
}
// Hash type restrictions.
terms, err := typeparams.InterfaceTermSet(t)
// if err != nil t has invalid type restrictions.
if err == nil {
hash += h.hashTermSet(terms)
}
return hash
case *types.Map:
return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem())
case *types.Chan:
return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem())
case *types.Named:
hash := h.hashPtr(t.Obj())
targs := t.TypeArgs()
for i := 0; i < targs.Len(); i++ {
targ := targs.At(i)
hash += 2 * h.Hash(targ)
}
return hash
case *types.TypeParam:
return h.hashTypeParam(t)
case *types.Tuple:
return h.hashTuple(t)
}
panic(fmt.Sprintf("%T: %v", t, t))
}
func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
// See go/types.identicalTypes for rationale.
n := tuple.Len()
hash := 9137 + 2*uint32(n)
for i := 0; i < n; i++ {
hash += 3 * h.Hash(tuple.At(i).Type())
}
return hash
}
func (h Hasher) hashUnion(t *types.Union) uint32 {
// Hash type restrictions.
terms, err := typeparams.UnionTermSet(t)
// if err != nil t has invalid type restrictions. Fall back on a non-zero
// hash.
if err != nil {
return 9151
}
return h.hashTermSet(terms)
}
func (h Hasher) hashTermSet(terms []*types.Term) uint32 {
hash := 9157 + 2*uint32(len(terms))
for _, term := range terms {
// term order is not significant.
termHash := h.Hash(term.Type())
if term.Tilde() {
termHash *= 9161
}
hash += 3 * termHash
}
return hash
}
// hashTypeParam returns a hash of the type parameter t, with a hash value
// depending on whether t is contained in h.sigTParams.
//
// If h.sigTParams is set and contains t, then we are in the process of hashing
// a signature, and the hash value of t must depend only on t's index and
// constraint: signatures are considered identical modulo type parameter
// renaming. To avoid infinite recursion, we only hash the type parameter
// index, and rely on types.Identical to handle signatures where constraints
// are not identical.
//
// Otherwise the hash of t depends only on t's pointer identity.
func (h Hasher) hashTypeParam(t *types.TypeParam) uint32 {
if h.sigTParams != nil {
i := t.Index()
if i >= 0 && i < h.sigTParams.Len() && t == h.sigTParams.At(i) {
return 9173 + 3*uint32(i)
}
}
return h.hashPtr(t.Obj())
}
// hashPtr hashes the pointer identity of ptr. It uses h.ptrMap to ensure that
// pointers values are not dependent on the GC.
func (h Hasher) hashPtr(ptr any) uint32 {
if hash, ok := h.ptrMap[ptr]; ok {
return hash
}
hash := uint32(reflect.ValueOf(ptr).Pointer())
h.ptrMap[ptr] = hash
return hash
}
// shallowHash computes a hash of t without looking at any of its
// element Types, to avoid potential anonymous cycles in the types of
// interface methods.
//
// When an unnamed non-empty interface type appears anywhere among the
// arguments or results of an interface method, there is a potential
// for endless recursion. Consider:
//
// type X interface { m() []*interface { X } }
//
// The problem is that the Methods of the interface in m's result type
// include m itself; there is no mention of the named type X that
// might help us break the cycle.
// (See comment in go/types.identical, case *Interface, for more.)
func (h Hasher) shallowHash(t types.Type) uint32 {
// t is the type of an interface method (Signature),
// its params or results (Tuples), or their immediate
// elements (mostly Slice, Pointer, Basic, Named),
// so there's no need to optimize anything else.
switch t := t.(type) {
case *types.Alias:
return h.shallowHash(types.Unalias(t))
case *types.Signature:
var hash uint32 = 604171
if t.Variadic() {
hash *= 971767
}
// The Signature/Tuple recursion is always finite
// and invariably shallow.
return hash + 1062599*h.shallowHash(t.Params()) + 1282529*h.shallowHash(t.Results())
case *types.Tuple:
n := t.Len()
hash := 9137 + 2*uint32(n)
for i := 0; i < n; i++ {
hash += 53471161 * h.shallowHash(t.At(i).Type())
}
return hash
case *types.Basic:
return 45212177 * uint32(t.Kind())
case *types.Array:
return 1524181 + 2*uint32(t.Len())
case *types.Slice:
return 2690201
case *types.Struct:
return 3326489
case *types.Pointer:
return 4393139
case *types.Union:
return 562448657
case *types.Interface:
return 2124679 // no recursion here
case *types.Map:
return 9109
case *types.Chan:
return 9127
case *types.Named:
return h.hashPtr(t.Obj())
case *types.TypeParam:
return h.hashPtr(t.Obj())
}
panic(fmt.Sprintf("shallowHash: %T: %v", t, t))
}

View File

@@ -0,0 +1,71 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements a cache of method sets.
package typeutil
import (
"go/types"
"sync"
)
// A MethodSetCache records the method set of each type T for which
// MethodSet(T) is called so that repeat queries are fast.
// The zero value is a ready-to-use cache instance.
type MethodSetCache struct {
mu sync.Mutex
named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N
others map[types.Type]*types.MethodSet // all other types
}
// MethodSet returns the method set of type T. It is thread-safe.
//
// If cache is nil, this function is equivalent to types.NewMethodSet(T).
// Utility functions can thus expose an optional *MethodSetCache
// parameter to clients that care about performance.
func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet {
if cache == nil {
return types.NewMethodSet(T)
}
cache.mu.Lock()
defer cache.mu.Unlock()
switch T := types.Unalias(T).(type) {
case *types.Named:
return cache.lookupNamed(T).value
case *types.Pointer:
if N, ok := types.Unalias(T.Elem()).(*types.Named); ok {
return cache.lookupNamed(N).pointer
}
}
// all other types
// (The map uses pointer equivalence, not type identity.)
mset := cache.others[T]
if mset == nil {
mset = types.NewMethodSet(T)
if cache.others == nil {
cache.others = make(map[types.Type]*types.MethodSet)
}
cache.others[T] = mset
}
return mset
}
func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } {
if cache.named == nil {
cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet })
}
// Avoid recomputing mset(*T) for each distinct Pointer
// instance whose underlying type is a named type.
msets, ok := cache.named[named]
if !ok {
msets.value = types.NewMethodSet(named)
msets.pointer = types.NewMethodSet(types.NewPointer(named))
cache.named[named] = msets
}
return msets
}

53
vendor/golang.org/x/tools/go/types/typeutil/ui.go generated vendored Normal file
View File

@@ -0,0 +1,53 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package typeutil
// This file defines utilities for user interfaces that display types.
import (
"go/types"
)
// IntuitiveMethodSet returns the intuitive method set of a type T,
// which is the set of methods you can call on an addressable value of
// that type.
//
// The result always contains MethodSet(T), and is exactly MethodSet(T)
// for interface types and for pointer-to-concrete types.
// For all other concrete types T, the result additionally
// contains each method belonging to *T if there is no identically
// named method on T itself.
//
// This corresponds to user intuition about method sets;
// this function is intended only for user interfaces.
//
// The order of the result is as for types.MethodSet(T).
func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection {
isPointerToConcrete := func(T types.Type) bool {
ptr, ok := types.Unalias(T).(*types.Pointer)
return ok && !types.IsInterface(ptr.Elem())
}
var result []*types.Selection
mset := msets.MethodSet(T)
if types.IsInterface(T) || isPointerToConcrete(T) {
for i, n := 0, mset.Len(); i < n; i++ {
result = append(result, mset.At(i))
}
} else {
// T is some other concrete type.
// Report methods of T and *T, preferring those of T.
pmset := msets.MethodSet(types.NewPointer(T))
for i, n := 0, pmset.Len(); i < n; i++ {
meth := pmset.At(i)
if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil {
meth = m
}
result = append(result, meth)
}
}
return result
}

View File

@@ -7,8 +7,8 @@
package imports // import "golang.org/x/tools/imports"
import (
"io/ioutil"
"log"
"os"
"golang.org/x/tools/internal/gocommand"
intimp "golang.org/x/tools/internal/imports"
@@ -44,7 +44,7 @@ var LocalPrefix string
func Process(filename string, src []byte, opt *Options) ([]byte, error) {
var err error
if src == nil {
src, err = ioutil.ReadFile(filename)
src, err = os.ReadFile(filename)
if err != nil {
return nil, err
}

38
vendor/golang.org/x/tools/internal/aliases/aliases.go generated vendored Normal file
View File

@@ -0,0 +1,38 @@
// Copyright 2024 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 aliases
import (
"go/token"
"go/types"
)
// Package aliases defines backward compatible shims
// for the types.Alias type representation added in 1.22.
// This defines placeholders for x/tools until 1.26.
// NewAlias creates a new TypeName in Package pkg that
// is an alias for the type rhs.
//
// The enabled parameter determines whether the resulting [TypeName]'s
// type is an [types.Alias]. Its value must be the result of a call to
// [Enabled], which computes the effective value of
// GODEBUG=gotypesalias=... by invoking the type checker. The Enabled
// function is expensive and should be called once per task (e.g.
// package import), not once per call to NewAlias.
//
// Precondition: enabled || len(tparams)==0.
// If materialized aliases are disabled, there must not be any type parameters.
func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type, tparams []*types.TypeParam) *types.TypeName {
if enabled {
tname := types.NewTypeName(pos, pkg, name, nil)
SetTypeParams(types.NewAlias(tname, rhs), tparams)
return tname
}
if len(tparams) > 0 {
panic("cannot create an alias with type parameters when gotypesalias is not enabled")
}
return types.NewTypeName(pos, pkg, name, rhs)
}

View File

@@ -0,0 +1,80 @@
// Copyright 2024 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 aliases
import (
"go/ast"
"go/parser"
"go/token"
"go/types"
)
// Rhs returns the type on the right-hand side of the alias declaration.
func Rhs(alias *types.Alias) types.Type {
if alias, ok := any(alias).(interface{ Rhs() types.Type }); ok {
return alias.Rhs() // go1.23+
}
// go1.22's Alias didn't have the Rhs method,
// so Unalias is the best we can do.
return types.Unalias(alias)
}
// TypeParams returns the type parameter list of the alias.
func TypeParams(alias *types.Alias) *types.TypeParamList {
if alias, ok := any(alias).(interface{ TypeParams() *types.TypeParamList }); ok {
return alias.TypeParams() // go1.23+
}
return nil
}
// SetTypeParams sets the type parameters of the alias type.
func SetTypeParams(alias *types.Alias, tparams []*types.TypeParam) {
if alias, ok := any(alias).(interface {
SetTypeParams(tparams []*types.TypeParam)
}); ok {
alias.SetTypeParams(tparams) // go1.23+
} else if len(tparams) > 0 {
panic("cannot set type parameters of an Alias type in go1.22")
}
}
// TypeArgs returns the type arguments used to instantiate the Alias type.
func TypeArgs(alias *types.Alias) *types.TypeList {
if alias, ok := any(alias).(interface{ TypeArgs() *types.TypeList }); ok {
return alias.TypeArgs() // go1.23+
}
return nil // empty (go1.22)
}
// Origin returns the generic Alias type of which alias is an instance.
// If alias is not an instance of a generic alias, Origin returns alias.
func Origin(alias *types.Alias) *types.Alias {
if alias, ok := any(alias).(interface{ Origin() *types.Alias }); ok {
return alias.Origin() // go1.23+
}
return alias // not an instance of a generic alias (go1.22)
}
// Enabled reports whether [NewAlias] should create [types.Alias] types.
//
// This function is expensive! Call it sparingly.
func Enabled() bool {
// The only reliable way to compute the answer is to invoke go/types.
// We don't parse the GODEBUG environment variable, because
// (a) it's tricky to do so in a manner that is consistent
// with the godebug package; in particular, a simple
// substring check is not good enough. The value is a
// rightmost-wins list of options. But more importantly:
// (b) it is impossible to detect changes to the effective
// setting caused by os.Setenv("GODEBUG"), as happens in
// many tests. Therefore any attempt to cache the result
// is just incorrect.
fset := token.NewFileSet()
f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", parser.SkipObjectResolution)
pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil)
_, enabled := pkg.Scope().Lookup("A").Type().(*types.Alias)
return enabled
}

21
vendor/golang.org/x/tools/internal/event/keys/util.go generated vendored Normal file
View File

@@ -0,0 +1,21 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package keys
import (
"sort"
"strings"
)
// Join returns a canonical join of the keys in S:
// a sorted comma-separated string list.
func Join[S ~[]T, T ~string](s S) string {
strs := make([]string, 0, len(s))
for _, v := range s {
strs = append(strs, string(v))
}
sort.Strings(strs)
return strings.Join(strs, ",")
}

View File

@@ -1,196 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package fastwalk provides a faster version of filepath.Walk for file system
// scanning tools.
package fastwalk
import (
"errors"
"os"
"path/filepath"
"runtime"
"sync"
)
// ErrTraverseLink is used as a return value from WalkFuncs to indicate that the
// symlink named in the call may be traversed.
var ErrTraverseLink = errors.New("fastwalk: traverse symlink, assuming target is a directory")
// ErrSkipFiles is a used as a return value from WalkFuncs to indicate that the
// callback should not be called for any other files in the current directory.
// Child directories will still be traversed.
var ErrSkipFiles = errors.New("fastwalk: skip remaining files in directory")
// Walk is a faster implementation of filepath.Walk.
//
// filepath.Walk's design necessarily calls os.Lstat on each file,
// even if the caller needs less info.
// Many tools need only the type of each file.
// On some platforms, this information is provided directly by the readdir
// system call, avoiding the need to stat each file individually.
// fastwalk_unix.go contains a fork of the syscall routines.
//
// See golang.org/issue/16399
//
// Walk walks the file tree rooted at root, calling walkFn for
// each file or directory in the tree, including root.
//
// If fastWalk returns filepath.SkipDir, the directory is skipped.
//
// Unlike filepath.Walk:
// - file stat calls must be done by the user.
// The only provided metadata is the file type, which does not include
// any permission bits.
// - multiple goroutines stat the filesystem concurrently. The provided
// walkFn must be safe for concurrent use.
// - fastWalk can follow symlinks if walkFn returns the TraverseLink
// sentinel error. It is the walkFn's responsibility to prevent
// fastWalk from going into symlink cycles.
func Walk(root string, walkFn func(path string, typ os.FileMode) error) error {
// TODO(bradfitz): make numWorkers configurable? We used a
// minimum of 4 to give the kernel more info about multiple
// things we want, in hopes its I/O scheduling can take
// advantage of that. Hopefully most are in cache. Maybe 4 is
// even too low of a minimum. Profile more.
numWorkers := 4
if n := runtime.NumCPU(); n > numWorkers {
numWorkers = n
}
// Make sure to wait for all workers to finish, otherwise
// walkFn could still be called after returning. This Wait call
// runs after close(e.donec) below.
var wg sync.WaitGroup
defer wg.Wait()
w := &walker{
fn: walkFn,
enqueuec: make(chan walkItem, numWorkers), // buffered for performance
workc: make(chan walkItem, numWorkers), // buffered for performance
donec: make(chan struct{}),
// buffered for correctness & not leaking goroutines:
resc: make(chan error, numWorkers),
}
defer close(w.donec)
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go w.doWork(&wg)
}
todo := []walkItem{{dir: root}}
out := 0
for {
workc := w.workc
var workItem walkItem
if len(todo) == 0 {
workc = nil
} else {
workItem = todo[len(todo)-1]
}
select {
case workc <- workItem:
todo = todo[:len(todo)-1]
out++
case it := <-w.enqueuec:
todo = append(todo, it)
case err := <-w.resc:
out--
if err != nil {
return err
}
if out == 0 && len(todo) == 0 {
// It's safe to quit here, as long as the buffered
// enqueue channel isn't also readable, which might
// happen if the worker sends both another unit of
// work and its result before the other select was
// scheduled and both w.resc and w.enqueuec were
// readable.
select {
case it := <-w.enqueuec:
todo = append(todo, it)
default:
return nil
}
}
}
}
}
// doWork reads directories as instructed (via workc) and runs the
// user's callback function.
func (w *walker) doWork(wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case <-w.donec:
return
case it := <-w.workc:
select {
case <-w.donec:
return
case w.resc <- w.walk(it.dir, !it.callbackDone):
}
}
}
}
type walker struct {
fn func(path string, typ os.FileMode) error
donec chan struct{} // closed on fastWalk's return
workc chan walkItem // to workers
enqueuec chan walkItem // from workers
resc chan error // from workers
}
type walkItem struct {
dir string
callbackDone bool // callback already called; don't do it again
}
func (w *walker) enqueue(it walkItem) {
select {
case w.enqueuec <- it:
case <-w.donec:
}
}
func (w *walker) onDirEnt(dirName, baseName string, typ os.FileMode) error {
joined := dirName + string(os.PathSeparator) + baseName
if typ == os.ModeDir {
w.enqueue(walkItem{dir: joined})
return nil
}
err := w.fn(joined, typ)
if typ == os.ModeSymlink {
if err == ErrTraverseLink {
// Set callbackDone so we don't call it twice for both the
// symlink-as-symlink and the symlink-as-directory later:
w.enqueue(walkItem{dir: joined, callbackDone: true})
return nil
}
if err == filepath.SkipDir {
// Permit SkipDir on symlinks too.
return nil
}
}
return err
}
func (w *walker) walk(root string, runUserCallback bool) error {
if runUserCallback {
err := w.fn(root, os.ModeDir)
if err == filepath.SkipDir {
return nil
}
if err != nil {
return err
}
}
return readDir(root, w.onDirEnt)
}

View File

@@ -1,119 +0,0 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build darwin && cgo
// +build darwin,cgo
package fastwalk
/*
#include <dirent.h>
// fastwalk_readdir_r wraps readdir_r so that we don't have to pass a dirent**
// result pointer which triggers CGO's "Go pointer to Go pointer" check unless
// we allocat the result dirent* with malloc.
//
// fastwalk_readdir_r returns 0 on success, -1 upon reaching the end of the
// directory, or a positive error number to indicate failure.
static int fastwalk_readdir_r(DIR *fd, struct dirent *entry) {
struct dirent *result;
int ret = readdir_r(fd, entry, &result);
if (ret == 0 && result == NULL) {
ret = -1; // EOF
}
return ret;
}
*/
import "C"
import (
"os"
"syscall"
"unsafe"
)
func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error {
fd, err := openDir(dirName)
if err != nil {
return &os.PathError{Op: "opendir", Path: dirName, Err: err}
}
defer C.closedir(fd)
skipFiles := false
var dirent syscall.Dirent
for {
ret := int(C.fastwalk_readdir_r(fd, (*C.struct_dirent)(unsafe.Pointer(&dirent))))
if ret != 0 {
if ret == -1 {
break // EOF
}
if ret == int(syscall.EINTR) {
continue
}
return &os.PathError{Op: "readdir", Path: dirName, Err: syscall.Errno(ret)}
}
if dirent.Ino == 0 {
continue
}
typ := dtToType(dirent.Type)
if skipFiles && typ.IsRegular() {
continue
}
name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:]
name = name[:dirent.Namlen]
for i, c := range name {
if c == 0 {
name = name[:i]
break
}
}
// Check for useless names before allocating a string.
if string(name) == "." || string(name) == ".." {
continue
}
if err := fn(dirName, string(name), typ); err != nil {
if err != ErrSkipFiles {
return err
}
skipFiles = true
}
}
return nil
}
func dtToType(typ uint8) os.FileMode {
switch typ {
case syscall.DT_BLK:
return os.ModeDevice
case syscall.DT_CHR:
return os.ModeDevice | os.ModeCharDevice
case syscall.DT_DIR:
return os.ModeDir
case syscall.DT_FIFO:
return os.ModeNamedPipe
case syscall.DT_LNK:
return os.ModeSymlink
case syscall.DT_REG:
return 0
case syscall.DT_SOCK:
return os.ModeSocket
}
return ^os.FileMode(0)
}
// openDir wraps opendir(3) and handles any EINTR errors. The returned *DIR
// needs to be closed with closedir(3).
func openDir(path string) (*C.DIR, error) {
name, err := syscall.BytePtrFromString(path)
if err != nil {
return nil, err
}
for {
fd, err := C.opendir((*C.char)(unsafe.Pointer(name)))
if err != syscall.EINTR {
return fd, err
}
}
}

View File

@@ -1,14 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build freebsd || openbsd || netbsd
// +build freebsd openbsd netbsd
package fastwalk
import "syscall"
func direntInode(dirent *syscall.Dirent) uint64 {
return uint64(dirent.Fileno)
}

View File

@@ -1,15 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build (linux || (darwin && !cgo)) && !appengine
// +build linux darwin,!cgo
// +build !appengine
package fastwalk
import "syscall"
func direntInode(dirent *syscall.Dirent) uint64 {
return dirent.Ino
}

View File

@@ -1,14 +0,0 @@
// 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.
//go:build (darwin && !cgo) || freebsd || openbsd || netbsd
// +build darwin,!cgo freebsd openbsd netbsd
package fastwalk
import "syscall"
func direntNamlen(dirent *syscall.Dirent) uint64 {
return uint64(dirent.Namlen)
}

View File

@@ -1,29 +0,0 @@
// 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.
//go:build linux && !appengine
// +build linux,!appengine
package fastwalk
import (
"bytes"
"syscall"
"unsafe"
)
func direntNamlen(dirent *syscall.Dirent) uint64 {
const fixedHdr = uint16(unsafe.Offsetof(syscall.Dirent{}.Name))
nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0]))
const nameBufLen = uint16(len(nameBuf))
limit := dirent.Reclen - fixedHdr
if limit > nameBufLen {
limit = nameBufLen
}
nameLen := bytes.IndexByte(nameBuf[:limit], 0)
if nameLen < 0 {
panic("failed to find terminating 0 byte in dirent")
}
return uint64(nameLen)
}

View File

@@ -1,38 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build appengine || (!linux && !darwin && !freebsd && !openbsd && !netbsd)
// +build appengine !linux,!darwin,!freebsd,!openbsd,!netbsd
package fastwalk
import (
"io/ioutil"
"os"
)
// readDir calls fn for each directory entry in dirName.
// It does not descend into directories or follow symlinks.
// If fn returns a non-nil error, readDir returns with that error
// immediately.
func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error {
fis, err := ioutil.ReadDir(dirName)
if err != nil {
return err
}
skipFiles := false
for _, fi := range fis {
if fi.Mode().IsRegular() && skipFiles {
continue
}
if err := fn(dirName, fi.Name(), fi.Mode()&os.ModeType); err != nil {
if err == ErrSkipFiles {
skipFiles = true
continue
}
return err
}
}
return nil
}

View File

@@ -1,153 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build (linux || freebsd || openbsd || netbsd || (darwin && !cgo)) && !appengine
// +build linux freebsd openbsd netbsd darwin,!cgo
// +build !appengine
package fastwalk
import (
"fmt"
"os"
"syscall"
"unsafe"
)
const blockSize = 8 << 10
// unknownFileMode is a sentinel (and bogus) os.FileMode
// value used to represent a syscall.DT_UNKNOWN Dirent.Type.
const unknownFileMode os.FileMode = os.ModeNamedPipe | os.ModeSocket | os.ModeDevice
func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error {
fd, err := open(dirName, 0, 0)
if err != nil {
return &os.PathError{Op: "open", Path: dirName, Err: err}
}
defer syscall.Close(fd)
// The buffer must be at least a block long.
buf := make([]byte, blockSize) // stack-allocated; doesn't escape
bufp := 0 // starting read position in buf
nbuf := 0 // end valid data in buf
skipFiles := false
for {
if bufp >= nbuf {
bufp = 0
nbuf, err = readDirent(fd, buf)
if err != nil {
return os.NewSyscallError("readdirent", err)
}
if nbuf <= 0 {
return nil
}
}
consumed, name, typ := parseDirEnt(buf[bufp:nbuf])
bufp += consumed
if name == "" || name == "." || name == ".." {
continue
}
// Fallback for filesystems (like old XFS) that don't
// support Dirent.Type and have DT_UNKNOWN (0) there
// instead.
if typ == unknownFileMode {
fi, err := os.Lstat(dirName + "/" + name)
if err != nil {
// It got deleted in the meantime.
if os.IsNotExist(err) {
continue
}
return err
}
typ = fi.Mode() & os.ModeType
}
if skipFiles && typ.IsRegular() {
continue
}
if err := fn(dirName, name, typ); err != nil {
if err == ErrSkipFiles {
skipFiles = true
continue
}
return err
}
}
}
func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode) {
// golang.org/issue/37269
dirent := &syscall.Dirent{}
copy((*[unsafe.Sizeof(syscall.Dirent{})]byte)(unsafe.Pointer(dirent))[:], buf)
if v := unsafe.Offsetof(dirent.Reclen) + unsafe.Sizeof(dirent.Reclen); uintptr(len(buf)) < v {
panic(fmt.Sprintf("buf size of %d smaller than dirent header size %d", len(buf), v))
}
if len(buf) < int(dirent.Reclen) {
panic(fmt.Sprintf("buf size %d < record length %d", len(buf), dirent.Reclen))
}
consumed = int(dirent.Reclen)
if direntInode(dirent) == 0 { // File absent in directory.
return
}
switch dirent.Type {
case syscall.DT_REG:
typ = 0
case syscall.DT_DIR:
typ = os.ModeDir
case syscall.DT_LNK:
typ = os.ModeSymlink
case syscall.DT_BLK:
typ = os.ModeDevice
case syscall.DT_FIFO:
typ = os.ModeNamedPipe
case syscall.DT_SOCK:
typ = os.ModeSocket
case syscall.DT_UNKNOWN:
typ = unknownFileMode
default:
// Skip weird things.
// It's probably a DT_WHT (http://lwn.net/Articles/325369/)
// or something. Revisit if/when this package is moved outside
// of goimports. goimports only cares about regular files,
// symlinks, and directories.
return
}
nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0]))
nameLen := direntNamlen(dirent)
// Special cases for common things:
if nameLen == 1 && nameBuf[0] == '.' {
name = "."
} else if nameLen == 2 && nameBuf[0] == '.' && nameBuf[1] == '.' {
name = ".."
} else {
name = string(nameBuf[:nameLen])
}
return
}
// According to https://golang.org/doc/go1.14#runtime
// A consequence of the implementation of preemption is that on Unix systems, including Linux and macOS
// systems, programs built with Go 1.14 will receive more signals than programs built with earlier releases.
//
// This causes syscall.Open and syscall.ReadDirent sometimes fail with EINTR errors.
// We need to retry in this case.
func open(path string, mode int, perm uint32) (fd int, err error) {
for {
fd, err := syscall.Open(path, mode, perm)
if err != syscall.EINTR {
return fd, err
}
}
}
func readDirent(fd int, buf []byte) (n int, err error) {
for {
nbuf, err := syscall.ReadDirent(fd, buf)
if err != syscall.EINTR {
return nbuf, err
}
}
}

View File

@@ -1,852 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Binary package export.
// This file was derived from $GOROOT/src/cmd/compile/internal/gc/bexport.go;
// see that file for specification of the format.
package gcimporter
import (
"bytes"
"encoding/binary"
"fmt"
"go/constant"
"go/token"
"go/types"
"math"
"math/big"
"sort"
"strings"
)
// If debugFormat is set, each integer and string value is preceded by a marker
// and position information in the encoding. This mechanism permits an importer
// to recognize immediately when it is out of sync. The importer recognizes this
// mode automatically (i.e., it can import export data produced with debugging
// support even if debugFormat is not set at the time of import). This mode will
// lead to massively larger export data (by a factor of 2 to 3) and should only
// be enabled during development and debugging.
//
// NOTE: This flag is the first flag to enable if importing dies because of
// (suspected) format errors, and whenever a change is made to the format.
const debugFormat = false // default: false
// Current export format version. Increase with each format change.
//
// Note: The latest binary (non-indexed) export format is at version 6.
// This exporter is still at level 4, but it doesn't matter since
// the binary importer can handle older versions just fine.
//
// 6: package height (CL 105038) -- NOT IMPLEMENTED HERE
// 5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMENTED HERE
// 4: type name objects support type aliases, uses aliasTag
// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used)
// 2: removed unused bool in ODCL export (compiler only)
// 1: header format change (more regular), export package for _ struct fields
// 0: Go1.7 encoding
const exportVersion = 4
// trackAllTypes enables cycle tracking for all types, not just named
// types. The existing compiler invariants assume that unnamed types
// that are not completely set up are not used, or else there are spurious
// errors.
// If disabled, only named types are tracked, possibly leading to slightly
// less efficient encoding in rare cases. It also prevents the export of
// some corner-case type declarations (but those are not handled correctly
// with with the textual export format either).
// TODO(gri) enable and remove once issues caused by it are fixed
const trackAllTypes = false
type exporter struct {
fset *token.FileSet
out bytes.Buffer
// object -> index maps, indexed in order of serialization
strIndex map[string]int
pkgIndex map[*types.Package]int
typIndex map[types.Type]int
// position encoding
posInfoFormat bool
prevFile string
prevLine int
// debugging support
written int // bytes written
indent int // for trace
}
// internalError represents an error generated inside this package.
type internalError string
func (e internalError) Error() string { return "gcimporter: " + string(e) }
func internalErrorf(format string, args ...interface{}) error {
return internalError(fmt.Sprintf(format, args...))
}
// BExportData returns binary export data for pkg.
// If no file set is provided, position info will be missing.
func BExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) {
if !debug {
defer func() {
if e := recover(); e != nil {
if ierr, ok := e.(internalError); ok {
err = ierr
return
}
// Not an internal error; panic again.
panic(e)
}
}()
}
p := exporter{
fset: fset,
strIndex: map[string]int{"": 0}, // empty string is mapped to 0
pkgIndex: make(map[*types.Package]int),
typIndex: make(map[types.Type]int),
posInfoFormat: true, // TODO(gri) might become a flag, eventually
}
// write version info
// The version string must start with "version %d" where %d is the version
// number. Additional debugging information may follow after a blank; that
// text is ignored by the importer.
p.rawStringln(fmt.Sprintf("version %d", exportVersion))
var debug string
if debugFormat {
debug = "debug"
}
p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly
p.bool(trackAllTypes)
p.bool(p.posInfoFormat)
// --- generic export data ---
// populate type map with predeclared "known" types
for index, typ := range predeclared() {
p.typIndex[typ] = index
}
if len(p.typIndex) != len(predeclared()) {
return nil, internalError("duplicate entries in type map?")
}
// write package data
p.pkg(pkg, true)
if trace {
p.tracef("\n")
}
// write objects
objcount := 0
scope := pkg.Scope()
for _, name := range scope.Names() {
if !token.IsExported(name) {
continue
}
if trace {
p.tracef("\n")
}
p.obj(scope.Lookup(name))
objcount++
}
// indicate end of list
if trace {
p.tracef("\n")
}
p.tag(endTag)
// for self-verification only (redundant)
p.int(objcount)
if trace {
p.tracef("\n")
}
// --- end of export data ---
return p.out.Bytes(), nil
}
func (p *exporter) pkg(pkg *types.Package, emptypath bool) {
if pkg == nil {
panic(internalError("unexpected nil pkg"))
}
// if we saw the package before, write its index (>= 0)
if i, ok := p.pkgIndex[pkg]; ok {
p.index('P', i)
return
}
// otherwise, remember the package, write the package tag (< 0) and package data
if trace {
p.tracef("P%d = { ", len(p.pkgIndex))
defer p.tracef("} ")
}
p.pkgIndex[pkg] = len(p.pkgIndex)
p.tag(packageTag)
p.string(pkg.Name())
if emptypath {
p.string("")
} else {
p.string(pkg.Path())
}
}
func (p *exporter) obj(obj types.Object) {
switch obj := obj.(type) {
case *types.Const:
p.tag(constTag)
p.pos(obj)
p.qualifiedName(obj)
p.typ(obj.Type())
p.value(obj.Val())
case *types.TypeName:
if obj.IsAlias() {
p.tag(aliasTag)
p.pos(obj)
p.qualifiedName(obj)
} else {
p.tag(typeTag)
}
p.typ(obj.Type())
case *types.Var:
p.tag(varTag)
p.pos(obj)
p.qualifiedName(obj)
p.typ(obj.Type())
case *types.Func:
p.tag(funcTag)
p.pos(obj)
p.qualifiedName(obj)
sig := obj.Type().(*types.Signature)
p.paramList(sig.Params(), sig.Variadic())
p.paramList(sig.Results(), false)
default:
panic(internalErrorf("unexpected object %v (%T)", obj, obj))
}
}
func (p *exporter) pos(obj types.Object) {
if !p.posInfoFormat {
return
}
file, line := p.fileLine(obj)
if file == p.prevFile {
// common case: write line delta
// delta == 0 means different file or no line change
delta := line - p.prevLine
p.int(delta)
if delta == 0 {
p.int(-1) // -1 means no file change
}
} else {
// different file
p.int(0)
// Encode filename as length of common prefix with previous
// filename, followed by (possibly empty) suffix. Filenames
// frequently share path prefixes, so this can save a lot
// of space and make export data size less dependent on file
// path length. The suffix is unlikely to be empty because
// file names tend to end in ".go".
n := commonPrefixLen(p.prevFile, file)
p.int(n) // n >= 0
p.string(file[n:]) // write suffix only
p.prevFile = file
p.int(line)
}
p.prevLine = line
}
func (p *exporter) fileLine(obj types.Object) (file string, line int) {
if p.fset != nil {
pos := p.fset.Position(obj.Pos())
file = pos.Filename
line = pos.Line
}
return
}
func commonPrefixLen(a, b string) int {
if len(a) > len(b) {
a, b = b, a
}
// len(a) <= len(b)
i := 0
for i < len(a) && a[i] == b[i] {
i++
}
return i
}
func (p *exporter) qualifiedName(obj types.Object) {
p.string(obj.Name())
p.pkg(obj.Pkg(), false)
}
func (p *exporter) typ(t types.Type) {
if t == nil {
panic(internalError("nil type"))
}
// Possible optimization: Anonymous pointer types *T where
// T is a named type are common. We could canonicalize all
// such types *T to a single type PT = *T. This would lead
// to at most one *T entry in typIndex, and all future *T's
// would be encoded as the respective index directly. Would
// save 1 byte (pointerTag) per *T and reduce the typIndex
// size (at the cost of a canonicalization map). We can do
// this later, without encoding format change.
// if we saw the type before, write its index (>= 0)
if i, ok := p.typIndex[t]; ok {
p.index('T', i)
return
}
// otherwise, remember the type, write the type tag (< 0) and type data
if trackAllTypes {
if trace {
p.tracef("T%d = {>\n", len(p.typIndex))
defer p.tracef("<\n} ")
}
p.typIndex[t] = len(p.typIndex)
}
switch t := t.(type) {
case *types.Named:
if !trackAllTypes {
// if we don't track all types, track named types now
p.typIndex[t] = len(p.typIndex)
}
p.tag(namedTag)
p.pos(t.Obj())
p.qualifiedName(t.Obj())
p.typ(t.Underlying())
if !types.IsInterface(t) {
p.assocMethods(t)
}
case *types.Array:
p.tag(arrayTag)
p.int64(t.Len())
p.typ(t.Elem())
case *types.Slice:
p.tag(sliceTag)
p.typ(t.Elem())
case *dddSlice:
p.tag(dddTag)
p.typ(t.elem)
case *types.Struct:
p.tag(structTag)
p.fieldList(t)
case *types.Pointer:
p.tag(pointerTag)
p.typ(t.Elem())
case *types.Signature:
p.tag(signatureTag)
p.paramList(t.Params(), t.Variadic())
p.paramList(t.Results(), false)
case *types.Interface:
p.tag(interfaceTag)
p.iface(t)
case *types.Map:
p.tag(mapTag)
p.typ(t.Key())
p.typ(t.Elem())
case *types.Chan:
p.tag(chanTag)
p.int(int(3 - t.Dir())) // hack
p.typ(t.Elem())
default:
panic(internalErrorf("unexpected type %T: %s", t, t))
}
}
func (p *exporter) assocMethods(named *types.Named) {
// Sort methods (for determinism).
var methods []*types.Func
for i := 0; i < named.NumMethods(); i++ {
methods = append(methods, named.Method(i))
}
sort.Sort(methodsByName(methods))
p.int(len(methods))
if trace && methods != nil {
p.tracef("associated methods {>\n")
}
for i, m := range methods {
if trace && i > 0 {
p.tracef("\n")
}
p.pos(m)
name := m.Name()
p.string(name)
if !exported(name) {
p.pkg(m.Pkg(), false)
}
sig := m.Type().(*types.Signature)
p.paramList(types.NewTuple(sig.Recv()), false)
p.paramList(sig.Params(), sig.Variadic())
p.paramList(sig.Results(), false)
p.int(0) // dummy value for go:nointerface pragma - ignored by importer
}
if trace && methods != nil {
p.tracef("<\n} ")
}
}
type methodsByName []*types.Func
func (x methodsByName) Len() int { return len(x) }
func (x methodsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x methodsByName) Less(i, j int) bool { return x[i].Name() < x[j].Name() }
func (p *exporter) fieldList(t *types.Struct) {
if trace && t.NumFields() > 0 {
p.tracef("fields {>\n")
defer p.tracef("<\n} ")
}
p.int(t.NumFields())
for i := 0; i < t.NumFields(); i++ {
if trace && i > 0 {
p.tracef("\n")
}
p.field(t.Field(i))
p.string(t.Tag(i))
}
}
func (p *exporter) field(f *types.Var) {
if !f.IsField() {
panic(internalError("field expected"))
}
p.pos(f)
p.fieldName(f)
p.typ(f.Type())
}
func (p *exporter) iface(t *types.Interface) {
// TODO(gri): enable importer to load embedded interfaces,
// then emit Embeddeds and ExplicitMethods separately here.
p.int(0)
n := t.NumMethods()
if trace && n > 0 {
p.tracef("methods {>\n")
defer p.tracef("<\n} ")
}
p.int(n)
for i := 0; i < n; i++ {
if trace && i > 0 {
p.tracef("\n")
}
p.method(t.Method(i))
}
}
func (p *exporter) method(m *types.Func) {
sig := m.Type().(*types.Signature)
if sig.Recv() == nil {
panic(internalError("method expected"))
}
p.pos(m)
p.string(m.Name())
if m.Name() != "_" && !token.IsExported(m.Name()) {
p.pkg(m.Pkg(), false)
}
// interface method; no need to encode receiver.
p.paramList(sig.Params(), sig.Variadic())
p.paramList(sig.Results(), false)
}
func (p *exporter) fieldName(f *types.Var) {
name := f.Name()
if f.Anonymous() {
// anonymous field - we distinguish between 3 cases:
// 1) field name matches base type name and is exported
// 2) field name matches base type name and is not exported
// 3) field name doesn't match base type name (alias name)
bname := basetypeName(f.Type())
if name == bname {
if token.IsExported(name) {
name = "" // 1) we don't need to know the field name or package
} else {
name = "?" // 2) use unexported name "?" to force package export
}
} else {
// 3) indicate alias and export name as is
// (this requires an extra "@" but this is a rare case)
p.string("@")
}
}
p.string(name)
if name != "" && !token.IsExported(name) {
p.pkg(f.Pkg(), false)
}
}
func basetypeName(typ types.Type) string {
switch typ := deref(typ).(type) {
case *types.Basic:
return typ.Name()
case *types.Named:
return typ.Obj().Name()
default:
return "" // unnamed type
}
}
func (p *exporter) paramList(params *types.Tuple, variadic bool) {
// use negative length to indicate unnamed parameters
// (look at the first parameter only since either all
// names are present or all are absent)
n := params.Len()
if n > 0 && params.At(0).Name() == "" {
n = -n
}
p.int(n)
for i := 0; i < params.Len(); i++ {
q := params.At(i)
t := q.Type()
if variadic && i == params.Len()-1 {
t = &dddSlice{t.(*types.Slice).Elem()}
}
p.typ(t)
if n > 0 {
name := q.Name()
p.string(name)
if name != "_" {
p.pkg(q.Pkg(), false)
}
}
p.string("") // no compiler-specific info
}
}
func (p *exporter) value(x constant.Value) {
if trace {
p.tracef("= ")
}
switch x.Kind() {
case constant.Bool:
tag := falseTag
if constant.BoolVal(x) {
tag = trueTag
}
p.tag(tag)
case constant.Int:
if v, exact := constant.Int64Val(x); exact {
// common case: x fits into an int64 - use compact encoding
p.tag(int64Tag)
p.int64(v)
return
}
// uncommon case: large x - use float encoding
// (powers of 2 will be encoded efficiently with exponent)
p.tag(floatTag)
p.float(constant.ToFloat(x))
case constant.Float:
p.tag(floatTag)
p.float(x)
case constant.Complex:
p.tag(complexTag)
p.float(constant.Real(x))
p.float(constant.Imag(x))
case constant.String:
p.tag(stringTag)
p.string(constant.StringVal(x))
case constant.Unknown:
// package contains type errors
p.tag(unknownTag)
default:
panic(internalErrorf("unexpected value %v (%T)", x, x))
}
}
func (p *exporter) float(x constant.Value) {
if x.Kind() != constant.Float {
panic(internalErrorf("unexpected constant %v, want float", x))
}
// extract sign (there is no -0)
sign := constant.Sign(x)
if sign == 0 {
// x == 0
p.int(0)
return
}
// x != 0
var f big.Float
if v, exact := constant.Float64Val(x); exact {
// float64
f.SetFloat64(v)
} else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int {
// TODO(gri): add big.Rat accessor to constant.Value.
r := valueToRat(num)
f.SetRat(r.Quo(r, valueToRat(denom)))
} else {
// Value too large to represent as a fraction => inaccessible.
// TODO(gri): add big.Float accessor to constant.Value.
f.SetFloat64(math.MaxFloat64) // FIXME
}
// extract exponent such that 0.5 <= m < 1.0
var m big.Float
exp := f.MantExp(&m)
// extract mantissa as *big.Int
// - set exponent large enough so mant satisfies mant.IsInt()
// - get *big.Int from mant
m.SetMantExp(&m, int(m.MinPrec()))
mant, acc := m.Int(nil)
if acc != big.Exact {
panic(internalError("internal error"))
}
p.int(sign)
p.int(exp)
p.string(string(mant.Bytes()))
}
func valueToRat(x constant.Value) *big.Rat {
// Convert little-endian to big-endian.
// I can't believe this is necessary.
bytes := constant.Bytes(x)
for i := 0; i < len(bytes)/2; i++ {
bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i]
}
return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes))
}
func (p *exporter) bool(b bool) bool {
if trace {
p.tracef("[")
defer p.tracef("= %v] ", b)
}
x := 0
if b {
x = 1
}
p.int(x)
return b
}
// ----------------------------------------------------------------------------
// Low-level encoders
func (p *exporter) index(marker byte, index int) {
if index < 0 {
panic(internalError("invalid index < 0"))
}
if debugFormat {
p.marker('t')
}
if trace {
p.tracef("%c%d ", marker, index)
}
p.rawInt64(int64(index))
}
func (p *exporter) tag(tag int) {
if tag >= 0 {
panic(internalError("invalid tag >= 0"))
}
if debugFormat {
p.marker('t')
}
if trace {
p.tracef("%s ", tagString[-tag])
}
p.rawInt64(int64(tag))
}
func (p *exporter) int(x int) {
p.int64(int64(x))
}
func (p *exporter) int64(x int64) {
if debugFormat {
p.marker('i')
}
if trace {
p.tracef("%d ", x)
}
p.rawInt64(x)
}
func (p *exporter) string(s string) {
if debugFormat {
p.marker('s')
}
if trace {
p.tracef("%q ", s)
}
// if we saw the string before, write its index (>= 0)
// (the empty string is mapped to 0)
if i, ok := p.strIndex[s]; ok {
p.rawInt64(int64(i))
return
}
// otherwise, remember string and write its negative length and bytes
p.strIndex[s] = len(p.strIndex)
p.rawInt64(-int64(len(s)))
for i := 0; i < len(s); i++ {
p.rawByte(s[i])
}
}
// marker emits a marker byte and position information which makes
// it easy for a reader to detect if it is "out of sync". Used for
// debugFormat format only.
func (p *exporter) marker(m byte) {
p.rawByte(m)
// Enable this for help tracking down the location
// of an incorrect marker when running in debugFormat.
if false && trace {
p.tracef("#%d ", p.written)
}
p.rawInt64(int64(p.written))
}
// rawInt64 should only be used by low-level encoders.
func (p *exporter) rawInt64(x int64) {
var tmp [binary.MaxVarintLen64]byte
n := binary.PutVarint(tmp[:], x)
for i := 0; i < n; i++ {
p.rawByte(tmp[i])
}
}
// rawStringln should only be used to emit the initial version string.
func (p *exporter) rawStringln(s string) {
for i := 0; i < len(s); i++ {
p.rawByte(s[i])
}
p.rawByte('\n')
}
// rawByte is the bottleneck interface to write to p.out.
// rawByte escapes b as follows (any encoding does that
// hides '$'):
//
// '$' => '|' 'S'
// '|' => '|' '|'
//
// Necessary so other tools can find the end of the
// export data by searching for "$$".
// rawByte should only be used by low-level encoders.
func (p *exporter) rawByte(b byte) {
switch b {
case '$':
// write '$' as '|' 'S'
b = 'S'
fallthrough
case '|':
// write '|' as '|' '|'
p.out.WriteByte('|')
p.written++
}
p.out.WriteByte(b)
p.written++
}
// tracef is like fmt.Printf but it rewrites the format string
// to take care of indentation.
func (p *exporter) tracef(format string, args ...interface{}) {
if strings.ContainsAny(format, "<>\n") {
var buf bytes.Buffer
for i := 0; i < len(format); i++ {
// no need to deal with runes
ch := format[i]
switch ch {
case '>':
p.indent++
continue
case '<':
p.indent--
continue
}
buf.WriteByte(ch)
if ch == '\n' {
for j := p.indent; j > 0; j-- {
buf.WriteString(". ")
}
}
}
format = buf.String()
}
fmt.Printf(format, args...)
}
// Debugging support.
// (tagString is only used when tracing is enabled)
var tagString = [...]string{
// Packages
-packageTag: "package",
// Types
-namedTag: "named type",
-arrayTag: "array",
-sliceTag: "slice",
-dddTag: "ddd",
-structTag: "struct",
-pointerTag: "pointer",
-signatureTag: "signature",
-interfaceTag: "interface",
-mapTag: "map",
-chanTag: "chan",
// Values
-falseTag: "false",
-trueTag: "true",
-int64Tag: "int64",
-floatTag: "float",
-fractionTag: "fraction",
-complexTag: "complex",
-stringTag: "string",
-unknownTag: "unknown",
// Type aliases
-aliasTag: "alias",
}

View File

@@ -2,340 +2,24 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file is a copy of $GOROOT/src/go/internal/gcimporter/bimport.go.
// This file contains the remaining vestiges of
// $GOROOT/src/go/internal/gcimporter/bimport.go.
package gcimporter
import (
"encoding/binary"
"fmt"
"go/constant"
"go/token"
"go/types"
"sort"
"strconv"
"strings"
"sync"
"unicode"
"unicode/utf8"
)
type importer struct {
imports map[string]*types.Package
data []byte
importpath string
buf []byte // for reading strings
version int // export format version
// object lists
strList []string // in order of appearance
pathList []string // in order of appearance
pkgList []*types.Package // in order of appearance
typList []types.Type // in order of appearance
interfaceList []*types.Interface // for delayed completion only
trackAllTypes bool
// position encoding
posInfoFormat bool
prevFile string
prevLine int
fake fakeFileSet
// debugging support
debugFormat bool
read int // bytes read
}
// BImportData imports a package from the serialized package data
// and returns the number of bytes consumed and a reference to the package.
// If the export data version is not recognized or the format is otherwise
// compromised, an error is returned.
func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
// catch panics and return them as errors
const currentVersion = 6
version := -1 // unknown version
defer func() {
if e := recover(); e != nil {
// Return a (possibly nil or incomplete) package unchanged (see #16088).
if version > currentVersion {
err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
} else {
err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
}
}
}()
p := importer{
imports: imports,
data: data,
importpath: path,
version: version,
strList: []string{""}, // empty string is mapped to 0
pathList: []string{""}, // empty string is mapped to 0
fake: fakeFileSet{
fset: fset,
files: make(map[string]*fileInfo),
},
}
defer p.fake.setLines() // set lines for files in fset
// read version info
var versionstr string
if b := p.rawByte(); b == 'c' || b == 'd' {
// Go1.7 encoding; first byte encodes low-level
// encoding format (compact vs debug).
// For backward-compatibility only (avoid problems with
// old installed packages). Newly compiled packages use
// the extensible format string.
// TODO(gri) Remove this support eventually; after Go1.8.
if b == 'd' {
p.debugFormat = true
}
p.trackAllTypes = p.rawByte() == 'a'
p.posInfoFormat = p.int() != 0
versionstr = p.string()
if versionstr == "v1" {
version = 0
}
} else {
// Go1.8 extensible encoding
// read version string and extract version number (ignore anything after the version number)
versionstr = p.rawStringln(b)
if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" {
if v, err := strconv.Atoi(s[1]); err == nil && v > 0 {
version = v
}
}
}
p.version = version
// read version specific flags - extend as necessary
switch p.version {
// case currentVersion:
// ...
// fallthrough
case currentVersion, 5, 4, 3, 2, 1:
p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
p.trackAllTypes = p.int() != 0
p.posInfoFormat = p.int() != 0
case 0:
// Go1.7 encoding format - nothing to do here
default:
errorf("unknown bexport format version %d (%q)", p.version, versionstr)
}
// --- generic export data ---
// populate typList with predeclared "known" types
p.typList = append(p.typList, predeclared()...)
// read package data
pkg = p.pkg()
// read objects of phase 1 only (see cmd/compile/internal/gc/bexport.go)
objcount := 0
for {
tag := p.tagOrIndex()
if tag == endTag {
break
}
p.obj(tag)
objcount++
}
// self-verification
if count := p.int(); count != objcount {
errorf("got %d objects; want %d", objcount, count)
}
// ignore compiler-specific import data
// complete interfaces
// TODO(gri) re-investigate if we still need to do this in a delayed fashion
for _, typ := range p.interfaceList {
typ.Complete()
}
// record all referenced packages as imports
list := append(([]*types.Package)(nil), p.pkgList[1:]...)
sort.Sort(byPath(list))
pkg.SetImports(list)
// package was imported completely and without errors
pkg.MarkComplete()
return p.read, pkg, nil
}
func errorf(format string, args ...interface{}) {
panic(fmt.Sprintf(format, args...))
}
func (p *importer) pkg() *types.Package {
// if the package was seen before, i is its index (>= 0)
i := p.tagOrIndex()
if i >= 0 {
return p.pkgList[i]
}
// otherwise, i is the package tag (< 0)
if i != packageTag {
errorf("unexpected package tag %d version %d", i, p.version)
}
// read package data
name := p.string()
var path string
if p.version >= 5 {
path = p.path()
} else {
path = p.string()
}
if p.version >= 6 {
p.int() // package height; unused by go/types
}
// we should never see an empty package name
if name == "" {
errorf("empty package name in import")
}
// an empty path denotes the package we are currently importing;
// it must be the first package we see
if (path == "") != (len(p.pkgList) == 0) {
errorf("package path %q for pkg index %d", path, len(p.pkgList))
}
// if the package was imported before, use that one; otherwise create a new one
if path == "" {
path = p.importpath
}
pkg := p.imports[path]
if pkg == nil {
pkg = types.NewPackage(path, name)
p.imports[path] = pkg
} else if pkg.Name() != name {
errorf("conflicting names %s and %s for package %q", pkg.Name(), name, path)
}
p.pkgList = append(p.pkgList, pkg)
return pkg
}
// objTag returns the tag value for each object kind.
func objTag(obj types.Object) int {
switch obj.(type) {
case *types.Const:
return constTag
case *types.TypeName:
return typeTag
case *types.Var:
return varTag
case *types.Func:
return funcTag
default:
errorf("unexpected object: %v (%T)", obj, obj) // panics
panic("unreachable")
}
}
func sameObj(a, b types.Object) bool {
// Because unnamed types are not canonicalized, we cannot simply compare types for
// (pointer) identity.
// Ideally we'd check equality of constant values as well, but this is good enough.
return objTag(a) == objTag(b) && types.Identical(a.Type(), b.Type())
}
func (p *importer) declare(obj types.Object) {
pkg := obj.Pkg()
if alt := pkg.Scope().Insert(obj); alt != nil {
// This can only trigger if we import a (non-type) object a second time.
// Excluding type aliases, this cannot happen because 1) we only import a package
// once; and b) we ignore compiler-specific export data which may contain
// functions whose inlined function bodies refer to other functions that
// were already imported.
// However, type aliases require reexporting the original type, so we need
// to allow it (see also the comment in cmd/compile/internal/gc/bimport.go,
// method importer.obj, switch case importing functions).
// TODO(gri) review/update this comment once the gc compiler handles type aliases.
if !sameObj(obj, alt) {
errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt)
}
}
}
func (p *importer) obj(tag int) {
switch tag {
case constTag:
pos := p.pos()
pkg, name := p.qualifiedName()
typ := p.typ(nil, nil)
val := p.value()
p.declare(types.NewConst(pos, pkg, name, typ, val))
case aliasTag:
// TODO(gri) verify type alias hookup is correct
pos := p.pos()
pkg, name := p.qualifiedName()
typ := p.typ(nil, nil)
p.declare(types.NewTypeName(pos, pkg, name, typ))
case typeTag:
p.typ(nil, nil)
case varTag:
pos := p.pos()
pkg, name := p.qualifiedName()
typ := p.typ(nil, nil)
p.declare(types.NewVar(pos, pkg, name, typ))
case funcTag:
pos := p.pos()
pkg, name := p.qualifiedName()
params, isddd := p.paramList()
result, _ := p.paramList()
sig := types.NewSignature(nil, params, result, isddd)
p.declare(types.NewFunc(pos, pkg, name, sig))
default:
errorf("unexpected object tag %d", tag)
}
}
const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go
func (p *importer) pos() token.Pos {
if !p.posInfoFormat {
return token.NoPos
}
file := p.prevFile
line := p.prevLine
delta := p.int()
line += delta
if p.version >= 5 {
if delta == deltaNewFile {
if n := p.int(); n >= 0 {
// file changed
file = p.path()
line = n
}
}
} else {
if delta == 0 {
if n := p.int(); n >= 0 {
// file changed
file = p.prevFile[:n] + p.string()
line = p.int()
}
}
}
p.prevFile = file
p.prevLine = line
return p.fake.pos(file, line, 0)
}
// Synthesize a token.Pos
type fakeFileSet struct {
fset *token.FileSet
@@ -389,205 +73,6 @@ var (
fakeLinesOnce sync.Once
)
func (p *importer) qualifiedName() (pkg *types.Package, name string) {
name = p.string()
pkg = p.pkg()
return
}
func (p *importer) record(t types.Type) {
p.typList = append(p.typList, t)
}
// A dddSlice is a types.Type representing ...T parameters.
// It only appears for parameter types and does not escape
// the importer.
type dddSlice struct {
elem types.Type
}
func (t *dddSlice) Underlying() types.Type { return t }
func (t *dddSlice) String() string { return "..." + t.elem.String() }
// parent is the package which declared the type; parent == nil means
// the package currently imported. The parent package is needed for
// exported struct fields and interface methods which don't contain
// explicit package information in the export data.
//
// A non-nil tname is used as the "owner" of the result type; i.e.,
// the result type is the underlying type of tname. tname is used
// to give interface methods a named receiver type where possible.
func (p *importer) typ(parent *types.Package, tname *types.Named) types.Type {
// if the type was seen before, i is its index (>= 0)
i := p.tagOrIndex()
if i >= 0 {
return p.typList[i]
}
// otherwise, i is the type tag (< 0)
switch i {
case namedTag:
// read type object
pos := p.pos()
parent, name := p.qualifiedName()
scope := parent.Scope()
obj := scope.Lookup(name)
// if the object doesn't exist yet, create and insert it
if obj == nil {
obj = types.NewTypeName(pos, parent, name, nil)
scope.Insert(obj)
}
if _, ok := obj.(*types.TypeName); !ok {
errorf("pkg = %s, name = %s => %s", parent, name, obj)
}
// associate new named type with obj if it doesn't exist yet
t0 := types.NewNamed(obj.(*types.TypeName), nil, nil)
// but record the existing type, if any
tname := obj.Type().(*types.Named) // tname is either t0 or the existing type
p.record(tname)
// read underlying type
t0.SetUnderlying(p.typ(parent, t0))
// interfaces don't have associated methods
if types.IsInterface(t0) {
return tname
}
// read associated methods
for i := p.int(); i > 0; i-- {
// TODO(gri) replace this with something closer to fieldName
pos := p.pos()
name := p.string()
if !exported(name) {
p.pkg()
}
recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver?
params, isddd := p.paramList()
result, _ := p.paramList()
p.int() // go:nointerface pragma - discarded
sig := types.NewSignature(recv.At(0), params, result, isddd)
t0.AddMethod(types.NewFunc(pos, parent, name, sig))
}
return tname
case arrayTag:
t := new(types.Array)
if p.trackAllTypes {
p.record(t)
}
n := p.int64()
*t = *types.NewArray(p.typ(parent, nil), n)
return t
case sliceTag:
t := new(types.Slice)
if p.trackAllTypes {
p.record(t)
}
*t = *types.NewSlice(p.typ(parent, nil))
return t
case dddTag:
t := new(dddSlice)
if p.trackAllTypes {
p.record(t)
}
t.elem = p.typ(parent, nil)
return t
case structTag:
t := new(types.Struct)
if p.trackAllTypes {
p.record(t)
}
*t = *types.NewStruct(p.fieldList(parent))
return t
case pointerTag:
t := new(types.Pointer)
if p.trackAllTypes {
p.record(t)
}
*t = *types.NewPointer(p.typ(parent, nil))
return t
case signatureTag:
t := new(types.Signature)
if p.trackAllTypes {
p.record(t)
}
params, isddd := p.paramList()
result, _ := p.paramList()
*t = *types.NewSignature(nil, params, result, isddd)
return t
case interfaceTag:
// Create a dummy entry in the type list. This is safe because we
// cannot expect the interface type to appear in a cycle, as any
// such cycle must contain a named type which would have been
// first defined earlier.
// TODO(gri) Is this still true now that we have type aliases?
// See issue #23225.
n := len(p.typList)
if p.trackAllTypes {
p.record(nil)
}
var embeddeds []types.Type
for n := p.int(); n > 0; n-- {
p.pos()
embeddeds = append(embeddeds, p.typ(parent, nil))
}
t := newInterface(p.methodList(parent, tname), embeddeds)
p.interfaceList = append(p.interfaceList, t)
if p.trackAllTypes {
p.typList[n] = t
}
return t
case mapTag:
t := new(types.Map)
if p.trackAllTypes {
p.record(t)
}
key := p.typ(parent, nil)
val := p.typ(parent, nil)
*t = *types.NewMap(key, val)
return t
case chanTag:
t := new(types.Chan)
if p.trackAllTypes {
p.record(t)
}
dir := chanDir(p.int())
val := p.typ(parent, nil)
*t = *types.NewChan(dir, val)
return t
default:
errorf("unexpected type tag %d", i) // panics
panic("unreachable")
}
}
func chanDir(d int) types.ChanDir {
// tag values must match the constants in cmd/compile/internal/gc/go.go
switch d {
@@ -602,452 +87,3 @@ func chanDir(d int) types.ChanDir {
return 0
}
}
func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags []string) {
if n := p.int(); n > 0 {
fields = make([]*types.Var, n)
tags = make([]string, n)
for i := range fields {
fields[i], tags[i] = p.field(parent)
}
}
return
}
func (p *importer) field(parent *types.Package) (*types.Var, string) {
pos := p.pos()
pkg, name, alias := p.fieldName(parent)
typ := p.typ(parent, nil)
tag := p.string()
anonymous := false
if name == "" {
// anonymous field - typ must be T or *T and T must be a type name
switch typ := deref(typ).(type) {
case *types.Basic: // basic types are named types
pkg = nil // // objects defined in Universe scope have no package
name = typ.Name()
case *types.Named:
name = typ.Obj().Name()
default:
errorf("named base type expected")
}
anonymous = true
} else if alias {
// anonymous field: we have an explicit name because it's an alias
anonymous = true
}
return types.NewField(pos, pkg, name, typ, anonymous), tag
}
func (p *importer) methodList(parent *types.Package, baseType *types.Named) (methods []*types.Func) {
if n := p.int(); n > 0 {
methods = make([]*types.Func, n)
for i := range methods {
methods[i] = p.method(parent, baseType)
}
}
return
}
func (p *importer) method(parent *types.Package, baseType *types.Named) *types.Func {
pos := p.pos()
pkg, name, _ := p.fieldName(parent)
// If we don't have a baseType, use a nil receiver.
// A receiver using the actual interface type (which
// we don't know yet) will be filled in when we call
// types.Interface.Complete.
var recv *types.Var
if baseType != nil {
recv = types.NewVar(token.NoPos, parent, "", baseType)
}
params, isddd := p.paramList()
result, _ := p.paramList()
sig := types.NewSignature(recv, params, result, isddd)
return types.NewFunc(pos, pkg, name, sig)
}
func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) {
name = p.string()
pkg = parent
if pkg == nil {
// use the imported package instead
pkg = p.pkgList[0]
}
if p.version == 0 && name == "_" {
// version 0 didn't export a package for _ fields
return
}
switch name {
case "":
// 1) field name matches base type name and is exported: nothing to do
case "?":
// 2) field name matches base type name and is not exported: need package
name = ""
pkg = p.pkg()
case "@":
// 3) field name doesn't match type name (alias)
name = p.string()
alias = true
fallthrough
default:
if !exported(name) {
pkg = p.pkg()
}
}
return
}
func (p *importer) paramList() (*types.Tuple, bool) {
n := p.int()
if n == 0 {
return nil, false
}
// negative length indicates unnamed parameters
named := true
if n < 0 {
n = -n
named = false
}
// n > 0
params := make([]*types.Var, n)
isddd := false
for i := range params {
params[i], isddd = p.param(named)
}
return types.NewTuple(params...), isddd
}
func (p *importer) param(named bool) (*types.Var, bool) {
t := p.typ(nil, nil)
td, isddd := t.(*dddSlice)
if isddd {
t = types.NewSlice(td.elem)
}
var pkg *types.Package
var name string
if named {
name = p.string()
if name == "" {
errorf("expected named parameter")
}
if name != "_" {
pkg = p.pkg()
}
if i := strings.Index(name, "·"); i > 0 {
name = name[:i] // cut off gc-specific parameter numbering
}
}
// read and discard compiler-specific info
p.string()
return types.NewVar(token.NoPos, pkg, name, t), isddd
}
func exported(name string) bool {
ch, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(ch)
}
func (p *importer) value() constant.Value {
switch tag := p.tagOrIndex(); tag {
case falseTag:
return constant.MakeBool(false)
case trueTag:
return constant.MakeBool(true)
case int64Tag:
return constant.MakeInt64(p.int64())
case floatTag:
return p.float()
case complexTag:
re := p.float()
im := p.float()
return constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
case stringTag:
return constant.MakeString(p.string())
case unknownTag:
return constant.MakeUnknown()
default:
errorf("unexpected value tag %d", tag) // panics
panic("unreachable")
}
}
func (p *importer) float() constant.Value {
sign := p.int()
if sign == 0 {
return constant.MakeInt64(0)
}
exp := p.int()
mant := []byte(p.string()) // big endian
// remove leading 0's if any
for len(mant) > 0 && mant[0] == 0 {
mant = mant[1:]
}
// convert to little endian
// TODO(gri) go/constant should have a more direct conversion function
// (e.g., once it supports a big.Float based implementation)
for i, j := 0, len(mant)-1; i < j; i, j = i+1, j-1 {
mant[i], mant[j] = mant[j], mant[i]
}
// adjust exponent (constant.MakeFromBytes creates an integer value,
// but mant represents the mantissa bits such that 0.5 <= mant < 1.0)
exp -= len(mant) << 3
if len(mant) > 0 {
for msd := mant[len(mant)-1]; msd&0x80 == 0; msd <<= 1 {
exp++
}
}
x := constant.MakeFromBytes(mant)
switch {
case exp < 0:
d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp))
x = constant.BinaryOp(x, token.QUO, d)
case exp > 0:
x = constant.Shift(x, token.SHL, uint(exp))
}
if sign < 0 {
x = constant.UnaryOp(token.SUB, x, 0)
}
return x
}
// ----------------------------------------------------------------------------
// Low-level decoders
func (p *importer) tagOrIndex() int {
if p.debugFormat {
p.marker('t')
}
return int(p.rawInt64())
}
func (p *importer) int() int {
x := p.int64()
if int64(int(x)) != x {
errorf("exported integer too large")
}
return int(x)
}
func (p *importer) int64() int64 {
if p.debugFormat {
p.marker('i')
}
return p.rawInt64()
}
func (p *importer) path() string {
if p.debugFormat {
p.marker('p')
}
// if the path was seen before, i is its index (>= 0)
// (the empty string is at index 0)
i := p.rawInt64()
if i >= 0 {
return p.pathList[i]
}
// otherwise, i is the negative path length (< 0)
a := make([]string, -i)
for n := range a {
a[n] = p.string()
}
s := strings.Join(a, "/")
p.pathList = append(p.pathList, s)
return s
}
func (p *importer) string() string {
if p.debugFormat {
p.marker('s')
}
// if the string was seen before, i is its index (>= 0)
// (the empty string is at index 0)
i := p.rawInt64()
if i >= 0 {
return p.strList[i]
}
// otherwise, i is the negative string length (< 0)
if n := int(-i); n <= cap(p.buf) {
p.buf = p.buf[:n]
} else {
p.buf = make([]byte, n)
}
for i := range p.buf {
p.buf[i] = p.rawByte()
}
s := string(p.buf)
p.strList = append(p.strList, s)
return s
}
func (p *importer) marker(want byte) {
if got := p.rawByte(); got != want {
errorf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read)
}
pos := p.read
if n := int(p.rawInt64()); n != pos {
errorf("incorrect position: got %d; want %d", n, pos)
}
}
// rawInt64 should only be used by low-level decoders.
func (p *importer) rawInt64() int64 {
i, err := binary.ReadVarint(p)
if err != nil {
errorf("read error: %v", err)
}
return i
}
// rawStringln should only be used to read the initial version string.
func (p *importer) rawStringln(b byte) string {
p.buf = p.buf[:0]
for b != '\n' {
p.buf = append(p.buf, b)
b = p.rawByte()
}
return string(p.buf)
}
// needed for binary.ReadVarint in rawInt64
func (p *importer) ReadByte() (byte, error) {
return p.rawByte(), nil
}
// byte is the bottleneck interface for reading p.data.
// It unescapes '|' 'S' to '$' and '|' '|' to '|'.
// rawByte should only be used by low-level decoders.
func (p *importer) rawByte() byte {
b := p.data[0]
r := 1
if b == '|' {
b = p.data[1]
r = 2
switch b {
case 'S':
b = '$'
case '|':
// nothing to do
default:
errorf("unexpected escape sequence in export data")
}
}
p.data = p.data[r:]
p.read += r
return b
}
// ----------------------------------------------------------------------------
// Export format
// Tags. Must be < 0.
const (
// Objects
packageTag = -(iota + 1)
constTag
typeTag
varTag
funcTag
endTag
// Types
namedTag
arrayTag
sliceTag
dddTag
structTag
pointerTag
signatureTag
interfaceTag
mapTag
chanTag
// Values
falseTag
trueTag
int64Tag
floatTag
fractionTag // not used by gc
complexTag
stringTag
nilTag // only used by gc (appears in exported inlined function bodies)
unknownTag // not used by gc (only appears in packages with errors)
// Type aliases
aliasTag
)
var predeclOnce sync.Once
var predecl []types.Type // initialized lazily
func predeclared() []types.Type {
predeclOnce.Do(func() {
// initialize lazily to be sure that all
// elements have been initialized before
predecl = []types.Type{ // basic types
types.Typ[types.Bool],
types.Typ[types.Int],
types.Typ[types.Int8],
types.Typ[types.Int16],
types.Typ[types.Int32],
types.Typ[types.Int64],
types.Typ[types.Uint],
types.Typ[types.Uint8],
types.Typ[types.Uint16],
types.Typ[types.Uint32],
types.Typ[types.Uint64],
types.Typ[types.Uintptr],
types.Typ[types.Float32],
types.Typ[types.Float64],
types.Typ[types.Complex64],
types.Typ[types.Complex128],
types.Typ[types.String],
// basic type aliases
types.Universe.Lookup("byte").Type(),
types.Universe.Lookup("rune").Type(),
// error
types.Universe.Lookup("error").Type(),
// untyped types
types.Typ[types.UntypedBool],
types.Typ[types.UntypedInt],
types.Typ[types.UntypedRune],
types.Typ[types.UntypedFloat],
types.Typ[types.UntypedComplex],
types.Typ[types.UntypedString],
types.Typ[types.UntypedNil],
// package unsafe
types.Typ[types.UnsafePointer],
// invalid type
types.Typ[types.Invalid], // only appears in packages with errors
// used internally by gc; never used by this package or in .a files
anyType{},
}
predecl = append(predecl, additionalPredeclared()...)
})
return predecl
}
type anyType struct{}
func (t anyType) Underlying() types.Type { return t }
func (t anyType) String() string { return "any" }

View File

@@ -7,6 +7,18 @@
// Package gcimporter provides various functions for reading
// gc-generated object files that can be used to implement the
// Importer interface defined by the Go 1.5 standard library package.
//
// The encoding is deterministic: if the encoder is applied twice to
// the same types.Package data structure, both encodings are equal.
// This property may be important to avoid spurious changes in
// applications such as build systems.
//
// However, the encoder is not necessarily idempotent. Importing an
// exported package may yield a types.Package that, while it
// represents the same set of Go types as the original, may differ in
// the details of its internal representation. Because of these
// differences, re-encoding the imported package may yield a
// different, but equally valid, encoding of the package.
package gcimporter // import "golang.org/x/tools/internal/gcimporter"
import (
@@ -17,7 +29,6 @@ import (
"go/token"
"go/types"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
@@ -209,7 +220,7 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func
switch hdr {
case "$$B\n":
var data []byte
data, err = ioutil.ReadAll(buf)
data, err = io.ReadAll(buf)
if err != nil {
break
}
@@ -218,20 +229,22 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func
// Or, define a new standard go/types/gcexportdata package.
fset := token.NewFileSet()
// The indexed export format starts with an 'i'; the older
// binary export format starts with a 'c', 'd', or 'v'
// (from "version"). Select appropriate importer.
// Select appropriate importer.
if len(data) > 0 {
switch data[0] {
case 'v', 'c', 'd':
// binary: emitted by cmd/compile till go1.10; obsolete.
return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0])
case 'i':
// indexed: emitted by cmd/compile till go1.19;
// now used only for serializing go/types.
// See https://github.com/golang/go/issues/69491.
_, pkg, err := IImportData(fset, packages, data[1:], id)
return pkg, err
case 'v', 'c', 'd':
_, pkg, err := BImportData(fset, packages, data, id)
return pkg, err
case 'u':
// unified: emitted by cmd/compile since go1.20.
_, pkg, err := UImportData(fset, packages, data[1:size], id)
return pkg, err
@@ -251,13 +264,6 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func
return
}
func deref(typ types.Type) types.Type {
if p, _ := typ.(*types.Pointer); p != nil {
return p.Elem()
}
return typ
}
type byPath []*types.Package
func (a byPath) Len() int { return len(a) }

View File

@@ -2,9 +2,227 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Indexed binary package export.
// This file was derived from $GOROOT/src/cmd/compile/internal/gc/iexport.go;
// see that file for specification of the format.
// Indexed package export.
//
// The indexed export data format is an evolution of the previous
// binary export data format. Its chief contribution is introducing an
// index table, which allows efficient random access of individual
// declarations and inline function bodies. In turn, this allows
// avoiding unnecessary work for compilation units that import large
// packages.
//
//
// The top-level data format is structured as:
//
// Header struct {
// Tag byte // 'i'
// Version uvarint
// StringSize uvarint
// DataSize uvarint
// }
//
// Strings [StringSize]byte
// Data [DataSize]byte
//
// MainIndex []struct{
// PkgPath stringOff
// PkgName stringOff
// PkgHeight uvarint
//
// Decls []struct{
// Name stringOff
// Offset declOff
// }
// }
//
// Fingerprint [8]byte
//
// uvarint means a uint64 written out using uvarint encoding.
//
// []T means a uvarint followed by that many T objects. In other
// words:
//
// Len uvarint
// Elems [Len]T
//
// stringOff means a uvarint that indicates an offset within the
// Strings section. At that offset is another uvarint, followed by
// that many bytes, which form the string value.
//
// declOff means a uvarint that indicates an offset within the Data
// section where the associated declaration can be found.
//
//
// There are five kinds of declarations, distinguished by their first
// byte:
//
// type Var struct {
// Tag byte // 'V'
// Pos Pos
// Type typeOff
// }
//
// type Func struct {
// Tag byte // 'F' or 'G'
// Pos Pos
// TypeParams []typeOff // only present if Tag == 'G'
// Signature Signature
// }
//
// type Const struct {
// Tag byte // 'C'
// Pos Pos
// Value Value
// }
//
// type Type struct {
// Tag byte // 'T' or 'U'
// Pos Pos
// TypeParams []typeOff // only present if Tag == 'U'
// Underlying typeOff
//
// Methods []struct{ // omitted if Underlying is an interface type
// Pos Pos
// Name stringOff
// Recv Param
// Signature Signature
// }
// }
//
// type Alias struct {
// Tag byte // 'A' or 'B'
// Pos Pos
// TypeParams []typeOff // only present if Tag == 'B'
// Type typeOff
// }
//
// // "Automatic" declaration of each typeparam
// type TypeParam struct {
// Tag byte // 'P'
// Pos Pos
// Implicit bool
// Constraint typeOff
// }
//
// typeOff means a uvarint that either indicates a predeclared type,
// or an offset into the Data section. If the uvarint is less than
// predeclReserved, then it indicates the index into the predeclared
// types list (see predeclared in bexport.go for order). Otherwise,
// subtracting predeclReserved yields the offset of a type descriptor.
//
// Value means a type, kind, and type-specific value. See
// (*exportWriter).value for details.
//
//
// There are twelve kinds of type descriptors, distinguished by an itag:
//
// type DefinedType struct {
// Tag itag // definedType
// Name stringOff
// PkgPath stringOff
// }
//
// type PointerType struct {
// Tag itag // pointerType
// Elem typeOff
// }
//
// type SliceType struct {
// Tag itag // sliceType
// Elem typeOff
// }
//
// type ArrayType struct {
// Tag itag // arrayType
// Len uint64
// Elem typeOff
// }
//
// type ChanType struct {
// Tag itag // chanType
// Dir uint64 // 1 RecvOnly; 2 SendOnly; 3 SendRecv
// Elem typeOff
// }
//
// type MapType struct {
// Tag itag // mapType
// Key typeOff
// Elem typeOff
// }
//
// type FuncType struct {
// Tag itag // signatureType
// PkgPath stringOff
// Signature Signature
// }
//
// type StructType struct {
// Tag itag // structType
// PkgPath stringOff
// Fields []struct {
// Pos Pos
// Name stringOff
// Type typeOff
// Embedded bool
// Note stringOff
// }
// }
//
// type InterfaceType struct {
// Tag itag // interfaceType
// PkgPath stringOff
// Embeddeds []struct {
// Pos Pos
// Type typeOff
// }
// Methods []struct {
// Pos Pos
// Name stringOff
// Signature Signature
// }
// }
//
// // Reference to a type param declaration
// type TypeParamType struct {
// Tag itag // typeParamType
// Name stringOff
// PkgPath stringOff
// }
//
// // Instantiation of a generic type (like List[T2] or List[int])
// type InstanceType struct {
// Tag itag // instanceType
// Pos pos
// TypeArgs []typeOff
// BaseType typeOff
// }
//
// type UnionType struct {
// Tag itag // interfaceType
// Terms []struct {
// tilde bool
// Type typeOff
// }
// }
//
//
//
// type Signature struct {
// Params []Param
// Results []Param
// Variadic bool // omitted if Results is empty
// }
//
// type Param struct {
// Pos Pos
// Name stringOff
// Type typOff
// }
//
//
// Pos encodes a file:line:column triple, incorporating a simple delta
// encoding scheme within a data object. See exportWriter.pos for
// details.
package gcimporter
@@ -22,17 +240,22 @@ import (
"strconv"
"strings"
"golang.org/x/tools/internal/tokeninternal"
"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/go/types/objectpath"
"golang.org/x/tools/internal/aliases"
)
// IExportShallow encodes "shallow" export data for the specified package.
//
// No promises are made about the encoding other than that it can be
// decoded by the same version of IIExportShallow. If you plan to save
// export data in the file system, be sure to include a cryptographic
// digest of the executable in the key to avoid version skew.
func IExportShallow(fset *token.FileSet, pkg *types.Package) ([]byte, error) {
// No promises are made about the encoding other than that it can be decoded by
// the same version of IIExportShallow. If you plan to save export data in the
// file system, be sure to include a cryptographic digest of the executable in
// the key to avoid version skew.
//
// If the provided reportf func is non-nil, it will be used for reporting bugs
// encountered during export.
// TODO(rfindley): remove reportf when we are confident enough in the new
// objectpath encoding.
func IExportShallow(fset *token.FileSet, pkg *types.Package, reportf ReportFunc) ([]byte, error) {
// In principle this operation can only fail if out.Write fails,
// but that's impossible for bytes.Buffer---and as a matter of
// fact iexportCommon doesn't even check for I/O errors.
@@ -44,22 +267,30 @@ func IExportShallow(fset *token.FileSet, pkg *types.Package) ([]byte, error) {
return out.Bytes(), err
}
// IImportShallow decodes "shallow" types.Package data encoded by IExportShallow
// in the same executable. This function cannot import data from
// IImportShallow decodes "shallow" types.Package data encoded by
// IExportShallow in the same executable. This function cannot import data from
// cmd/compile or gcexportdata.Write.
func IImportShallow(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string, insert InsertType) (*types.Package, error) {
//
// The importer calls getPackages to obtain package symbols for all
// packages mentioned in the export data, including the one being
// decoded.
//
// If the provided reportf func is non-nil, it will be used for reporting bugs
// encountered during import.
// TODO(rfindley): remove reportf when we are confident enough in the new
// objectpath encoding.
func IImportShallow(fset *token.FileSet, getPackages GetPackagesFunc, data []byte, path string, reportf ReportFunc) (*types.Package, error) {
const bundle = false
pkgs, err := iimportCommon(fset, imports, data, bundle, path, insert)
const shallow = true
pkgs, err := iimportCommon(fset, getPackages, data, bundle, path, shallow, reportf)
if err != nil {
return nil, err
}
return pkgs[0], nil
}
// InsertType is the type of a function that creates a types.TypeName
// object for a named type and inserts it into the scope of the
// specified Package.
type InsertType = func(pkg *types.Package, name string)
// ReportFunc is the type of a function used to report formatted bugs.
type ReportFunc = func(string, ...interface{})
// Current bundled export format version. Increase with each format change.
// 0: initial implementation
@@ -209,7 +440,7 @@ func (p *iexporter) encodeFile(w *intWriter, file *token.File, needed []uint64)
// Sort the set of needed offsets. Duplicates are harmless.
sort.Slice(needed, func(i, j int) bool { return needed[i] < needed[j] })
lines := tokeninternal.GetLines(file) // byte offset of each line start
lines := file.Lines() // byte offset of each line start
w.uint64(uint64(len(lines)))
// Rather than record the entire array of line start offsets,
@@ -313,8 +544,9 @@ type iexporter struct {
out *bytes.Buffer
version int
shallow bool // don't put types from other packages in the index
localpkg *types.Package // (nil in bundle mode)
shallow bool // don't put types from other packages in the index
objEncoder *objectpath.Encoder // encodes objects from other packages in shallow mode; lazily allocated
localpkg *types.Package // (nil in bundle mode)
// allPkgs tracks all packages that have been referenced by
// the export data, so we can ensure to include them in the
@@ -354,6 +586,17 @@ func (p *iexporter) trace(format string, args ...interface{}) {
fmt.Printf(strings.Repeat("..", p.indent)+format+"\n", args...)
}
// objectpathEncoder returns the lazily allocated objectpath.Encoder to use
// when encoding objects in other packages during shallow export.
//
// Using a shared Encoder amortizes some of cost of objectpath search.
func (p *iexporter) objectpathEncoder() *objectpath.Encoder {
if p.objEncoder == nil {
p.objEncoder = new(objectpath.Encoder)
}
return p.objEncoder
}
// stringOff returns the offset of s within the string section.
// If not already present, it's added to the end.
func (p *iexporter) stringOff(s string) uint64 {
@@ -413,7 +656,6 @@ type exportWriter struct {
p *iexporter
data intWriter
currPkg *types.Package
prevFile string
prevLine int64
prevColumn int64
@@ -436,11 +678,10 @@ func (p *iexporter) doDecl(obj types.Object) {
}()
}
w := p.newWriter()
w.setPkg(obj.Pkg(), false)
switch obj := obj.(type) {
case *types.Var:
w.tag('V')
w.tag(varTag)
w.pos(obj.Pos())
w.typ(obj.Type(), obj.Pkg())
@@ -457,10 +698,10 @@ func (p *iexporter) doDecl(obj types.Object) {
}
// Function.
if typeparams.ForSignature(sig).Len() == 0 {
w.tag('F')
if sig.TypeParams().Len() == 0 {
w.tag(funcTag)
} else {
w.tag('G')
w.tag(genericFuncTag)
}
w.pos(obj.Pos())
// The tparam list of the function type is the declaration of the type
@@ -470,27 +711,27 @@ func (p *iexporter) doDecl(obj types.Object) {
//
// While importing the type parameters, tparamList computes and records
// their export name, so that it can be later used when writing the index.
if tparams := typeparams.ForSignature(sig); tparams.Len() > 0 {
if tparams := sig.TypeParams(); tparams.Len() > 0 {
w.tparamList(obj.Name(), tparams, obj.Pkg())
}
w.signature(sig)
case *types.Const:
w.tag('C')
w.tag(constTag)
w.pos(obj.Pos())
w.value(obj.Type(), obj.Val())
case *types.TypeName:
t := obj.Type()
if tparam, ok := t.(*typeparams.TypeParam); ok {
w.tag('P')
if tparam, ok := types.Unalias(t).(*types.TypeParam); ok {
w.tag(typeParamTag)
w.pos(obj.Pos())
constraint := tparam.Constraint()
if p.version >= iexportVersionGo1_18 {
implicit := false
if iface, _ := constraint.(*types.Interface); iface != nil {
implicit = typeparams.IsImplicit(iface)
if iface, _ := types.Unalias(constraint).(*types.Interface); iface != nil {
implicit = iface.IsImplicit()
}
w.bool(implicit)
}
@@ -499,8 +740,26 @@ func (p *iexporter) doDecl(obj types.Object) {
}
if obj.IsAlias() {
w.tag('A')
alias, materialized := t.(*types.Alias) // may fail when aliases are not enabled
var tparams *types.TypeParamList
if materialized {
tparams = aliases.TypeParams(alias)
}
if tparams.Len() == 0 {
w.tag(aliasTag)
} else {
w.tag(genericAliasTag)
}
w.pos(obj.Pos())
if tparams.Len() > 0 {
w.tparamList(obj.Name(), tparams, obj.Pkg())
}
if materialized {
// Preserve materialized aliases,
// even of non-exported types.
t = aliases.Rhs(alias)
}
w.typ(t, obj.Pkg())
break
}
@@ -511,20 +770,20 @@ func (p *iexporter) doDecl(obj types.Object) {
panic(internalErrorf("%s is not a defined type", t))
}
if typeparams.ForNamed(named).Len() == 0 {
w.tag('T')
if named.TypeParams().Len() == 0 {
w.tag(typeTag)
} else {
w.tag('U')
w.tag(genericTypeTag)
}
w.pos(obj.Pos())
if typeparams.ForNamed(named).Len() > 0 {
if named.TypeParams().Len() > 0 {
// While importing the type parameters, tparamList computes and records
// their export name, so that it can be later used when writing the index.
w.tparamList(obj.Name(), typeparams.ForNamed(named), obj.Pkg())
w.tparamList(obj.Name(), named.TypeParams(), obj.Pkg())
}
underlying := obj.Type().Underlying()
underlying := named.Underlying()
w.typ(underlying, obj.Pkg())
if types.IsInterface(t) {
@@ -541,7 +800,7 @@ func (p *iexporter) doDecl(obj types.Object) {
// Receiver type parameters are type arguments of the receiver type, so
// their name must be qualified before exporting recv.
if rparams := typeparams.RecvTypeParams(sig); rparams.Len() > 0 {
if rparams := sig.RecvTypeParams(); rparams.Len() > 0 {
prefix := obj.Name() + "." + m.Name()
for i := 0; i < rparams.Len(); i++ {
rparam := rparams.At(i)
@@ -673,6 +932,9 @@ func (w *exportWriter) qualifiedType(obj *types.TypeName) {
w.pkg(obj.Pkg())
}
// TODO(rfindley): what does 'pkg' even mean here? It would be better to pass
// it in explicitly into signatures and structs that may use it for
// constructing fields.
func (w *exportWriter) typ(t types.Type, pkg *types.Package) {
w.data.uint64(w.p.typOff(t, pkg))
}
@@ -712,20 +974,31 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
}()
}
switch t := t.(type) {
case *types.Alias:
if targs := aliases.TypeArgs(t); targs.Len() > 0 {
w.startType(instanceType)
w.pos(t.Obj().Pos())
w.typeList(targs, pkg)
w.typ(aliases.Origin(t), pkg)
return
}
w.startType(aliasType)
w.qualifiedType(t.Obj())
case *types.Named:
if targs := typeparams.NamedTypeArgs(t); targs.Len() > 0 {
if targs := t.TypeArgs(); targs.Len() > 0 {
w.startType(instanceType)
// TODO(rfindley): investigate if this position is correct, and if it
// matters.
w.pos(t.Obj().Pos())
w.typeList(targs, pkg)
w.typ(typeparams.NamedTypeOrigin(t), pkg)
w.typ(t.Origin(), pkg)
return
}
w.startType(definedType)
w.qualifiedType(t.Obj())
case *typeparams.TypeParam:
case *types.TypeParam:
w.startType(typeParamType)
w.qualifiedType(t.Obj())
@@ -764,37 +1037,60 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
case *types.Signature:
w.startType(signatureType)
w.setPkg(pkg, true)
w.pkg(pkg)
w.signature(t)
case *types.Struct:
w.startType(structType)
n := t.NumFields()
// Even for struct{} we must emit some qualifying package, because that's
// what the compiler does, and thus that's what the importer expects.
fieldPkg := pkg
if n > 0 {
w.setPkg(t.Field(0).Pkg(), true) // qualifying package for field objects
} else {
w.setPkg(pkg, true)
fieldPkg = t.Field(0).Pkg()
}
if fieldPkg == nil {
// TODO(rfindley): improve this very hacky logic.
//
// The importer expects a package to be set for all struct types, even
// those with no fields. A better encoding might be to set NumFields
// before pkg. setPkg panics with a nil package, which may be possible
// to reach with invalid packages (and perhaps valid packages, too?), so
// (arbitrarily) set the localpkg if available.
//
// Alternatively, we may be able to simply guarantee that pkg != nil, by
// reconsidering the encoding of constant values.
if w.p.shallow {
fieldPkg = w.p.localpkg
} else {
panic(internalErrorf("no package to set for empty struct"))
}
}
w.pkg(fieldPkg)
w.uint64(uint64(n))
for i := 0; i < n; i++ {
f := t.Field(i)
if w.p.shallow {
w.objectPath(f)
}
w.pos(f.Pos())
w.string(f.Name()) // unexported fields implicitly qualified by prior setPkg
w.typ(f.Type(), pkg)
w.typ(f.Type(), fieldPkg)
w.bool(f.Anonymous())
w.string(t.Tag(i)) // note (or tag)
}
case *types.Interface:
w.startType(interfaceType)
w.setPkg(pkg, true)
w.pkg(pkg)
n := t.NumEmbeddeds()
w.uint64(uint64(n))
for i := 0; i < n; i++ {
ft := t.EmbeddedType(i)
tPkg := pkg
if named, _ := ft.(*types.Named); named != nil {
if named, _ := types.Unalias(ft).(*types.Named); named != nil {
w.pos(named.Obj().Pos())
} else {
w.pos(token.NoPos)
@@ -802,17 +1098,23 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
w.typ(ft, tPkg)
}
// See comment for struct fields. In shallow mode we change the encoding
// for interface methods that are promoted from other packages.
n = t.NumExplicitMethods()
w.uint64(uint64(n))
for i := 0; i < n; i++ {
m := t.ExplicitMethod(i)
if w.p.shallow {
w.objectPath(m)
}
w.pos(m.Pos())
w.string(m.Name())
sig, _ := m.Type().(*types.Signature)
w.signature(sig)
}
case *typeparams.Union:
case *types.Union:
w.startType(unionType)
nt := t.Len()
w.uint64(uint64(nt))
@@ -827,12 +1129,61 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
}
}
func (w *exportWriter) setPkg(pkg *types.Package, write bool) {
if write {
w.pkg(pkg)
// objectPath writes the package and objectPath to use to look up obj in a
// different package, when encoding in "shallow" mode.
//
// When doing a shallow import, the importer creates only the local package,
// and requests package symbols for dependencies from the client.
// However, certain types defined in the local package may hold objects defined
// (perhaps deeply) within another package.
//
// For example, consider the following:
//
// package a
// func F() chan * map[string] struct { X int }
//
// package b
// import "a"
// var B = a.F()
//
// In this example, the type of b.B holds fields defined in package a.
// In order to have the correct canonical objects for the field defined in the
// type of B, they are encoded as objectPaths and later looked up in the
// importer. The same problem applies to interface methods.
func (w *exportWriter) objectPath(obj types.Object) {
if obj.Pkg() == nil || obj.Pkg() == w.p.localpkg {
// obj.Pkg() may be nil for the builtin error.Error.
// In this case, or if obj is declared in the local package, no need to
// encode.
w.string("")
return
}
w.currPkg = pkg
objectPath, err := w.p.objectpathEncoder().For(obj)
if err != nil {
// Fall back to the empty string, which will cause the importer to create a
// new object, which matches earlier behavior. Creating a new object is
// sufficient for many purposes (such as type checking), but causes certain
// references algorithms to fail (golang/go#60819). However, we didn't
// notice this problem during months of gopls@v0.12.0 testing.
//
// TODO(golang/go#61674): this workaround is insufficient, as in the case
// where the field forwarded from an instantiated type that may not appear
// in the export data of the original package:
//
// // package a
// type A[P any] struct{ F P }
//
// // package b
// type B a.A[int]
//
// We need to update references algorithms not to depend on this
// de-duplication, at which point we may want to simply remove the
// workaround here.
w.string("")
return
}
w.string(string(objectPath))
w.pkg(obj.Pkg())
}
func (w *exportWriter) signature(sig *types.Signature) {
@@ -843,14 +1194,14 @@ func (w *exportWriter) signature(sig *types.Signature) {
}
}
func (w *exportWriter) typeList(ts *typeparams.TypeList, pkg *types.Package) {
func (w *exportWriter) typeList(ts *types.TypeList, pkg *types.Package) {
w.uint64(uint64(ts.Len()))
for i := 0; i < ts.Len(); i++ {
w.typ(ts.At(i), pkg)
}
}
func (w *exportWriter) tparamList(prefix string, list *typeparams.TypeParamList, pkg *types.Package) {
func (w *exportWriter) tparamList(prefix string, list *types.TypeParamList, pkg *types.Package) {
ll := uint64(list.Len())
w.uint64(ll)
for i := 0; i < list.Len(); i++ {
@@ -868,7 +1219,7 @@ const blankMarker = "$"
// differs from its actual object name: it is prefixed with a qualifier, and
// blank type parameter names are disambiguated by their index in the type
// parameter list.
func tparamExportName(prefix string, tparam *typeparams.TypeParam) string {
func tparamExportName(prefix string, tparam *types.TypeParam) string {
assert(prefix != "")
name := tparam.Obj().Name()
if name == "_" {
@@ -913,6 +1264,17 @@ func (w *exportWriter) value(typ types.Type, v constant.Value) {
w.int64(int64(v.Kind()))
}
if v.Kind() == constant.Unknown {
// golang/go#60605: treat unknown constant values as if they have invalid type
//
// This loses some fidelity over the package type-checked from source, but that
// is acceptable.
//
// TODO(rfindley): we should switch on the recorded constant kind rather
// than the constant type
return
}
switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType {
case types.IsBoolean:
w.bool(constant.BoolVal(v))
@@ -969,6 +1331,16 @@ func constantToFloat(x constant.Value) *big.Float {
return &f
}
func valueToRat(x constant.Value) *big.Rat {
// Convert little-endian to big-endian.
// I can't believe this is necessary.
bytes := constant.Bytes(x)
for i := 0; i < len(bytes)/2; i++ {
bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i]
}
return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes))
}
// mpint exports a multi-precision integer.
//
// For unsigned types, small values are written out as a single
@@ -1178,3 +1550,19 @@ func (q *objQueue) popHead() types.Object {
q.head++
return obj
}
// internalError represents an error generated inside this package.
type internalError string
func (e internalError) Error() string { return "gcimporter: " + string(e) }
// TODO(adonovan): make this call panic, so that it's symmetric with errorf.
// Otherwise it's easy to forget to do anything with the error.
//
// TODO(adonovan): also, consider switching the names "errorf" and
// "internalErrorf" as the former is used for bugs, whose cause is
// internal inconsistency, whereas the latter is used for ordinary
// situations like bad input, whose cause is external.
func internalErrorf(format string, args ...interface{}) error {
return internalError(fmt.Sprintf(format, args...))
}

View File

@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// Indexed package import.
// See cmd/compile/internal/gc/iexport.go for the export data format.
// See iexport.go for the export data format.
// This file is a copy of $GOROOT/src/go/internal/gcimporter/iimport.go.
@@ -21,7 +21,9 @@ import (
"sort"
"strings"
"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/go/types/objectpath"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/typesinternal"
)
type intReader struct {
@@ -51,6 +53,7 @@ const (
iexportVersionPosCol = 1
iexportVersionGo1_18 = 2
iexportVersionGenerics = 2
iexportVersion = iexportVersionGenerics
iexportVersionCurrent = 2
)
@@ -78,6 +81,20 @@ const (
typeParamType
instanceType
unionType
aliasType
)
// Object tags
const (
varTag = 'V'
funcTag = 'F'
genericFuncTag = 'G'
constTag = 'C'
aliasTag = 'A'
genericAliasTag = 'B'
typeParamTag = 'P'
typeTag = 'T'
genericTypeTag = 'U'
)
// IImportData imports a package from the serialized package data
@@ -85,7 +102,7 @@ const (
// If the export data version is not recognized or the format is otherwise
// compromised, an error is returned.
func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) {
pkgs, err := iimportCommon(fset, imports, data, false, path, nil)
pkgs, err := iimportCommon(fset, GetPackagesFromMap(imports), data, false, path, false, nil)
if err != nil {
return 0, nil, err
}
@@ -94,10 +111,49 @@ func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []
// IImportBundle imports a set of packages from the serialized package bundle.
func IImportBundle(fset *token.FileSet, imports map[string]*types.Package, data []byte) ([]*types.Package, error) {
return iimportCommon(fset, imports, data, true, "", nil)
return iimportCommon(fset, GetPackagesFromMap(imports), data, true, "", false, nil)
}
func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data []byte, bundle bool, path string, insert InsertType) (pkgs []*types.Package, err error) {
// A GetPackagesFunc function obtains the non-nil symbols for a set of
// packages, creating and recursively importing them as needed. An
// implementation should store each package symbol is in the Pkg
// field of the items array.
//
// Any error causes importing to fail. This can be used to quickly read
// the import manifest of an export data file without fully decoding it.
type GetPackagesFunc = func(items []GetPackagesItem) error
// A GetPackagesItem is a request from the importer for the package
// symbol of the specified name and path.
type GetPackagesItem struct {
Name, Path string
Pkg *types.Package // to be filled in by GetPackagesFunc call
// private importer state
pathOffset uint64
nameIndex map[string]uint64
}
// GetPackagesFromMap returns a GetPackagesFunc that retrieves
// packages from the given map of package path to package.
//
// The returned function may mutate m: each requested package that is not
// found is created with types.NewPackage and inserted into m.
func GetPackagesFromMap(m map[string]*types.Package) GetPackagesFunc {
return func(items []GetPackagesItem) error {
for i, item := range items {
pkg, ok := m[item.Path]
if !ok {
pkg = types.NewPackage(item.Path, item.Name)
m[item.Path] = pkg
}
items[i].Pkg = pkg
}
return nil
}
}
func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte, bundle bool, path string, shallow bool, reportf ReportFunc) (pkgs []*types.Package, err error) {
const currentVersion = iexportVersionCurrent
version := int64(-1)
if !debug {
@@ -108,7 +164,7 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
} else if version > currentVersion {
err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
} else {
err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
err = fmt.Errorf("internal error while importing %q (%v); please report an issue", path, e)
}
}
}()
@@ -117,11 +173,8 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
r := &intReader{bytes.NewReader(data), path}
if bundle {
bundleVersion := r.uint64()
switch bundleVersion {
case bundleVersion:
default:
errorf("unknown bundle format version %d", bundleVersion)
if v := r.uint64(); v != bundleVersion {
errorf("unknown bundle format version %d", v)
}
}
@@ -139,7 +192,7 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
sLen := int64(r.uint64())
var fLen int64
var fileOffset []uint64
if insert != nil {
if shallow {
// Shallow mode uses a different position encoding.
fLen = int64(r.uint64())
fileOffset = make([]uint64, r.uint64())
@@ -158,7 +211,9 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
p := iimporter{
version: int(version),
ipath: path,
insert: insert,
aliases: aliases.Enabled(),
shallow: shallow,
reportf: reportf,
stringData: stringData,
stringCache: make(map[uint64]string),
@@ -185,8 +240,10 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
p.typCache[uint64(i)] = pt
}
pkgList := make([]*types.Package, r.uint64())
for i := range pkgList {
// Gather the relevant packages from the manifest.
items := make([]GetPackagesItem, r.uint64())
uniquePkgPaths := make(map[string]bool)
for i := range items {
pkgPathOff := r.uint64()
pkgPath := p.stringAt(pkgPathOff)
pkgName := p.stringAt(r.uint64())
@@ -195,30 +252,48 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
if pkgPath == "" {
pkgPath = path
}
pkg := imports[pkgPath]
if pkg == nil {
pkg = types.NewPackage(pkgPath, pkgName)
imports[pkgPath] = pkg
} else if pkg.Name() != pkgName {
errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path)
}
if i == 0 && !bundle {
p.localpkg = pkg
}
p.pkgCache[pkgPathOff] = pkg
items[i].Name = pkgName
items[i].Path = pkgPath
items[i].pathOffset = pkgPathOff
// Read index for package.
nameIndex := make(map[string]uint64)
nSyms := r.uint64()
// In shallow mode we don't expect an index for other packages.
assert(nSyms == 0 || p.localpkg == pkg || p.insert == nil)
// In shallow mode, only the current package (i=0) has an index.
assert(!(shallow && i > 0 && nSyms != 0))
for ; nSyms > 0; nSyms-- {
name := p.stringAt(r.uint64())
nameIndex[name] = r.uint64()
}
p.pkgIndex[pkg] = nameIndex
items[i].nameIndex = nameIndex
uniquePkgPaths[pkgPath] = true
}
// Debugging #63822; hypothesis: there are duplicate PkgPaths.
if len(uniquePkgPaths) != len(items) {
reportf("found duplicate PkgPaths while reading export data manifest: %v", items)
}
// Request packages all at once from the client,
// enabling a parallel implementation.
if err := getPackages(items); err != nil {
return nil, err // don't wrap this error
}
// Check the results and complete the index.
pkgList := make([]*types.Package, len(items))
for i, item := range items {
pkg := item.Pkg
if pkg == nil {
errorf("internal error: getPackages returned nil package for %q", item.Path)
} else if pkg.Path() != item.Path {
errorf("internal error: getPackages returned wrong path %q, want %q", pkg.Path(), item.Path)
} else if pkg.Name() != item.Name {
errorf("internal error: getPackages returned wrong name %s for package %q, want %s", pkg.Name(), item.Path, item.Name)
}
p.pkgCache[item.pathOffset] = pkg
p.pkgIndex[pkg] = item.nameIndex
pkgList[i] = pkg
}
@@ -265,23 +340,30 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
}
// SetConstraint can't be called if the constraint type is not yet complete.
// When type params are created in the 'P' case of (*importReader).obj(),
// When type params are created in the typeParamTag case of (*importReader).obj(),
// the associated constraint type may not be complete due to recursion.
// Therefore, we defer calling SetConstraint there, and call it here instead
// after all types are complete.
for _, d := range p.later {
typeparams.SetTypeParamConstraint(d.t, d.constraint)
d.t.SetConstraint(d.constraint)
}
for _, typ := range p.interfaceList {
typ.Complete()
}
// Workaround for golang/go#61561. See the doc for instanceList for details.
for _, typ := range p.instanceList {
if iface, _ := typ.Underlying().(*types.Interface); iface != nil {
iface.Complete()
}
}
return pkgs, nil
}
type setConstraintArgs struct {
t *typeparams.TypeParam
t *types.TypeParam
constraint types.Type
}
@@ -289,8 +371,9 @@ type iimporter struct {
version int
ipath string
localpkg *types.Package
insert func(pkg *types.Package, name string) // "shallow" mode only
aliases bool
shallow bool
reportf ReportFunc // if non-nil, used to report bugs
stringData []byte
stringCache map[uint64]string
@@ -307,6 +390,12 @@ type iimporter struct {
fake fakeFileSet
interfaceList []*types.Interface
// Workaround for the go/types bug golang/go#61561: instances produced during
// instantiation may contain incomplete interfaces. Here we only complete the
// underlying type of the instance, which is the most common case but doesn't
// handle parameterized interface literals defined deeper in the type.
instanceList []types.Type // instances for later completion (see golang/go#61561)
// Arguments for calls to SetConstraint that are deferred due to recursive types
later []setConstraintArgs
@@ -338,13 +427,9 @@ func (p *iimporter) doDecl(pkg *types.Package, name string) {
off, ok := p.pkgIndex[pkg][name]
if !ok {
// In "shallow" mode, call back to the application to
// find the object and insert it into the package scope.
if p.insert != nil {
assert(pkg != p.localpkg)
p.insert(pkg, name) // "can't fail"
return
}
// In deep mode, the index should be complete. In shallow
// mode, we should have already recursively loaded necessary
// dependencies so the above Lookup succeeds.
errorf("%v.%v not in index", pkg, name)
}
@@ -456,7 +541,7 @@ func canReuse(def *types.Named, rhs types.Type) bool {
if def == nil {
return true
}
iface, _ := rhs.(*types.Interface)
iface, _ := types.Unalias(rhs).(*types.Interface)
if iface == nil {
return true
}
@@ -478,25 +563,29 @@ func (r *importReader) obj(name string) {
pos := r.pos()
switch tag {
case 'A':
case aliasTag, genericAliasTag:
var tparams []*types.TypeParam
if tag == genericAliasTag {
tparams = r.tparamList()
}
typ := r.typ()
obj := aliases.NewAlias(r.p.aliases, pos, r.currPkg, name, typ, tparams)
r.declare(obj)
r.declare(types.NewTypeName(pos, r.currPkg, name, typ))
case 'C':
case constTag:
typ, val := r.value()
r.declare(types.NewConst(pos, r.currPkg, name, typ, val))
case 'F', 'G':
var tparams []*typeparams.TypeParam
if tag == 'G' {
case funcTag, genericFuncTag:
var tparams []*types.TypeParam
if tag == genericFuncTag {
tparams = r.tparamList()
}
sig := r.signature(nil, nil, tparams)
r.declare(types.NewFunc(pos, r.currPkg, name, sig))
case 'T', 'U':
case typeTag, genericTypeTag:
// Types can be recursive. We need to setup a stub
// declaration before recursing.
obj := types.NewTypeName(pos, r.currPkg, name, nil)
@@ -504,9 +593,9 @@ func (r *importReader) obj(name string) {
// Declare obj before calling r.tparamList, so the new type name is recognized
// if used in the constraint of one of its own typeparams (see #48280).
r.declare(obj)
if tag == 'U' {
if tag == genericTypeTag {
tparams := r.tparamList()
typeparams.SetForNamed(named, tparams)
named.SetTypeParams(tparams)
}
underlying := r.p.typAt(r.uint64(), named).Underlying()
@@ -521,14 +610,13 @@ func (r *importReader) obj(name string) {
// If the receiver has any targs, set those as the
// rparams of the method (since those are the
// typeparams being used in the method sig/body).
base := baseType(recv.Type())
assert(base != nil)
targs := typeparams.NamedTypeArgs(base)
var rparams []*typeparams.TypeParam
_, recvNamed := typesinternal.ReceiverNamed(recv)
targs := recvNamed.TypeArgs()
var rparams []*types.TypeParam
if targs.Len() > 0 {
rparams = make([]*typeparams.TypeParam, targs.Len())
rparams = make([]*types.TypeParam, targs.Len())
for i := range rparams {
rparams[i] = targs.At(i).(*typeparams.TypeParam)
rparams[i] = types.Unalias(targs.At(i)).(*types.TypeParam)
}
}
msig := r.signature(recv, rparams, nil)
@@ -537,7 +625,7 @@ func (r *importReader) obj(name string) {
}
}
case 'P':
case typeParamTag:
// We need to "declare" a typeparam in order to have a name that
// can be referenced recursively (if needed) in the type param's
// bound.
@@ -546,7 +634,7 @@ func (r *importReader) obj(name string) {
}
name0 := tparamName(name)
tn := types.NewTypeName(pos, r.currPkg, name0, nil)
t := typeparams.NewTypeParam(tn, nil)
t := types.NewTypeParam(tn, nil)
// To handle recursive references to the typeparam within its
// bound, save the partial type in tparamIndex before reading the bounds.
@@ -558,11 +646,11 @@ func (r *importReader) obj(name string) {
}
constraint := r.typ()
if implicit {
iface, _ := constraint.(*types.Interface)
iface, _ := types.Unalias(constraint).(*types.Interface)
if iface == nil {
errorf("non-interface constraint marked implicit")
}
typeparams.MarkImplicit(iface)
iface.MarkImplicit()
}
// The constraint type may not be complete, if we
// are in the middle of a type recursion involving type
@@ -570,7 +658,7 @@ func (r *importReader) obj(name string) {
// completely set up all types in ImportData.
r.p.later = append(r.p.later, setConstraintArgs{t: t, constraint: constraint})
case 'V':
case varTag:
typ := r.typ()
r.declare(types.NewVar(pos, r.currPkg, name, typ))
@@ -711,7 +799,8 @@ func (r *importReader) qualifiedIdent() (*types.Package, string) {
}
func (r *importReader) pos() token.Pos {
if r.p.insert != nil { // shallow mode
if r.p.shallow {
// precise offsets are encoded only in shallow mode
return r.posv2()
}
if r.p.version >= iexportVersionPosCol {
@@ -764,7 +853,7 @@ func (r *importReader) typ() types.Type {
}
func isInterface(t types.Type) bool {
_, ok := t.(*types.Interface)
_, ok := types.Unalias(t).(*types.Interface)
return ok
}
@@ -774,7 +863,7 @@ func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
func (r *importReader) doType(base *types.Named) (res types.Type) {
k := r.kind()
if debug {
r.p.trace("importing type %d (base: %s)", k, base)
r.p.trace("importing type %d (base: %v)", k, base)
r.p.indent++
defer func() {
r.p.indent--
@@ -786,7 +875,7 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
errorf("unexpected kind tag in %q: %v", r.p.ipath, k)
return nil
case definedType:
case aliasType, definedType:
pkg, name := r.qualifiedIdent()
r.p.doDecl(pkg, name)
return pkg.Scope().Lookup(name).(*types.TypeName).Type()
@@ -812,13 +901,28 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
fields := make([]*types.Var, r.uint64())
tags := make([]string, len(fields))
for i := range fields {
var field *types.Var
if r.p.shallow {
field, _ = r.objectPathObject().(*types.Var)
}
fpos := r.pos()
fname := r.ident()
ftyp := r.typ()
emb := r.bool()
tag := r.string()
fields[i] = types.NewField(fpos, r.currPkg, fname, ftyp, emb)
// Either this is not a shallow import, the field is local, or the
// encoded objectPath failed to produce an object (a bug).
//
// Even in this last, buggy case, fall back on creating a new field. As
// discussed in iexport.go, this is not correct, but mostly works and is
// preferable to failing (for now at least).
if field == nil {
field = types.NewField(fpos, r.currPkg, fname, ftyp, emb)
}
fields[i] = field
tags[i] = tag
}
return types.NewStruct(fields, tags)
@@ -834,6 +938,11 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
methods := make([]*types.Func, r.uint64())
for i := range methods {
var method *types.Func
if r.p.shallow {
method, _ = r.objectPathObject().(*types.Func)
}
mpos := r.pos()
mname := r.ident()
@@ -843,12 +952,15 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
if base != nil {
recv = types.NewVar(token.NoPos, r.currPkg, "", base)
}
msig := r.signature(recv, nil, nil)
methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig)
if method == nil {
method = types.NewFunc(mpos, r.currPkg, mname, msig)
}
methods[i] = method
}
typ := newInterface(methods, embeddeds)
typ := types.NewInterfaceType(methods, embeddeds)
r.p.interfaceList = append(r.p.interfaceList, typ)
return typ
@@ -882,18 +994,21 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
// The imported instantiated type doesn't include any methods, so
// we must always use the methods of the base (orig) type.
// TODO provide a non-nil *Environment
t, _ := typeparams.Instantiate(nil, baseType, targs, false)
t, _ := types.Instantiate(nil, baseType, targs, false)
// Workaround for golang/go#61561. See the doc for instanceList for details.
r.p.instanceList = append(r.p.instanceList, t)
return t
case unionType:
if r.p.version < iexportVersionGenerics {
errorf("unexpected instantiation type")
}
terms := make([]*typeparams.Term, r.uint64())
terms := make([]*types.Term, r.uint64())
for i := range terms {
terms[i] = typeparams.NewTerm(r.bool(), r.typ())
terms[i] = types.NewTerm(r.bool(), r.typ())
}
return typeparams.NewUnion(terms)
return types.NewUnion(terms)
}
}
@@ -901,23 +1016,43 @@ func (r *importReader) kind() itag {
return itag(r.uint64())
}
func (r *importReader) signature(recv *types.Var, rparams []*typeparams.TypeParam, tparams []*typeparams.TypeParam) *types.Signature {
// objectPathObject is the inverse of exportWriter.objectPath.
//
// In shallow mode, certain fields and methods may need to be looked up in an
// imported package. See the doc for exportWriter.objectPath for a full
// explanation.
func (r *importReader) objectPathObject() types.Object {
objPath := objectpath.Path(r.string())
if objPath == "" {
return nil
}
pkg := r.pkg()
obj, err := objectpath.Object(pkg, objPath)
if err != nil {
if r.p.reportf != nil {
r.p.reportf("failed to find object for objectPath %q: %v", objPath, err)
}
}
return obj
}
func (r *importReader) signature(recv *types.Var, rparams []*types.TypeParam, tparams []*types.TypeParam) *types.Signature {
params := r.paramList()
results := r.paramList()
variadic := params.Len() > 0 && r.bool()
return typeparams.NewSignatureType(recv, rparams, tparams, params, results, variadic)
return types.NewSignatureType(recv, rparams, tparams, params, results, variadic)
}
func (r *importReader) tparamList() []*typeparams.TypeParam {
func (r *importReader) tparamList() []*types.TypeParam {
n := r.uint64()
if n == 0 {
return nil
}
xs := make([]*typeparams.TypeParam, n)
xs := make([]*types.TypeParam, n)
for i := range xs {
// Note: the standard library importer is tolerant of nil types here,
// though would panic in SetTypeParams.
xs[i] = r.typ().(*typeparams.TypeParam)
xs[i] = types.Unalias(r.typ()).(*types.TypeParam)
}
return xs
}
@@ -964,13 +1099,3 @@ func (r *importReader) byte() byte {
}
return x
}
func baseType(typ types.Type) *types.Named {
// pointer receivers are never types.Named types
if p, _ := typ.(*types.Pointer); p != nil {
typ = p.Elem()
}
// receiver base types are always (possibly generic) types.Named types
n, _ := typ.(*types.Named)
return n
}

View File

@@ -1,22 +0,0 @@
// 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.
//go:build !go1.11
// +build !go1.11
package gcimporter
import "go/types"
func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface {
named := make([]*types.Named, len(embeddeds))
for i, e := range embeddeds {
var ok bool
named[i], ok = e.(*types.Named)
if !ok {
panic("embedding of non-defined interfaces in interfaces is not supported before Go 1.11")
}
}
return types.NewInterface(methods, named)
}

View File

@@ -1,14 +0,0 @@
// 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.
//go:build go1.11
// +build go1.11
package gcimporter
import "go/types"
func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface {
return types.NewInterfaceType(methods, embeddeds)
}

View File

@@ -0,0 +1,91 @@
// Copyright 2024 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 gcimporter
import (
"go/types"
"sync"
)
// predecl is a cache for the predeclared types in types.Universe.
//
// Cache a distinct result based on the runtime value of any.
// The pointer value of the any type varies based on GODEBUG settings.
var predeclMu sync.Mutex
var predecl map[types.Type][]types.Type
func predeclared() []types.Type {
anyt := types.Universe.Lookup("any").Type()
predeclMu.Lock()
defer predeclMu.Unlock()
if pre, ok := predecl[anyt]; ok {
return pre
}
if predecl == nil {
predecl = make(map[types.Type][]types.Type)
}
decls := []types.Type{ // basic types
types.Typ[types.Bool],
types.Typ[types.Int],
types.Typ[types.Int8],
types.Typ[types.Int16],
types.Typ[types.Int32],
types.Typ[types.Int64],
types.Typ[types.Uint],
types.Typ[types.Uint8],
types.Typ[types.Uint16],
types.Typ[types.Uint32],
types.Typ[types.Uint64],
types.Typ[types.Uintptr],
types.Typ[types.Float32],
types.Typ[types.Float64],
types.Typ[types.Complex64],
types.Typ[types.Complex128],
types.Typ[types.String],
// basic type aliases
types.Universe.Lookup("byte").Type(),
types.Universe.Lookup("rune").Type(),
// error
types.Universe.Lookup("error").Type(),
// untyped types
types.Typ[types.UntypedBool],
types.Typ[types.UntypedInt],
types.Typ[types.UntypedRune],
types.Typ[types.UntypedFloat],
types.Typ[types.UntypedComplex],
types.Typ[types.UntypedString],
types.Typ[types.UntypedNil],
// package unsafe
types.Typ[types.UnsafePointer],
// invalid type
types.Typ[types.Invalid], // only appears in packages with errors
// used internally by gc; never used by this package or in .a files
anyType{},
// comparable
types.Universe.Lookup("comparable").Type(),
// any
anyt,
}
predecl[anyt] = decls
return decls
}
type anyType struct{}
func (t anyType) Underlying() types.Type { return t }
func (t anyType) String() string { return "any" }

View File

@@ -1,16 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.18
// +build !go1.18
package gcimporter
import "go/types"
const iexportVersion = iexportVersionGo1_11
func additionalPredeclared() []types.Type {
return nil
}

View File

@@ -1,37 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
package gcimporter
import "go/types"
const iexportVersion = iexportVersionGenerics
// additionalPredeclared returns additional predeclared types in go.1.18.
func additionalPredeclared() []types.Type {
return []types.Type{
// comparable
types.Universe.Lookup("comparable").Type(),
// any
types.Universe.Lookup("any").Type(),
}
}
// See cmd/compile/internal/types.SplitVargenSuffix.
func splitVargenSuffix(name string) (base, suffix string) {
i := len(name)
for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' {
i--
}
const dot = "·"
if i >= len(dot) && name[i-len(dot):i] == dot {
i -= len(dot)
return name[:i], name[i:]
}
return name, ""
}

View File

@@ -1,10 +0,0 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !(go1.18 && goexperiment.unified)
// +build !go1.18 !goexperiment.unified
package gcimporter
const unifiedIR = false

View File

@@ -1,10 +0,0 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18 && goexperiment.unified
// +build go1.18,goexperiment.unified
package gcimporter
const unifiedIR = true

View File

@@ -1,19 +0,0 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.18
// +build !go1.18
package gcimporter
import (
"fmt"
"go/token"
"go/types"
)
func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
err = fmt.Errorf("go/tools compiled with a Go version earlier than 1.18 cannot read unified IR export data")
return
}

View File

@@ -4,16 +4,16 @@
// Derived from go/internal/gcimporter/ureader.go
//go:build go1.18
// +build go1.18
package gcimporter
import (
"fmt"
"go/token"
"go/types"
"sort"
"strings"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/pkgbits"
)
@@ -26,6 +26,7 @@ type pkgReader struct {
ctxt *types.Context
imports map[string]*types.Package // previously imported packages, indexed by path
aliases bool // create types.Alias nodes
// lazily initialized arrays corresponding to the unified IR
// PosBase, Pkg, and Type sections, respectively.
@@ -51,8 +52,7 @@ func (pr *pkgReader) later(fn func()) {
// See cmd/compile/internal/noder.derivedInfo.
type derivedInfo struct {
idx pkgbits.Index
needed bool
idx pkgbits.Index
}
// See cmd/compile/internal/noder.typeInfo.
@@ -62,6 +62,14 @@ type typeInfo struct {
}
func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
if !debug {
defer func() {
if x := recover(); x != nil {
err = fmt.Errorf("internal error in importing %q (%v); please report an issue", path, x)
}
}()
}
s := string(data)
s = s[:strings.LastIndex(s, "\n$$\n")]
input := pkgbits.NewPkgDecoder(path, s)
@@ -91,6 +99,7 @@ func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[st
ctxt: ctxt,
imports: imports,
aliases: aliases.Enabled(),
posBases: make([]string, input.NumElems(pkgbits.RelocPosBase)),
pkgs: make([]*types.Package, input.NumElems(pkgbits.RelocPkg)),
@@ -100,13 +109,17 @@ func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[st
r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
pkg := r.pkg()
r.Bool() // has init
if r.Version().Has(pkgbits.HasInit) {
r.Bool()
}
for i, n := 0, r.Len(); i < n; i++ {
// As if r.obj(), but avoiding the Scope.Lookup call,
// to avoid eager loading of imports.
r.Sync(pkgbits.SyncObject)
assert(!r.Bool())
if r.Version().Has(pkgbits.DerivedFuncInstance) {
assert(!r.Bool())
}
r.p.objIdx(r.Reloc(pkgbits.RelocObj))
assert(r.Len() == 0)
}
@@ -121,6 +134,16 @@ func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[st
iface.Complete()
}
// Imports() of pkg are all of the transitive packages that were loaded.
var imps []*types.Package
for _, imp := range pr.pkgs {
if imp != nil && imp != pkg {
imps = append(imps, imp)
}
}
sort.Sort(byPath(imps))
pkg.SetImports(imps)
pkg.MarkComplete()
return pkg
}
@@ -145,7 +168,7 @@ type readerDict struct {
// tparams is a slice of the constructed TypeParams for the element.
tparams []*types.TypeParam
// devived is a slice of types derived from tparams, which may be
// derived is a slice of types derived from tparams, which may be
// instantiated while reading the current element.
derived []derivedInfo
derivedTypes []types.Type // lazily instantiated from derived
@@ -260,39 +283,9 @@ func (r *reader) doPkg() *types.Package {
pkg := types.NewPackage(path, name)
r.p.imports[path] = pkg
imports := make([]*types.Package, r.Len())
for i := range imports {
imports[i] = r.pkg()
}
pkg.SetImports(flattenImports(imports))
return pkg
}
// flattenImports returns the transitive closure of all imported
// packages rooted from pkgs.
func flattenImports(pkgs []*types.Package) []*types.Package {
var res []*types.Package
seen := make(map[*types.Package]struct{})
for _, pkg := range pkgs {
if _, ok := seen[pkg]; ok {
continue
}
seen[pkg] = struct{}{}
res = append(res, pkg)
// pkg.Imports() is already flattened.
for _, pkg := range pkg.Imports() {
if _, ok := seen[pkg]; ok {
continue
}
seen[pkg] = struct{}{}
res = append(res, pkg)
}
}
return res
}
// @@@ Types
func (r *reader) typ() types.Type {
@@ -481,7 +474,9 @@ func (r *reader) param() *types.Var {
func (r *reader) obj() (types.Object, []types.Type) {
r.Sync(pkgbits.SyncObject)
assert(!r.Bool())
if r.Version().Has(pkgbits.DerivedFuncInstance) {
assert(!r.Bool())
}
pkg, name := r.p.objIdx(r.Reloc(pkgbits.RelocObj))
obj := pkgScope(pkg).Lookup(name)
@@ -535,8 +530,12 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) {
case pkgbits.ObjAlias:
pos := r.pos()
var tparams []*types.TypeParam
if r.Version().Has(pkgbits.AliasTypeParamNames) {
tparams = r.typeParamNames()
}
typ := r.typ()
declare(types.NewTypeName(pos, objPkg, objName, typ))
declare(aliases.NewAlias(r.p.aliases, pos, objPkg, objName, typ, tparams))
case pkgbits.ObjConst:
pos := r.pos()
@@ -563,7 +562,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) {
// If the underlying type is an interface, we need to
// duplicate its methods so we can replace the receiver
// parameter's type (#49906).
if iface, ok := underlying.(*types.Interface); ok && iface.NumExplicitMethods() != 0 {
if iface, ok := types.Unalias(underlying).(*types.Interface); ok && iface.NumExplicitMethods() != 0 {
methods := make([]*types.Func, iface.NumExplicitMethods())
for i := range methods {
fn := iface.ExplicitMethod(i)
@@ -642,7 +641,10 @@ func (pr *pkgReader) objDictIdx(idx pkgbits.Index) *readerDict {
dict.derived = make([]derivedInfo, r.Len())
dict.derivedTypes = make([]types.Type, len(dict.derived))
for i := range dict.derived {
dict.derived[i] = derivedInfo{r.Reloc(pkgbits.RelocType), r.Bool()}
dict.derived[i] = derivedInfo{idx: r.Reloc(pkgbits.RelocType)}
if r.Version().Has(pkgbits.DerivedInfoNeeded) {
assert(!r.Bool())
}
}
pr.retireReader(r)
@@ -736,3 +738,17 @@ func pkgScope(pkg *types.Package) *types.Scope {
}
return types.Universe
}
// See cmd/compile/internal/types.SplitVargenSuffix.
func splitVargenSuffix(name string) (base, suffix string) {
i := len(name)
for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' {
i--
}
const dot = "·"
if i >= len(dot) && name[i-len(dot):i] == dot {
i -= len(dot)
return name[:i], name[i:]
}
return name, ""
}

View File

@@ -8,10 +8,14 @@ package gocommand
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strconv"
@@ -19,9 +23,9 @@ import (
"sync"
"time"
exec "golang.org/x/sys/execabs"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/event/keys"
"golang.org/x/tools/internal/event/label"
)
// An Runner will run go command invocations and serialize
@@ -51,9 +55,22 @@ func (runner *Runner) initialize() {
// 1.14: go: updating go.mod: existing contents have changed since last read
var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`)
// event keys for go command invocations
var (
verb = keys.NewString("verb", "go command verb")
directory = keys.NewString("directory", "")
)
func invLabels(inv Invocation) []label.Label {
return []label.Label{verb.Of(inv.Verb), directory.Of(inv.WorkingDir)}
}
// Run is a convenience wrapper around RunRaw.
// It returns only stdout and a "friendly" error.
func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, error) {
ctx, done := event.Start(ctx, "gocommand.Runner.Run", invLabels(inv)...)
defer done()
stdout, _, friendly, _ := runner.RunRaw(ctx, inv)
return stdout, friendly
}
@@ -61,13 +78,19 @@ func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, e
// RunPiped runs the invocation serially, always waiting for any concurrent
// invocations to complete first.
func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) error {
ctx, done := event.Start(ctx, "gocommand.Runner.RunPiped", invLabels(inv)...)
defer done()
_, err := runner.runPiped(ctx, inv, stdout, stderr)
return err
}
// RunRaw runs the invocation, serializing requests only if they fight over
// go.mod changes.
// Postcondition: both error results have same nilness.
func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
ctx, done := event.Start(ctx, "gocommand.Runner.RunRaw", invLabels(inv)...)
defer done()
// Make sure the runner is always initialized.
runner.initialize()
@@ -75,23 +98,24 @@ func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer
stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv)
// If we encounter a load concurrency error, we need to retry serially.
if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) {
return stdout, stderr, friendlyErr, err
}
event.Error(ctx, "Load concurrency error, will retry serially", err)
if friendlyErr != nil && modConcurrencyError.MatchString(friendlyErr.Error()) {
event.Error(ctx, "Load concurrency error, will retry serially", err)
// Run serially by calling runPiped.
stdout.Reset()
stderr.Reset()
friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr)
}
// Run serially by calling runPiped.
stdout.Reset()
stderr.Reset()
friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr)
return stdout, stderr, friendlyErr, err
}
// Postcondition: both error results have same nilness.
func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
// Wait for 1 worker to become available.
select {
case <-ctx.Done():
return nil, nil, nil, ctx.Err()
return nil, nil, ctx.Err(), ctx.Err()
case runner.inFlight <- struct{}{}:
defer func() { <-runner.inFlight }()
}
@@ -101,6 +125,7 @@ func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes
return stdout, stderr, friendlyErr, err
}
// Postcondition: both error results have same nilness.
func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) {
// Make sure the runner is always initialized.
runner.initialize()
@@ -109,7 +134,7 @@ func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stde
// runPiped commands.
select {
case <-ctx.Done():
return nil, ctx.Err()
return ctx.Err(), ctx.Err()
case runner.serialized <- struct{}{}:
defer func() { <-runner.serialized }()
}
@@ -119,7 +144,7 @@ func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stde
for i := 0; i < maxInFlight; i++ {
select {
case <-ctx.Done():
return nil, ctx.Err()
return ctx.Err(), ctx.Err()
case runner.inFlight <- struct{}{}:
// Make sure we always "return" any workers we took.
defer func() { <-runner.inFlight }()
@@ -136,12 +161,17 @@ type Invocation struct {
BuildFlags []string
// If ModFlag is set, the go command is invoked with -mod=ModFlag.
// TODO(rfindley): remove, in favor of Args.
ModFlag string
// If ModFile is set, the go command is invoked with -modfile=ModFile.
// TODO(rfindley): remove, in favor of Args.
ModFile string
// If Overlay is set, the go command is invoked with -overlay=Overlay.
// Overlay is the name of the JSON overlay file that describes
// unsaved editor buffers; see [WriteOverlays].
// If set, the go command is invoked with -overlay=Overlay.
// TODO(rfindley): remove, in favor of Args.
Overlay string
// If CleanEnv is set, the invocation will run only with the environment
@@ -152,6 +182,7 @@ type Invocation struct {
Logf func(format string, args ...interface{})
}
// Postcondition: both error results have same nilness.
func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) {
rawError = i.run(ctx, stdout, stderr)
if rawError != nil {
@@ -168,12 +199,14 @@ func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io
return
}
func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
log := i.Logf
if log == nil {
log = func(string, ...interface{}) {}
// logf logs if i.Logf is non-nil.
func (i *Invocation) logf(format string, args ...any) {
if i.Logf != nil {
i.Logf(format, args...)
}
}
func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
goArgs := []string{i.Verb}
appendModFile := func() {
@@ -215,12 +248,24 @@ func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
cmd := exec.Command("go", goArgs...)
cmd.Stdout = stdout
cmd.Stderr = stderr
// 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
// https://go.dev/issue/59541: don't wait forever copying stderr
// after the command has exited.
// After CL 484741 we copy stdout manually, so we we'll stop reading that as
// soon as ctx is done. However, we also don't want to wait around forever
// for stderr. Give a much-longer-than-reasonable delay and then assume that
// something has wedged in the kernel or runtime.
cmd.WaitDelay = 30 * time.Second
// The cwd gets resolved to the real path. On Darwin, where
// /tmp is a symlink, this breaks anything that expects the
// working directory to keep the original path, including the
// go command when dealing with modules.
// 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.
//
// os.Getwd 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.
if !i.CleanEnv {
cmd.Env = os.Environ()
}
@@ -229,7 +274,13 @@ func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
cmd.Env = append(cmd.Env, "PWD="+i.WorkingDir)
cmd.Dir = i.WorkingDir
}
defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now())
debugStr := cmdDebugStr(cmd)
i.logf("starting %v", debugStr)
start := time.Now()
defer func() {
i.logf("%s for %v", time.Since(start), debugStr)
}()
return runCmdContext(ctx, cmd)
}
@@ -242,10 +293,86 @@ var DebugHangingGoCommands = false
// runCmdContext is like exec.CommandContext except it sends os.Interrupt
// before os.Kill.
func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
if err := cmd.Start(); err != nil {
func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) {
// If cmd.Stdout is not an *os.File, the exec package will create a pipe and
// copy it to the Writer in a goroutine until the process has finished and
// either the pipe reaches EOF or command's WaitDelay expires.
//
// However, the output from 'go list' can be quite large, and we don't want to
// keep reading (and allocating buffers) if we've already decided we don't
// care about the output. We don't want to wait for the process to finish, and
// we don't wait to wait for the WaitDelay to expire either.
//
// Instead, if cmd.Stdout requires a copying goroutine we explicitly replace
// it with a pipe (which is an *os.File), which we can close in order to stop
// copying output as soon as we realize we don't care about it.
var stdoutW *os.File
if cmd.Stdout != nil {
if _, ok := cmd.Stdout.(*os.File); !ok {
var stdoutR *os.File
stdoutR, stdoutW, err = os.Pipe()
if err != nil {
return err
}
prevStdout := cmd.Stdout
cmd.Stdout = stdoutW
stdoutErr := make(chan error, 1)
go func() {
_, err := io.Copy(prevStdout, stdoutR)
if err != nil {
err = fmt.Errorf("copying stdout: %w", err)
}
stdoutErr <- err
}()
defer func() {
// We started a goroutine to copy a stdout pipe.
// Wait for it to finish, or terminate it if need be.
var err2 error
select {
case err2 = <-stdoutErr:
stdoutR.Close()
case <-ctx.Done():
stdoutR.Close()
// Per https://pkg.go.dev/os#File.Close, the call to stdoutR.Close
// should cause the Read call in io.Copy to unblock and return
// immediately, but we still need to receive from stdoutErr to confirm
// that it has happened.
<-stdoutErr
err2 = ctx.Err()
}
if err == nil {
err = err2
}
}()
// Per https://pkg.go.dev/os/exec#Cmd, “If Stdout and Stderr are the
// same writer, and have a type that can be compared with ==, at most
// one goroutine at a time will call Write.”
//
// Since we're starting a goroutine that writes to cmd.Stdout, we must
// also update cmd.Stderr so that it still holds.
func() {
defer func() { recover() }()
if cmd.Stderr == prevStdout {
cmd.Stderr = cmd.Stdout
}
}()
}
}
startTime := time.Now()
err = cmd.Start()
if stdoutW != nil {
// The child process has inherited the pipe file,
// so close the copy held in this process.
stdoutW.Close()
stdoutW = nil
}
if err != nil {
return err
}
resChan := make(chan error, 1)
go func() {
resChan <- cmd.Wait()
@@ -253,12 +380,15 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
// If we're interested in debugging hanging Go commands, stop waiting after a
// minute and panic with interesting information.
if DebugHangingGoCommands {
debug := DebugHangingGoCommands
if debug {
timer := time.NewTimer(1 * time.Minute)
defer timer.Stop()
select {
case err := <-resChan:
return err
case <-time.After(1 * time.Minute):
HandleHangingGoCommand(cmd.Process)
case <-timer.C:
HandleHangingGoCommand(startTime, cmd)
case <-ctx.Done():
}
} else {
@@ -270,34 +400,29 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
}
// Cancelled. Interrupt and see if it ends voluntarily.
cmd.Process.Signal(os.Interrupt)
select {
case err := <-resChan:
return err
case <-time.After(time.Second):
if err := cmd.Process.Signal(os.Interrupt); err == nil {
// (We used to wait only 1s but this proved
// fragile on loaded builder machines.)
timer := time.NewTimer(5 * time.Second)
defer timer.Stop()
select {
case err := <-resChan:
return err
case <-timer.C:
}
}
// Didn't shut down in response to interrupt. Kill it hard.
// TODO(rfindley): per advice from bcmills@, it may be better to send SIGQUIT
// on certain platforms, such as unix.
if err := cmd.Process.Kill(); err != nil && DebugHangingGoCommands {
// Don't panic here as this reliably fails on windows with EINVAL.
if err := cmd.Process.Kill(); err != nil && !errors.Is(err, os.ErrProcessDone) && debug {
log.Printf("error killing the Go command: %v", err)
}
// See above: don't wait indefinitely if we're debugging hanging Go commands.
if DebugHangingGoCommands {
select {
case err := <-resChan:
return err
case <-time.After(10 * time.Second): // a shorter wait as resChan should return quickly following Kill
HandleHangingGoCommand(cmd.Process)
}
}
return <-resChan
}
func HandleHangingGoCommand(proc *os.Process) {
func HandleHangingGoCommand(start time.Time, cmd *exec.Cmd) {
switch runtime.GOOS {
case "linux", "darwin", "freebsd", "netbsd":
fmt.Fprintln(os.Stderr, `DETECTED A HANGING GO COMMAND
@@ -330,7 +455,7 @@ See golang/go#54461 for more details.`)
panic(fmt.Sprintf("running %s: %v", listFiles, err))
}
}
panic(fmt.Sprintf("detected hanging go command (pid %d): see golang/go#54461 for more details", proc.Pid))
panic(fmt.Sprintf("detected hanging go command (golang/go#54461); waited %s\n\tcommand:%s\n\tpid:%d", time.Since(start), cmd, cmd.Process.Pid))
}
func cmdDebugStr(cmd *exec.Cmd) string {
@@ -354,3 +479,73 @@ func cmdDebugStr(cmd *exec.Cmd) string {
}
return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], strings.Join(args, " "))
}
// WriteOverlays writes each value in the overlay (see the Overlay
// field of go/packages.Config) to a temporary file and returns the name
// of a JSON file describing the mapping that is suitable for the "go
// list -overlay" flag.
//
// On success, the caller must call the cleanup function exactly once
// when the files are no longer needed.
func WriteOverlays(overlay map[string][]byte) (filename string, cleanup func(), err error) {
// Do nothing if there are no overlays in the config.
if len(overlay) == 0 {
return "", func() {}, nil
}
dir, err := os.MkdirTemp("", "gocommand-*")
if err != nil {
return "", nil, err
}
// The caller must clean up this directory,
// unless this function returns an error.
// (The cleanup operand of each return
// statement below is ignored.)
defer func() {
cleanup = func() {
os.RemoveAll(dir)
}
if err != nil {
cleanup()
cleanup = nil
}
}()
// Write each map entry to a temporary file.
overlays := make(map[string]string)
for k, v := range overlay {
// Use a unique basename for each file (001-foo.go),
// to avoid creating nested directories.
base := fmt.Sprintf("%d-%s", 1+len(overlays), filepath.Base(k))
filename := filepath.Join(dir, base)
err := os.WriteFile(filename, v, 0666)
if err != nil {
return "", nil, err
}
overlays[k] = filename
}
// Write the JSON overlay file that maps logical file names to temp files.
//
// OverlayJSON is the format overlay files are expected to be in.
// The Replace map maps from overlaid paths to replacement paths:
// the Go command will forward all reads trying to open
// each overlaid path to its replacement path, or consider the overlaid
// path not to exist if the replacement path is empty.
//
// From golang/go#39958.
type OverlayJSON struct {
Replace map[string]string `json:"replace,omitempty"`
}
b, err := json.Marshal(OverlayJSON{Replace: overlays})
if err != nil {
return "", nil, err
}
filename = filepath.Join(dir, "overlay.json")
if err := os.WriteFile(filename, b, 0666); err != nil {
return "", nil, err
}
return filename, nil, nil
}

View File

@@ -107,3 +107,57 @@ func getMainModuleAnd114(ctx context.Context, inv Invocation, r *Runner) (*Modul
}
return mod, lines[4] == "go1.14", nil
}
// WorkspaceVendorEnabled reports whether workspace vendoring is enabled. It takes a *Runner to execute Go commands
// with the supplied context.Context and Invocation. The Invocation can contain pre-defined fields,
// of which only Verb and Args are modified to run the appropriate Go command.
// Inspired by setDefaultBuildMod in modload/init.go
func WorkspaceVendorEnabled(ctx context.Context, inv Invocation, r *Runner) (bool, []*ModuleJSON, error) {
inv.Verb = "env"
inv.Args = []string{"GOWORK"}
stdout, err := r.Run(ctx, inv)
if err != nil {
return false, nil, err
}
goWork := string(bytes.TrimSpace(stdout.Bytes()))
if fi, err := os.Stat(filepath.Join(filepath.Dir(goWork), "vendor")); err == nil && fi.IsDir() {
mainMods, err := getWorkspaceMainModules(ctx, inv, r)
if err != nil {
return false, nil, err
}
return true, mainMods, nil
}
return false, nil, nil
}
// getWorkspaceMainModules gets the main modules' information.
// This is the information needed to figure out if vendoring should be enabled.
func getWorkspaceMainModules(ctx context.Context, inv Invocation, r *Runner) ([]*ModuleJSON, error) {
const format = `{{.Path}}
{{.Dir}}
{{.GoMod}}
{{.GoVersion}}
`
inv.Verb = "list"
inv.Args = []string{"-m", "-f", format}
stdout, err := r.Run(ctx, inv)
if err != nil {
return nil, err
}
lines := strings.Split(strings.TrimSuffix(stdout.String(), "\n"), "\n")
if len(lines) < 4 {
return nil, fmt.Errorf("unexpected stdout: %q", stdout.String())
}
mods := make([]*ModuleJSON, 0, len(lines)/4)
for i := 0; i < len(lines); i += 4 {
mods = append(mods, &ModuleJSON{
Path: lines[i],
Dir: lines[i+1],
GoMod: lines[i+2],
GoVersion: lines[i+3],
Main: true,
})
}
return mods, nil
}

View File

@@ -23,21 +23,11 @@ import (
func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) {
inv.Verb = "list"
inv.Args = []string{"-e", "-f", `{{context.ReleaseTags}}`, `--`, `unsafe`}
inv.Env = append(append([]string{}, inv.Env...), "GO111MODULE=off")
// Unset any unneeded flags, and remove them from BuildFlags, if they're
// present.
inv.ModFile = ""
inv.BuildFlags = nil // This is not a build command.
inv.ModFlag = ""
var buildFlags []string
for _, flag := range inv.BuildFlags {
// Flags can be prefixed by one or two dashes.
f := strings.TrimPrefix(strings.TrimPrefix(flag, "-"), "-")
if strings.HasPrefix(f, "mod=") || strings.HasPrefix(f, "modfile=") {
continue
}
buildFlags = append(buildFlags, flag)
}
inv.BuildFlags = buildFlags
inv.ModFile = ""
inv.Env = append(inv.Env[:len(inv.Env):len(inv.Env)], "GO111MODULE=off")
stdoutBytes, err := r.Run(ctx, inv)
if err != nil {
return 0, err

View File

@@ -9,23 +9,27 @@ package gopathwalk
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"log"
"io"
"io/fs"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"time"
"golang.org/x/tools/internal/fastwalk"
)
// Options controls the behavior of a Walk call.
type Options struct {
// If Logf is non-nil, debug logging is enabled through this function.
Logf func(format string, args ...interface{})
// Search module caches. Also disables legacy goimports ignore rules.
ModulesEnabled bool
// Maximum number of concurrent calls to user-provided callbacks,
// or 0 for GOMAXPROCS.
Concurrency int
}
// RootType indicates the type of a Root.
@@ -46,22 +50,28 @@ type Root struct {
Type RootType
}
// Walk walks Go source directories ($GOROOT, $GOPATH, etc) to find packages.
// For each package found, add will be called (concurrently) with the absolute
// Walk concurrently walks Go source directories ($GOROOT, $GOPATH, etc) to find packages.
//
// For each package found, add will be called with the absolute
// paths of the containing source directory and the package directory.
// add will be called concurrently.
//
// Unlike filepath.WalkDir, Walk follows symbolic links
// (while guarding against cycles).
func Walk(roots []Root, add func(root Root, dir string), opts Options) {
WalkSkip(roots, add, func(Root, string) bool { return false }, opts)
}
// WalkSkip walks Go source directories ($GOROOT, $GOPATH, etc) to find packages.
// For each package found, add will be called (concurrently) with the absolute
// WalkSkip concurrently walks Go source directories ($GOROOT, $GOPATH, etc) to
// find packages.
//
// For each package found, add will be called with the absolute
// paths of the containing source directory and the package directory.
// For each directory that will be scanned, skip will be called (concurrently)
// For each directory that will be scanned, skip will be called
// with the absolute paths of the containing source directory and the directory.
// If skip returns false on a directory it will be processed.
// add will be called concurrently.
// skip will be called concurrently.
//
// Unlike filepath.WalkDir, WalkSkip follows symbolic links
// (while guarding against cycles).
func WalkSkip(roots []Root, add func(root Root, dir string), skip func(root Root, dir string) bool, opts Options) {
for _, root := range roots {
walkDir(root, add, skip, opts)
@@ -70,30 +80,51 @@ func WalkSkip(roots []Root, add func(root Root, dir string), skip func(root Root
// walkDir creates a walker and starts fastwalk with this walker.
func walkDir(root Root, add func(Root, string), skip func(root Root, dir string) bool, opts Options) {
if opts.Logf == nil {
opts.Logf = func(format string, args ...interface{}) {}
}
if _, err := os.Stat(root.Path); os.IsNotExist(err) {
if opts.Logf != nil {
opts.Logf("skipping nonexistent directory: %v", root.Path)
}
opts.Logf("skipping nonexistent directory: %v", root.Path)
return
}
start := time.Now()
if opts.Logf != nil {
opts.Logf("gopathwalk: scanning %s", root.Path)
opts.Logf("scanning %s", root.Path)
concurrency := opts.Concurrency
if concurrency == 0 {
// The walk be either CPU-bound or I/O-bound, depending on what the
// caller-supplied add function does and the details of the user's platform
// and machine. Rather than trying to fine-tune the concurrency level for a
// specific environment, we default to GOMAXPROCS: it is likely to be a good
// choice for a CPU-bound add function, and if it is instead I/O-bound, then
// dealing with I/O saturation is arguably the job of the kernel and/or
// runtime. (Oversaturating I/O seems unlikely to harm performance as badly
// as failing to saturate would.)
concurrency = runtime.GOMAXPROCS(0)
}
w := &walker{
root: root,
add: add,
skip: skip,
opts: opts,
sem: make(chan struct{}, concurrency),
}
w.init()
if err := fastwalk.Walk(root.Path, w.walk); err != nil {
log.Printf("gopathwalk: scanning directory %v: %v", root.Path, err)
}
if opts.Logf != nil {
opts.Logf("gopathwalk: scanned %s in %v", root.Path, time.Since(start))
w.sem <- struct{}{}
path := root.Path
if path == "" {
path = "."
}
if fi, err := os.Lstat(path); err == nil {
w.walk(path, nil, fs.FileInfoToDirEntry(fi))
} else {
w.opts.Logf("scanning directory %v: %v", root.Path, err)
}
<-w.sem
w.walking.Wait()
opts.Logf("scanned %s in %v", root.Path, time.Since(start))
}
// walker is the callback for fastwalk.Walk.
@@ -103,7 +134,18 @@ type walker struct {
skip func(Root, string) bool // The callback that will be invoked for every dir. dir is skipped if it returns true.
opts Options // Options passed to Walk by the user.
ignoredDirs []os.FileInfo // The ignored directories, loaded from .goimportsignore files.
walking sync.WaitGroup
sem chan struct{} // Channel of semaphore tokens; send to acquire, receive to release.
ignoredDirs []string
added sync.Map // map[string]bool
}
// A symlinkList is a linked list of os.FileInfos for parent directories
// reached via symlinks.
type symlinkList struct {
info os.FileInfo
prev *symlinkList
}
// init initializes the walker based on its Options
@@ -119,14 +161,8 @@ func (w *walker) init() {
for _, p := range ignoredPaths {
full := filepath.Join(w.root.Path, p)
if fi, err := os.Stat(full); err == nil {
w.ignoredDirs = append(w.ignoredDirs, fi)
if w.opts.Logf != nil {
w.opts.Logf("Directory added to ignore list: %s", full)
}
} else if w.opts.Logf != nil {
w.opts.Logf("Error statting ignored directory: %v", err)
}
w.ignoredDirs = append(w.ignoredDirs, full)
w.opts.Logf("Directory added to ignore list: %s", full)
}
}
@@ -135,13 +171,11 @@ func (w *walker) init() {
// The provided path is one of the $GOPATH entries with "src" appended.
func (w *walker) getIgnoredDirs(path string) []string {
file := filepath.Join(path, ".goimportsignore")
slurp, err := ioutil.ReadFile(file)
if w.opts.Logf != nil {
if err != nil {
w.opts.Logf("%v", err)
} else {
w.opts.Logf("Read %s", file)
}
slurp, err := os.ReadFile(file)
if err != nil {
w.opts.Logf("%v", err)
} else {
w.opts.Logf("Read %s", file)
}
if err != nil {
return nil
@@ -160,9 +194,9 @@ func (w *walker) getIgnoredDirs(path string) []string {
}
// shouldSkipDir reports whether the file should be skipped or not.
func (w *walker) shouldSkipDir(fi os.FileInfo, dir string) bool {
func (w *walker) shouldSkipDir(dir string) bool {
for _, ignoredDir := range w.ignoredDirs {
if os.SameFile(fi, ignoredDir) {
if dir == ignoredDir {
return true
}
}
@@ -174,81 +208,130 @@ func (w *walker) shouldSkipDir(fi os.FileInfo, dir string) bool {
}
// walk walks through the given path.
func (w *walker) walk(path string, typ os.FileMode) error {
if typ.IsRegular() {
//
// Errors are logged if w.opts.Logf is non-nil, but otherwise ignored.
func (w *walker) walk(path string, pathSymlinks *symlinkList, d fs.DirEntry) {
if d.Type()&os.ModeSymlink != 0 {
// Walk the symlink's target rather than the symlink itself.
//
// (Note that os.Stat, unlike the lower-lever os.Readlink,
// follows arbitrarily many layers of symlinks, so it will eventually
// reach either a non-symlink or a nonexistent target.)
//
// TODO(bcmills): 'go list all' itself ignores symlinks within GOROOT/src
// and GOPATH/src. Do we really need to traverse them here? If so, why?
fi, err := os.Stat(path)
if err != nil {
w.opts.Logf("%v", err)
return
}
// Avoid walking symlink cycles: if we have already followed a symlink to
// this directory as a parent of itself, don't follow it again.
//
// This doesn't catch the first time through a cycle, but it also minimizes
// the number of extra stat calls we make if we *don't* encounter a cycle.
// Since we don't actually expect to encounter symlink cycles in practice,
// this seems like the right tradeoff.
for parent := pathSymlinks; parent != nil; parent = parent.prev {
if os.SameFile(fi, parent.info) {
return
}
}
pathSymlinks = &symlinkList{
info: fi,
prev: pathSymlinks,
}
d = fs.FileInfoToDirEntry(fi)
}
if d.Type().IsRegular() {
if !strings.HasSuffix(path, ".go") {
return
}
dir := filepath.Dir(path)
if dir == w.root.Path && (w.root.Type == RootGOROOT || w.root.Type == RootGOPATH) {
// Doesn't make sense to have regular files
// directly in your $GOPATH/src or $GOROOT/src.
return fastwalk.ErrSkipFiles
}
if !strings.HasSuffix(path, ".go") {
return nil
//
// TODO(bcmills): there are many levels of directory within
// RootModuleCache where this also wouldn't make sense,
// Can we generalize this to any directory without a corresponding
// import path?
return
}
w.add(w.root, dir)
return fastwalk.ErrSkipFiles
}
if typ == os.ModeDir {
base := filepath.Base(path)
if base == "" || base[0] == '.' || base[0] == '_' ||
base == "testdata" ||
(w.root.Type == RootGOROOT && w.opts.ModulesEnabled && base == "vendor") ||
(!w.opts.ModulesEnabled && base == "node_modules") {
return filepath.SkipDir
}
fi, err := os.Lstat(path)
if err == nil && w.shouldSkipDir(fi, path) {
return filepath.SkipDir
}
return nil
}
if typ == os.ModeSymlink {
base := filepath.Base(path)
if strings.HasPrefix(base, ".#") {
// Emacs noise.
return nil
}
if w.shouldTraverse(path) {
return fastwalk.ErrTraverseLink
if _, dup := w.added.LoadOrStore(dir, true); !dup {
w.add(w.root, dir)
}
}
return nil
}
// shouldTraverse reports whether the symlink fi, found in dir,
// should be followed. It makes sure symlinks were never visited
// before to avoid symlink loops.
func (w *walker) shouldTraverse(path string) bool {
ts, err := os.Stat(path)
if !d.IsDir() {
return
}
base := filepath.Base(path)
if base == "" || base[0] == '.' || base[0] == '_' ||
base == "testdata" ||
(w.root.Type == RootGOROOT && w.opts.ModulesEnabled && base == "vendor") ||
(!w.opts.ModulesEnabled && base == "node_modules") ||
w.shouldSkipDir(path) {
return
}
// Read the directory and walk its entries.
f, err := os.Open(path)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return false
}
if !ts.IsDir() {
return false
}
if w.shouldSkipDir(ts, filepath.Dir(path)) {
return false
}
// Check for symlink loops by statting each directory component
// and seeing if any are the same file as ts.
for {
parent := filepath.Dir(path)
if parent == path {
// Made it to the root without seeing a cycle.
// Use this symlink.
return true
}
parentInfo, err := os.Stat(parent)
if err != nil {
return false
}
if os.SameFile(ts, parentInfo) {
// Cycle. Don't traverse.
return false
}
path = parent
w.opts.Logf("%v", err)
return
}
defer f.Close()
for {
// We impose an arbitrary limit on the number of ReadDir results per
// directory to limit the amount of memory consumed for stale or upcoming
// directory entries. The limit trades off CPU (number of syscalls to read
// the whole directory) against RAM (reachable directory entries other than
// the one currently being processed).
//
// Since we process the directories recursively, we will end up maintaining
// a slice of entries for each level of the directory tree.
// (Compare https://go.dev/issue/36197.)
ents, err := f.ReadDir(1024)
if err != nil {
if err != io.EOF {
w.opts.Logf("%v", err)
}
break
}
for _, d := range ents {
nextPath := filepath.Join(path, d.Name())
if d.IsDir() {
select {
case w.sem <- struct{}{}:
// Got a new semaphore token, so we can traverse the directory concurrently.
d := d
w.walking.Add(1)
go func() {
defer func() {
<-w.sem
w.walking.Done()
}()
w.walk(nextPath, pathSymlinks, d)
}()
continue
default:
// No tokens available, so traverse serially.
}
}
w.walk(nextPath, pathSymlinks, d)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate go run mkstdlib.go
// Package imports implements a Go pretty-printer (like package "go/format")
// that also adds or removes import statements as necessary.
package imports
@@ -11,6 +9,7 @@ package imports
import (
"bufio"
"bytes"
"context"
"fmt"
"go/ast"
"go/format"
@@ -23,6 +22,7 @@ import (
"strings"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/internal/event"
)
// Options is golang.org/x/tools/imports.Options with extra internal-only options.
@@ -66,14 +66,17 @@ func Process(filename string, src []byte, opt *Options) (formatted []byte, err e
//
// Note that filename's directory influences which imports can be chosen,
// so it is important that filename be accurate.
func FixImports(filename string, src []byte, opt *Options) (fixes []*ImportFix, err error) {
func FixImports(ctx context.Context, filename string, src []byte, opt *Options) (fixes []*ImportFix, err error) {
ctx, done := event.Start(ctx, "imports.FixImports")
defer done()
fileSet := token.NewFileSet()
file, _, err := parse(fileSet, filename, src, opt)
if err != nil {
return nil, err
}
return getFixes(fileSet, file, filename, opt.Env)
return getFixes(ctx, fileSet, file, filename, opt.Env)
}
// ApplyFixes applies all of the fixes to the file and formats it. extraMode
@@ -83,7 +86,7 @@ func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options, e
// Don't use parse() -- we don't care about fragments or statement lists
// here, and we need to work with unparseable files.
fileSet := token.NewFileSet()
parserMode := parser.Mode(0)
parserMode := parser.SkipObjectResolution
if opt.Comments {
parserMode |= parser.ParseComments
}
@@ -104,7 +107,7 @@ func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options, e
}
// formatFile formats the file syntax tree.
// It may mutate the token.FileSet.
// It may mutate the token.FileSet and the ast.File.
//
// If an adjust function is provided, it is called after formatting
// with the original source (formatFile's src parameter) and the
@@ -162,7 +165,7 @@ func formatFile(fset *token.FileSet, file *ast.File, src []byte, adjust func(ori
// parse parses src, which was read from filename,
// as a Go source file or statement list.
func parse(fset *token.FileSet, filename string, src []byte, opt *Options) (*ast.File, func(orig, src []byte) []byte, error) {
parserMode := parser.Mode(0)
var parserMode parser.Mode // legacy ast.Object resolution is required here
if opt.Comments {
parserMode |= parser.ParseComments
}
@@ -231,7 +234,7 @@ func parse(fset *token.FileSet, filename string, src []byte, opt *Options) (*ast
src = src[:len(src)-len("}\n")]
// Gofmt has also indented the function body one level.
// Remove that indent.
src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1)
src = bytes.ReplaceAll(src, []byte("\n\t"), []byte("\n"))
return matchSpace(orig, src)
}
return file, adjust, nil

View File

@@ -9,7 +9,6 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
@@ -19,80 +18,141 @@ import (
"strings"
"golang.org/x/mod/module"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/gocommand"
"golang.org/x/tools/internal/gopathwalk"
"golang.org/x/tools/internal/stdlib"
)
// ModuleResolver implements resolver for modules using the go command as little
// as feasible.
// Notes(rfindley): ModuleResolver appears to be heavily optimized for scanning
// as fast as possible, which is desirable for a call to goimports from the
// command line, but it doesn't work as well for gopls, where it suffers from
// slow startup (golang/go#44863) and intermittent hanging (golang/go#59216),
// both caused by populating the cache, albeit in slightly different ways.
//
// A high level list of TODOs:
// - Optimize the scan itself, as there is some redundancy statting and
// reading go.mod files.
// - Invert the relationship between ProcessEnv and Resolver (see the
// docstring of ProcessEnv).
// - Make it easier to use an external resolver implementation.
//
// Smaller TODOs are annotated in the code below.
// ModuleResolver implements the Resolver interface for a workspace using
// modules.
//
// A goal of the ModuleResolver is to invoke the Go command as little as
// possible. To this end, it runs the Go command only for listing module
// information (i.e. `go list -m -e -json ...`). Package scanning, the process
// of loading package information for the modules, is implemented internally
// via the scan method.
//
// It has two types of state: the state derived from the go command, which
// is populated by init, and the state derived from scans, which is populated
// via scan. A root is considered scanned if it has been walked to discover
// directories. However, if the scan did not require additional information
// from the directory (such as package name or exports), the directory
// information itself may be partially populated. It will be lazily filled in
// as needed by scans, using the scanCallback.
type ModuleResolver struct {
env *ProcessEnv
moduleCacheDir string
dummyVendorMod *gocommand.ModuleJSON // If vendoring is enabled, the pseudo-module that represents the /vendor directory.
roots []gopathwalk.Root
scanSema chan struct{} // scanSema prevents concurrent scans and guards scannedRoots.
scannedRoots map[gopathwalk.Root]bool
env *ProcessEnv
initialized bool
mains []*gocommand.ModuleJSON
mainByDir map[string]*gocommand.ModuleJSON
modsByModPath []*gocommand.ModuleJSON // All modules, ordered by # of path components in module Path...
modsByDir []*gocommand.ModuleJSON // ...or Dir.
// Module state, populated during construction
dummyVendorMod *gocommand.ModuleJSON // if vendoring is enabled, a pseudo-module to represent the /vendor directory
moduleCacheDir string // GOMODCACHE, inferred from GOPATH if unset
roots []gopathwalk.Root // roots to scan, in approximate order of importance
mains []*gocommand.ModuleJSON // main modules
mainByDir map[string]*gocommand.ModuleJSON // module information by dir, to join with roots
modsByModPath []*gocommand.ModuleJSON // all modules, ordered by # of path components in their module path
modsByDir []*gocommand.ModuleJSON // ...or by the number of path components in their Dir.
// moduleCacheCache stores information about the module cache.
moduleCacheCache *dirInfoCache
otherCache *dirInfoCache
// Scanning state, populated by scan
// scanSema prevents concurrent scans, and guards scannedRoots and the cache
// fields below (though the caches themselves are concurrency safe).
// Receive to acquire, send to release.
scanSema chan struct{}
scannedRoots map[gopathwalk.Root]bool // if true, root has been walked
// Caches of directory info, populated by scans and scan callbacks
//
// moduleCacheCache stores cached information about roots in the module
// cache, which are immutable and therefore do not need to be invalidated.
//
// otherCache stores information about all other roots (even GOROOT), which
// may change.
moduleCacheCache *DirInfoCache
otherCache *DirInfoCache
}
func newModuleResolver(e *ProcessEnv) *ModuleResolver {
// newModuleResolver returns a new module-aware goimports resolver.
//
// Note: use caution when modifying this constructor: changes must also be
// reflected in ModuleResolver.ClearForNewScan.
func newModuleResolver(e *ProcessEnv, moduleCacheCache *DirInfoCache) (*ModuleResolver, error) {
r := &ModuleResolver{
env: e,
scanSema: make(chan struct{}, 1),
}
r.scanSema <- struct{}{}
return r
}
func (r *ModuleResolver) init() error {
if r.initialized {
return nil
}
r.scanSema <- struct{}{} // release
goenv, err := r.env.goEnv()
if err != nil {
return err
return nil, err
}
// TODO(rfindley): can we refactor to share logic with r.env.invokeGo?
inv := gocommand.Invocation{
BuildFlags: r.env.BuildFlags,
ModFlag: r.env.ModFlag,
ModFile: r.env.ModFile,
Env: r.env.env(),
Logf: r.env.Logf,
WorkingDir: r.env.WorkingDir,
}
vendorEnabled := false
var mainModVendor *gocommand.ModuleJSON
var mainModVendor *gocommand.ModuleJSON // for module vendoring
var mainModsVendor []*gocommand.ModuleJSON // for workspace vendoring
// Module vendor directories are ignored in workspace mode:
// https://go.googlesource.com/proposal/+/master/design/45713-workspace.md
if len(r.env.Env["GOWORK"]) == 0 {
goWork := r.env.Env["GOWORK"]
if len(goWork) == 0 {
// TODO(rfindley): VendorEnabled runs the go command to get GOFLAGS, but
// they should be available from the ProcessEnv. Can we avoid the redundant
// invocation?
vendorEnabled, mainModVendor, err = gocommand.VendorEnabled(context.TODO(), inv, r.env.GocmdRunner)
if err != nil {
return err
return nil, err
}
} else {
vendorEnabled, mainModsVendor, err = gocommand.WorkspaceVendorEnabled(context.Background(), inv, r.env.GocmdRunner)
if err != nil {
return nil, err
}
}
if mainModVendor != nil && vendorEnabled {
// Vendor mode is on, so all the non-Main modules are irrelevant,
// and we need to search /vendor for everything.
r.mains = []*gocommand.ModuleJSON{mainModVendor}
r.dummyVendorMod = &gocommand.ModuleJSON{
Path: "",
Dir: filepath.Join(mainModVendor.Dir, "vendor"),
if vendorEnabled {
if mainModVendor != nil {
// Module vendor mode is on, so all the non-Main modules are irrelevant,
// and we need to search /vendor for everything.
r.mains = []*gocommand.ModuleJSON{mainModVendor}
r.dummyVendorMod = &gocommand.ModuleJSON{
Path: "",
Dir: filepath.Join(mainModVendor.Dir, "vendor"),
}
r.modsByModPath = []*gocommand.ModuleJSON{mainModVendor, r.dummyVendorMod}
r.modsByDir = []*gocommand.ModuleJSON{mainModVendor, r.dummyVendorMod}
} else {
// Workspace vendor mode is on, so all the non-Main modules are irrelevant,
// and we need to search /vendor for everything.
r.mains = mainModsVendor
r.dummyVendorMod = &gocommand.ModuleJSON{
Path: "",
Dir: filepath.Join(filepath.Dir(goWork), "vendor"),
}
r.modsByModPath = append(append([]*gocommand.ModuleJSON{}, mainModsVendor...), r.dummyVendorMod)
r.modsByDir = append(append([]*gocommand.ModuleJSON{}, mainModsVendor...), r.dummyVendorMod)
}
r.modsByModPath = []*gocommand.ModuleJSON{mainModVendor, r.dummyVendorMod}
r.modsByDir = []*gocommand.ModuleJSON{mainModVendor, r.dummyVendorMod}
} else {
// Vendor mode is off, so run go list -m ... to find everything.
err := r.initAllMods()
@@ -100,19 +160,14 @@ func (r *ModuleResolver) init() error {
// GO111MODULE=on. Other errors are fatal.
if err != nil {
if errMsg := err.Error(); !strings.Contains(errMsg, "working directory is not part of a module") && !strings.Contains(errMsg, "go.mod file not found") {
return err
return nil, err
}
}
}
if gmc := r.env.Env["GOMODCACHE"]; gmc != "" {
r.moduleCacheDir = gmc
} else {
gopaths := filepath.SplitList(goenv["GOPATH"])
if len(gopaths) == 0 {
return fmt.Errorf("empty GOPATH")
}
r.moduleCacheDir = filepath.Join(gopaths[0], "/pkg/mod")
r.moduleCacheDir = gomodcacheForEnv(goenv)
if r.moduleCacheDir == "" {
return nil, fmt.Errorf("cannot resolve GOMODCACHE")
}
sort.Slice(r.modsByModPath, func(i, j int) bool {
@@ -123,13 +178,14 @@ func (r *ModuleResolver) init() error {
})
sort.Slice(r.modsByDir, func(i, j int) bool {
count := func(x int) int {
return strings.Count(r.modsByDir[x].Dir, "/")
return strings.Count(r.modsByDir[x].Dir, string(filepath.Separator))
}
return count(j) < count(i) // descending order
})
r.roots = []gopathwalk.Root{
{Path: filepath.Join(goenv["GOROOT"], "/src"), Type: gopathwalk.RootGOROOT},
r.roots = []gopathwalk.Root{}
if goenv["GOROOT"] != "" { // "" happens in tests
r.roots = append(r.roots, gopathwalk.Root{Path: filepath.Join(goenv["GOROOT"], "/src"), Type: gopathwalk.RootGOROOT})
}
r.mainByDir = make(map[string]*gocommand.ModuleJSON)
for _, main := range r.mains {
@@ -141,7 +197,11 @@ func (r *ModuleResolver) init() error {
} else {
addDep := func(mod *gocommand.ModuleJSON) {
if mod.Replace == nil {
// This is redundant with the cache, but we'll skip it cheaply enough.
// This is redundant with the cache, but we'll skip it cheaply enough
// when we encounter it in the module cache scan.
//
// Including it at a lower index in r.roots than the module cache dir
// helps prioritize matches from within existing dependencies.
r.roots = append(r.roots, gopathwalk.Root{Path: mod.Dir, Type: gopathwalk.RootModuleCache})
} else {
r.roots = append(r.roots, gopathwalk.Root{Path: mod.Dir, Type: gopathwalk.RootOther})
@@ -158,24 +218,43 @@ func (r *ModuleResolver) init() error {
addDep(mod)
}
}
// If provided, share the moduleCacheCache.
//
// TODO(rfindley): The module cache is immutable. However, the loaded
// exports do depend on GOOS and GOARCH. Fortunately, the
// ProcessEnv.buildContext does not adjust these from build.DefaultContext
// (even though it should). So for now, this is OK to share, but we need to
// add logic for handling GOOS/GOARCH.
r.moduleCacheCache = moduleCacheCache
r.roots = append(r.roots, gopathwalk.Root{Path: r.moduleCacheDir, Type: gopathwalk.RootModuleCache})
}
r.scannedRoots = map[gopathwalk.Root]bool{}
if r.moduleCacheCache == nil {
r.moduleCacheCache = &dirInfoCache{
dirs: map[string]*directoryPackageInfo{},
listeners: map[*int]cacheListener{},
}
r.moduleCacheCache = NewDirInfoCache()
}
if r.otherCache == nil {
r.otherCache = &dirInfoCache{
dirs: map[string]*directoryPackageInfo{},
listeners: map[*int]cacheListener{},
}
r.otherCache = NewDirInfoCache()
return r, nil
}
// gomodcacheForEnv returns the GOMODCACHE value to use based on the given env
// map, which must have GOMODCACHE and GOPATH populated.
//
// TODO(rfindley): this is defensive refactoring.
// 1. Is this even relevant anymore? Can't we just read GOMODCACHE.
// 2. Use this to separate module cache scanning from other scanning.
func gomodcacheForEnv(goenv map[string]string) string {
if gmc := goenv["GOMODCACHE"]; gmc != "" {
// golang/go#67156: ensure that the module cache is clean, since it is
// assumed as a prefix to directories scanned by gopathwalk, which are
// themselves clean.
return filepath.Clean(gmc)
}
r.initialized = true
return nil
gopaths := filepath.SplitList(goenv["GOPATH"])
if len(gopaths) == 0 {
return ""
}
return filepath.Join(gopaths[0], "/pkg/mod")
}
func (r *ModuleResolver) initAllMods() error {
@@ -189,9 +268,7 @@ func (r *ModuleResolver) initAllMods() error {
return err
}
if mod.Dir == "" {
if r.env.Logf != nil {
r.env.Logf("module %v has not been downloaded and will be ignored", mod.Path)
}
r.env.logf("module %v has not been downloaded and will be ignored", mod.Path)
// Can't do anything with a module that's not downloaded.
continue
}
@@ -206,30 +283,86 @@ func (r *ModuleResolver) initAllMods() error {
return nil
}
func (r *ModuleResolver) ClearForNewScan() {
<-r.scanSema
r.scannedRoots = map[gopathwalk.Root]bool{}
r.otherCache = &dirInfoCache{
dirs: map[string]*directoryPackageInfo{},
listeners: map[*int]cacheListener{},
}
r.scanSema <- struct{}{}
}
// ClearForNewScan invalidates the last scan.
//
// It preserves the set of roots, but forgets about the set of directories.
// Though it forgets the set of module cache directories, it remembers their
// contents, since they are assumed to be immutable.
func (r *ModuleResolver) ClearForNewScan() Resolver {
<-r.scanSema // acquire r, to guard scannedRoots
r2 := &ModuleResolver{
env: r.env,
dummyVendorMod: r.dummyVendorMod,
moduleCacheDir: r.moduleCacheDir,
roots: r.roots,
mains: r.mains,
mainByDir: r.mainByDir,
modsByModPath: r.modsByModPath,
func (r *ModuleResolver) ClearForNewMod() {
<-r.scanSema
*r = ModuleResolver{
env: r.env,
scanSema: make(chan struct{}, 1),
scannedRoots: make(map[gopathwalk.Root]bool),
otherCache: NewDirInfoCache(),
moduleCacheCache: r.moduleCacheCache,
otherCache: r.otherCache,
scanSema: r.scanSema,
}
r.init()
r.scanSema <- struct{}{}
r2.scanSema <- struct{}{} // r2 must start released
// Invalidate root scans. We don't need to invalidate module cache roots,
// because they are immutable.
// (We don't support a use case where GOMODCACHE is cleaned in the middle of
// e.g. a gopls session: the user must restart gopls to get accurate
// imports.)
//
// Scanning for new directories in GOMODCACHE should be handled elsewhere,
// via a call to ScanModuleCache.
for _, root := range r.roots {
if root.Type == gopathwalk.RootModuleCache && r.scannedRoots[root] {
r2.scannedRoots[root] = true
}
}
r.scanSema <- struct{}{} // release r
return r2
}
// findPackage returns the module and directory that contains the package at
// the given import path, or returns nil, "" if no module is in scope.
// ClearModuleInfo invalidates resolver state that depends on go.mod file
// contents (essentially, the output of go list -m -json ...).
//
// Notably, it does not forget directory contents, which are reset
// asynchronously via ClearForNewScan.
//
// If the ProcessEnv is a GOPATH environment, ClearModuleInfo is a no op.
//
// TODO(rfindley): move this to a new env.go, consolidating ProcessEnv methods.
func (e *ProcessEnv) ClearModuleInfo() {
if r, ok := e.resolver.(*ModuleResolver); ok {
resolver, err := newModuleResolver(e, e.ModCache)
if err != nil {
e.resolver = nil
e.resolverErr = err
return
}
<-r.scanSema // acquire (guards caches)
resolver.moduleCacheCache = r.moduleCacheCache
resolver.otherCache = r.otherCache
r.scanSema <- struct{}{} // release
e.UpdateResolver(resolver)
}
}
// UpdateResolver sets the resolver for the ProcessEnv to use in imports
// operations. Only for use with the result of [Resolver.ClearForNewScan].
//
// TODO(rfindley): this awkward API is a result of the (arguably) inverted
// relationship between configuration and state described in the doc comment
// for [ProcessEnv].
func (e *ProcessEnv) UpdateResolver(r Resolver) {
e.resolver = r
e.resolverErr = nil
}
// findPackage returns the module and directory from within the main modules
// and their dependencies that contains the package at the given import path,
// or returns nil, "" if no module is in scope.
func (r *ModuleResolver) findPackage(importPath string) (*gocommand.ModuleJSON, string) {
// This can't find packages in the stdlib, but that's harmless for all
// the existing code paths.
@@ -264,7 +397,7 @@ func (r *ModuleResolver) findPackage(importPath string) (*gocommand.ModuleJSON,
}
// Not cached. Read the filesystem.
pkgFiles, err := ioutil.ReadDir(pkgDir)
pkgFiles, err := os.ReadDir(pkgDir)
if err != nil {
continue
}
@@ -295,10 +428,6 @@ func (r *ModuleResolver) cacheStore(info directoryPackageInfo) {
}
}
func (r *ModuleResolver) cacheKeys() []string {
return append(r.moduleCacheCache.Keys(), r.otherCache.Keys()...)
}
// cachePackageName caches the package name for a dir already in the cache.
func (r *ModuleResolver) cachePackageName(info directoryPackageInfo) (string, error) {
if info.rootType == gopathwalk.RootModuleCache {
@@ -307,7 +436,7 @@ func (r *ModuleResolver) cachePackageName(info directoryPackageInfo) (string, er
return r.otherCache.CachePackageName(info)
}
func (r *ModuleResolver) cacheExports(ctx context.Context, env *ProcessEnv, info directoryPackageInfo) (string, []string, error) {
func (r *ModuleResolver) cacheExports(ctx context.Context, env *ProcessEnv, info directoryPackageInfo) (string, []stdlib.Symbol, error) {
if info.rootType == gopathwalk.RootModuleCache {
return r.moduleCacheCache.CacheExports(ctx, env, info)
}
@@ -327,6 +456,10 @@ func (r *ModuleResolver) findModuleByDir(dir string) *gocommand.ModuleJSON {
// - in /vendor/ in -mod=vendor mode.
// - nested module? Dunno.
// Rumor has it that replace targets cannot contain other replace targets.
//
// Note that it is critical here that modsByDir is sorted to have deeper dirs
// first. This ensures that findModuleByDir finds the innermost module.
// See also golang/go#56291.
for _, m := range r.modsByDir {
if !strings.HasPrefix(dir, m.Dir) {
continue
@@ -363,15 +496,15 @@ func (r *ModuleResolver) dirIsNestedModule(dir string, mod *gocommand.ModuleJSON
return modDir != mod.Dir
}
func (r *ModuleResolver) modInfo(dir string) (modDir string, modName string) {
readModName := func(modFile string) string {
modBytes, err := ioutil.ReadFile(modFile)
if err != nil {
return ""
}
return modulePath(modBytes)
func readModName(modFile string) string {
modBytes, err := os.ReadFile(modFile)
if err != nil {
return ""
}
return modulePath(modBytes)
}
func (r *ModuleResolver) modInfo(dir string) (modDir, modName string) {
if r.dirInModuleCache(dir) {
if matches := modCacheRegexp.FindStringSubmatch(dir); len(matches) == 3 {
index := strings.Index(dir, matches[1]+"@"+matches[2])
@@ -405,11 +538,9 @@ func (r *ModuleResolver) dirInModuleCache(dir string) bool {
}
func (r *ModuleResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
if err := r.init(); err != nil {
return nil, err
}
names := map[string]string{}
for _, path := range importPaths {
// TODO(rfindley): shouldn't this use the dirInfoCache?
_, packageDir := r.findPackage(path)
if packageDir == "" {
continue
@@ -424,9 +555,8 @@ func (r *ModuleResolver) loadPackageNames(importPaths []string, srcDir string) (
}
func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error {
if err := r.init(); err != nil {
return err
}
ctx, done := event.Start(ctx, "imports.ModuleResolver.scan")
defer done()
processDir := func(info directoryPackageInfo) {
// Skip this directory if we were not able to get the package information successfully.
@@ -437,18 +567,18 @@ func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error
if err != nil {
return
}
if !callback.dirFound(pkg) {
return
}
pkg.packageName, err = r.cachePackageName(info)
if err != nil {
return
}
if !callback.packageNameLoaded(pkg) {
return
}
_, exports, err := r.loadExports(ctx, pkg, false)
if err != nil {
return
@@ -487,7 +617,6 @@ func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error
return packageScanned
}
// Add anything new to the cache, and process it if we're still listening.
add := func(root gopathwalk.Root, dir string) {
r.cacheStore(r.scanDirForPackage(root, dir))
}
@@ -502,9 +631,9 @@ func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error
select {
case <-ctx.Done():
return
case <-r.scanSema:
case <-r.scanSema: // acquire
}
defer func() { r.scanSema <- struct{}{} }()
defer func() { r.scanSema <- struct{}{} }() // release
// We have the lock on r.scannedRoots, and no other scans can run.
for _, root := range roots {
if ctx.Err() != nil {
@@ -527,7 +656,7 @@ func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error
}
func (r *ModuleResolver) scoreImportPath(ctx context.Context, path string) float64 {
if _, ok := stdlib[path]; ok {
if stdlib.HasPackage(path) {
return MaxRelevance
}
mod, _ := r.findPackage(path)
@@ -605,10 +734,7 @@ func (r *ModuleResolver) canonicalize(info directoryPackageInfo) (*pkg, error) {
return res, nil
}
func (r *ModuleResolver) loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []string, error) {
if err := r.init(); err != nil {
return "", nil, err
}
func (r *ModuleResolver) loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []stdlib.Symbol, error) {
if info, ok := r.cacheLoad(pkg.dir); ok && !includeTest {
return r.cacheExports(ctx, r.env, info)
}
@@ -617,8 +743,8 @@ func (r *ModuleResolver) loadExports(ctx context.Context, pkg *pkg, includeTest
func (r *ModuleResolver) scanDirForPackage(root gopathwalk.Root, dir string) directoryPackageInfo {
subdir := ""
if dir != root.Path {
subdir = dir[len(root.Path)+len("/"):]
if prefix := root.Path + string(filepath.Separator); strings.HasPrefix(dir, prefix) {
subdir = dir[len(prefix):]
}
importPath := filepath.ToSlash(subdir)
if strings.HasPrefix(importPath, "vendor/") {
@@ -641,9 +767,7 @@ func (r *ModuleResolver) scanDirForPackage(root gopathwalk.Root, dir string) dir
}
modPath, err := module.UnescapePath(filepath.ToSlash(matches[1]))
if err != nil {
if r.env.Logf != nil {
r.env.Logf("decoding module cache path %q: %v", subdir, err)
}
r.env.logf("decoding module cache path %q: %v", subdir, err)
return directoryPackageInfo{
status: directoryScanned,
err: fmt.Errorf("decoding module cache path %q: %v", subdir, err),

View File

@@ -7,12 +7,17 @@ package imports
import (
"context"
"fmt"
"path"
"path/filepath"
"strings"
"sync"
"golang.org/x/mod/module"
"golang.org/x/tools/internal/gopathwalk"
"golang.org/x/tools/internal/stdlib"
)
// To find packages to import, the resolver needs to know about all of the
// To find packages to import, the resolver needs to know about all of
// the packages that could be imported. This includes packages that are
// already in modules that are in (1) the current module, (2) replace targets,
// and (3) packages in the module cache. Packages in (1) and (2) may change over
@@ -39,6 +44,8 @@ const (
exportsLoaded
)
// directoryPackageInfo holds (possibly incomplete) information about packages
// contained in a given directory.
type directoryPackageInfo struct {
// status indicates the extent to which this struct has been filled in.
status directoryPackageStatus
@@ -63,8 +70,11 @@ type directoryPackageInfo struct {
packageName string // the package name, as declared in the source.
// Set when status >= exportsLoaded.
exports []string
// TODO(rfindley): it's hard to see this, but exports depend implicitly on
// the default build context GOOS and GOARCH.
//
// We can make this explicit, and key exports by GOOS, GOARCH.
exports []stdlib.Symbol
}
// reachedStatus returns true when info has a status at least target and any error associated with
@@ -79,7 +89,7 @@ func (info *directoryPackageInfo) reachedStatus(target directoryPackageStatus) (
return true, nil
}
// dirInfoCache is a concurrency safe map for storing information about
// DirInfoCache is a concurrency-safe map for storing information about
// directories that may contain packages.
//
// The information in this cache is built incrementally. Entries are initialized in scan.
@@ -92,21 +102,26 @@ func (info *directoryPackageInfo) reachedStatus(target directoryPackageStatus) (
// The information in the cache is not expected to change for the cache's
// lifetime, so there is no protection against competing writes. Users should
// take care not to hold the cache across changes to the underlying files.
//
// TODO(suzmue): consider other concurrency strategies and data structures (RWLocks, sync.Map, etc)
type dirInfoCache struct {
type DirInfoCache struct {
mu sync.Mutex
// dirs stores information about packages in directories, keyed by absolute path.
dirs map[string]*directoryPackageInfo
listeners map[*int]cacheListener
}
func NewDirInfoCache() *DirInfoCache {
return &DirInfoCache{
dirs: make(map[string]*directoryPackageInfo),
listeners: make(map[*int]cacheListener),
}
}
type cacheListener func(directoryPackageInfo)
// ScanAndListen calls listener on all the items in the cache, and on anything
// newly added. The returned stop function waits for all in-flight callbacks to
// finish and blocks new ones.
func (d *dirInfoCache) ScanAndListen(ctx context.Context, listener cacheListener) func() {
func (d *DirInfoCache) ScanAndListen(ctx context.Context, listener cacheListener) func() {
ctx, cancel := context.WithCancel(ctx)
// Flushing out all the callbacks is tricky without knowing how many there
@@ -162,8 +177,10 @@ func (d *dirInfoCache) ScanAndListen(ctx context.Context, listener cacheListener
}
// Store stores the package info for dir.
func (d *dirInfoCache) Store(dir string, info directoryPackageInfo) {
func (d *DirInfoCache) Store(dir string, info directoryPackageInfo) {
d.mu.Lock()
// TODO(rfindley, golang/go#59216): should we overwrite an existing entry?
// That seems incorrect as the cache should be idempotent.
_, old := d.dirs[dir]
d.dirs[dir] = &info
var listeners []cacheListener
@@ -180,7 +197,7 @@ func (d *dirInfoCache) Store(dir string, info directoryPackageInfo) {
}
// Load returns a copy of the directoryPackageInfo for absolute directory dir.
func (d *dirInfoCache) Load(dir string) (directoryPackageInfo, bool) {
func (d *DirInfoCache) Load(dir string) (directoryPackageInfo, bool) {
d.mu.Lock()
defer d.mu.Unlock()
info, ok := d.dirs[dir]
@@ -191,7 +208,7 @@ func (d *dirInfoCache) Load(dir string) (directoryPackageInfo, bool) {
}
// Keys returns the keys currently present in d.
func (d *dirInfoCache) Keys() (keys []string) {
func (d *DirInfoCache) Keys() (keys []string) {
d.mu.Lock()
defer d.mu.Unlock()
for key := range d.dirs {
@@ -200,7 +217,7 @@ func (d *dirInfoCache) Keys() (keys []string) {
return keys
}
func (d *dirInfoCache) CachePackageName(info directoryPackageInfo) (string, error) {
func (d *DirInfoCache) CachePackageName(info directoryPackageInfo) (string, error) {
if loaded, err := info.reachedStatus(nameLoaded); loaded {
return info.packageName, err
}
@@ -213,7 +230,7 @@ func (d *dirInfoCache) CachePackageName(info directoryPackageInfo) (string, erro
return info.packageName, info.err
}
func (d *dirInfoCache) CacheExports(ctx context.Context, env *ProcessEnv, info directoryPackageInfo) (string, []string, error) {
func (d *DirInfoCache) CacheExports(ctx context.Context, env *ProcessEnv, info directoryPackageInfo) (string, []stdlib.Symbol, error) {
if reached, _ := info.reachedStatus(exportsLoaded); reached {
return info.packageName, info.exports, info.err
}
@@ -234,3 +251,81 @@ func (d *dirInfoCache) CacheExports(ctx context.Context, env *ProcessEnv, info d
d.Store(info.dir, info)
return info.packageName, info.exports, info.err
}
// ScanModuleCache walks the given directory, which must be a GOMODCACHE value,
// for directory package information, storing the results in cache.
func ScanModuleCache(dir string, cache *DirInfoCache, logf func(string, ...any)) {
// Note(rfindley): it's hard to see, but this function attempts to implement
// just the side effects on cache of calling PrimeCache with a ProcessEnv
// that has the given dir as its GOMODCACHE.
//
// Teasing out the control flow, we see that we can avoid any handling of
// vendor/ and can infer module info entirely from the path, simplifying the
// logic here.
root := gopathwalk.Root{
Path: filepath.Clean(dir),
Type: gopathwalk.RootModuleCache,
}
directoryInfo := func(root gopathwalk.Root, dir string) directoryPackageInfo {
// This is a copy of ModuleResolver.scanDirForPackage, trimmed down to
// logic that applies to a module cache directory.
subdir := ""
if dir != root.Path {
subdir = dir[len(root.Path)+len("/"):]
}
matches := modCacheRegexp.FindStringSubmatch(subdir)
if len(matches) == 0 {
return directoryPackageInfo{
status: directoryScanned,
err: fmt.Errorf("invalid module cache path: %v", subdir),
}
}
modPath, err := module.UnescapePath(filepath.ToSlash(matches[1]))
if err != nil {
if logf != nil {
logf("decoding module cache path %q: %v", subdir, err)
}
return directoryPackageInfo{
status: directoryScanned,
err: fmt.Errorf("decoding module cache path %q: %v", subdir, err),
}
}
importPath := path.Join(modPath, filepath.ToSlash(matches[3]))
index := strings.Index(dir, matches[1]+"@"+matches[2])
modDir := filepath.Join(dir[:index], matches[1]+"@"+matches[2])
modName := readModName(filepath.Join(modDir, "go.mod"))
return directoryPackageInfo{
status: directoryScanned,
dir: dir,
rootType: root.Type,
nonCanonicalImportPath: importPath,
moduleDir: modDir,
moduleName: modName,
}
}
add := func(root gopathwalk.Root, dir string) {
info := directoryInfo(root, dir)
cache.Store(info.dir, info)
}
skip := func(_ gopathwalk.Root, dir string) bool {
// Skip directories that have already been scanned.
//
// Note that gopathwalk only adds "package" directories, which must contain
// a .go file, and all such package directories in the module cache are
// immutable. So if we can load a dir, it can be skipped.
info, ok := cache.Load(dir)
if !ok {
return false
}
packageScanned, _ := info.reachedStatus(directoryScanned)
return packageScanned
}
gopathwalk.WalkSkip([]gopathwalk.Root{root}, add, skip, gopathwalk.Options{Logf: logf, ModulesEnabled: true})
}

View File

@@ -18,7 +18,7 @@ import (
// sortImports sorts runs of consecutive import lines in import blocks in f.
// It also removes duplicate imports when it is possible to do so without data loss.
//
// It may mutate the token.File.
// It may mutate the token.File and the ast.File.
func sortImports(localPrefix string, tokFile *token.File, f *ast.File) {
for i, d := range f.Decls {
d, ok := d.(*ast.GenDecl)

File diff suppressed because it is too large Load Diff

View File

@@ -5,10 +5,6 @@
// Package packagesinternal exposes internal-only fields from go/packages.
package packagesinternal
import (
"golang.org/x/tools/internal/gocommand"
)
var GetForTest = func(p interface{}) string { return "" }
var GetDepsErrors = func(p interface{}) []*PackageError { return nil }
@@ -18,10 +14,6 @@ type PackageError struct {
Err string // the error itself
}
var GetGoCmdRunner = func(config interface{}) *gocommand.Runner { return nil }
var SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {}
var TypecheckCgo int
var DepsErrors int // must be set as a LoadMode to call GetDepsErrors
var ForTest int // must be set as a LoadMode to call GetForTest

View File

@@ -21,7 +21,7 @@ import (
// export data.
type PkgDecoder struct {
// version is the file format version.
version uint32
version Version
// sync indicates whether the file uses sync markers.
sync bool
@@ -68,8 +68,6 @@ func (pr *PkgDecoder) SyncMarkers() bool { return pr.sync }
// NewPkgDecoder returns a PkgDecoder initialized to read the Unified
// IR export data from input. pkgPath is the package path for the
// compilation unit that produced the export data.
//
// TODO(mdempsky): Remove pkgPath parameter; unneeded since CL 391014.
func NewPkgDecoder(pkgPath, input string) PkgDecoder {
pr := PkgDecoder{
pkgPath: pkgPath,
@@ -80,14 +78,15 @@ func NewPkgDecoder(pkgPath, input string) PkgDecoder {
r := strings.NewReader(input)
assert(binary.Read(r, binary.LittleEndian, &pr.version) == nil)
var ver uint32
assert(binary.Read(r, binary.LittleEndian, &ver) == nil)
pr.version = Version(ver)
switch pr.version {
default:
panic(fmt.Errorf("unsupported version: %v", pr.version))
case 0:
// no flags
case 1:
if pr.version >= numVersions {
panic(fmt.Errorf("cannot decode %q, export data version %d is greater than maximum supported version %d", pkgPath, pr.version, numVersions-1))
}
if pr.version.Has(Flags) {
var flags uint32
assert(binary.Read(r, binary.LittleEndian, &flags) == nil)
pr.sync = flags&flagSyncMarkers != 0
@@ -102,7 +101,9 @@ func NewPkgDecoder(pkgPath, input string) PkgDecoder {
assert(err == nil)
pr.elemData = input[pos:]
assert(len(pr.elemData)-8 == int(pr.elemEnds[len(pr.elemEnds)-1]))
const fingerprintSize = 8
assert(len(pr.elemData)-fingerprintSize == int(pr.elemEnds[len(pr.elemEnds)-1]))
return pr
}
@@ -136,7 +137,7 @@ func (pr *PkgDecoder) AbsIdx(k RelocKind, idx Index) int {
absIdx += int(pr.elemEndsEnds[k-1])
}
if absIdx >= int(pr.elemEndsEnds[k]) {
errorf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds)
panicf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds)
}
return absIdx
}
@@ -193,9 +194,7 @@ func (pr *PkgDecoder) NewDecoderRaw(k RelocKind, idx Index) Decoder {
Idx: idx,
}
// TODO(mdempsky) r.data.Reset(...) after #44505 is resolved.
r.Data = *strings.NewReader(pr.DataIdx(k, idx))
r.Data.Reset(pr.DataIdx(k, idx))
r.Sync(SyncRelocs)
r.Relocs = make([]RelocEnt, r.Len())
for i := range r.Relocs {
@@ -244,7 +243,7 @@ type Decoder struct {
func (r *Decoder) checkErr(err error) {
if err != nil {
errorf("unexpected decoding error: %w", err)
panicf("unexpected decoding error: %w", err)
}
}
@@ -515,3 +514,6 @@ func (pr *PkgDecoder) PeekObj(idx Index) (string, string, CodeObj) {
return path, name, tag
}
// Version reports the version of the bitstream.
func (w *Decoder) Version() Version { return w.common.version }

View File

@@ -12,18 +12,15 @@ import (
"io"
"math/big"
"runtime"
"strings"
)
// currentVersion is the current version number.
//
// - v0: initial prototype
//
// - v1: adds the flags uint32 word
const currentVersion uint32 = 1
// A PkgEncoder provides methods for encoding a package's Unified IR
// export data.
type PkgEncoder struct {
// version of the bitstream.
version Version
// elems holds the bitstream for previously encoded elements.
elems [numRelocs][]string
@@ -47,8 +44,9 @@ func (pw *PkgEncoder) SyncMarkers() bool { return pw.syncFrames >= 0 }
// export data files, but can help diagnosing desync errors in
// higher-level Unified IR reader/writer code. If syncFrames is
// negative, then sync markers are omitted entirely.
func NewPkgEncoder(syncFrames int) PkgEncoder {
func NewPkgEncoder(version Version, syncFrames int) PkgEncoder {
return PkgEncoder{
version: version,
stringsIdx: make(map[string]Index),
syncFrames: syncFrames,
}
@@ -64,13 +62,15 @@ func (pw *PkgEncoder) DumpTo(out0 io.Writer) (fingerprint [8]byte) {
assert(binary.Write(out, binary.LittleEndian, x) == nil)
}
writeUint32(currentVersion)
writeUint32(uint32(pw.version))
var flags uint32
if pw.SyncMarkers() {
flags |= flagSyncMarkers
if pw.version.Has(Flags) {
var flags uint32
if pw.SyncMarkers() {
flags |= flagSyncMarkers
}
writeUint32(flags)
}
writeUint32(flags)
// Write elemEndsEnds.
var sum uint32
@@ -159,7 +159,7 @@ type Encoder struct {
// Flush finalizes the element's bitstream and returns its Index.
func (w *Encoder) Flush() Index {
var sb bytes.Buffer // TODO(mdempsky): strings.Builder after #44505 is resolved
var sb strings.Builder
// Backup the data so we write the relocations at the front.
var tmp bytes.Buffer
@@ -189,7 +189,7 @@ func (w *Encoder) Flush() Index {
func (w *Encoder) checkErr(err error) {
if err != nil {
errorf("unexpected encoding error: %v", err)
panicf("unexpected encoding error: %v", err)
}
}
@@ -320,8 +320,14 @@ func (w *Encoder) Code(c Code) {
// section (if not already present), and then writing a relocation
// into the element bitstream.
func (w *Encoder) String(s string) {
w.StringRef(w.p.StringIdx(s))
}
// StringRef writes a reference to the given index, which must be a
// previously encoded string value.
func (w *Encoder) StringRef(idx Index) {
w.Sync(SyncString)
w.Reloc(RelocString, w.p.StringIdx(s))
w.Reloc(RelocString, idx)
}
// Strings encodes and writes a variable-length slice of strings into
@@ -348,7 +354,7 @@ func (w *Encoder) Value(val constant.Value) {
func (w *Encoder) scalar(val constant.Value) {
switch v := constant.Val(val).(type) {
default:
errorf("unhandled %v (%v)", val, val.Kind())
panicf("unhandled %v (%v)", val, val.Kind())
case bool:
w.Code(ValBool)
w.Bool(v)
@@ -381,3 +387,6 @@ func (w *Encoder) bigFloat(v *big.Float) {
b := v.Append(nil, 'p', -1)
w.String(string(b)) // TODO: More efficient encoding.
}
// Version reports the version of the bitstream.
func (w *Encoder) Version() Version { return w.p.version }

View File

@@ -1,21 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.7
// +build !go1.7
// TODO(mdempsky): Remove after #44505 is resolved
package pkgbits
import "runtime"
func walkFrames(pcs []uintptr, visit frameVisitor) {
for _, pc := range pcs {
fn := runtime.FuncForPC(pc)
file, line := fn.FileLine(pc)
visit(file, line, fn.Name(), pc-fn.Entry())
}
}

View File

@@ -1,28 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.7
// +build go1.7
package pkgbits
import "runtime"
// walkFrames calls visit for each call frame represented by pcs.
//
// pcs should be a slice of PCs, as returned by runtime.Callers.
func walkFrames(pcs []uintptr, visit frameVisitor) {
if len(pcs) == 0 {
return
}
frames := runtime.CallersFrames(pcs)
for {
frame, more := frames.Next()
visit(frame.File, frame.Line, frame.Function, frame.PC-frame.Entry)
if !more {
return
}
}
}

View File

@@ -12,6 +12,6 @@ func assert(b bool) {
}
}
func errorf(format string, args ...interface{}) {
func panicf(format string, args ...any) {
panic(fmt.Errorf(format, args...))
}

View File

@@ -6,6 +6,7 @@ package pkgbits
import (
"fmt"
"runtime"
"strings"
)
@@ -23,6 +24,24 @@ func fmtFrames(pcs ...uintptr) []string {
type frameVisitor func(file string, line int, name string, offset uintptr)
// walkFrames calls visit for each call frame represented by pcs.
//
// pcs should be a slice of PCs, as returned by runtime.Callers.
func walkFrames(pcs []uintptr, visit frameVisitor) {
if len(pcs) == 0 {
return
}
frames := runtime.CallersFrames(pcs)
for {
frame, more := frames.Next()
visit(frame.File, frame.Line, frame.Function, frame.PC-frame.Entry)
if !more {
return
}
}
}
// SyncMarker is an enum type that represents markers that may be
// written to export data to ensure the reader and writer stay
// synchronized.
@@ -110,4 +129,8 @@ const (
SyncStmtsEnd
SyncLabel
SyncOptLabel
SyncMultiExpr
SyncRType
SyncConvRTTI
)

View File

@@ -74,11 +74,14 @@ func _() {
_ = x[SyncStmtsEnd-64]
_ = x[SyncLabel-65]
_ = x[SyncOptLabel-66]
_ = x[SyncMultiExpr-67]
_ = x[SyncRType-68]
_ = x[SyncConvRTTI-69]
}
const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabel"
const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabelMultiExprRTypeConvRTTI"
var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458}
var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458, 467, 472, 480}
func (i SyncMarker) String() string {
i -= 1

85
vendor/golang.org/x/tools/internal/pkgbits/version.go generated vendored Normal file
View File

@@ -0,0 +1,85 @@
// Copyright 2024 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 pkgbits
// Version indicates a version of a unified IR bitstream.
// Each Version indicates the addition, removal, or change of
// new data in the bitstream.
//
// These are serialized to disk and the interpretation remains fixed.
type Version uint32
const (
// V0: initial prototype.
//
// All data that is not assigned a Field is in version V0
// and has not been deprecated.
V0 Version = iota
// V1: adds the Flags uint32 word
V1
// V2: removes unused legacy fields and supports type parameters for aliases.
// - remove the legacy "has init" bool from the public root
// - remove obj's "derived func instance" bool
// - add a TypeParamNames field to ObjAlias
// - remove derived info "needed" bool
V2
numVersions = iota
)
// Field denotes a unit of data in the serialized unified IR bitstream.
// It is conceptually a like field in a structure.
//
// We only really need Fields when the data may or may not be present
// in a stream based on the Version of the bitstream.
//
// Unlike much of pkgbits, Fields are not serialized and
// can change values as needed.
type Field int
const (
// Flags in a uint32 in the header of a bitstream
// that is used to indicate whether optional features are enabled.
Flags Field = iota
// Deprecated: HasInit was a bool indicating whether a package
// has any init functions.
HasInit
// Deprecated: DerivedFuncInstance was a bool indicating
// whether an object was a function instance.
DerivedFuncInstance
// ObjAlias has a list of TypeParamNames.
AliasTypeParamNames
// Deprecated: DerivedInfoNeeded was a bool indicating
// whether a type was a derived type.
DerivedInfoNeeded
numFields = iota
)
// introduced is the version a field was added.
var introduced = [numFields]Version{
Flags: V1,
AliasTypeParamNames: V2,
}
// removed is the version a field was removed in or 0 for fields
// that have not yet been deprecated.
// (So removed[f]-1 is the last version it is included in.)
var removed = [numFields]Version{
HasInit: V2,
DerivedFuncInstance: V2,
DerivedInfoNeeded: V2,
}
// Has reports whether field f is present in a bitstream at version v.
func (v Version) Has(f Field) bool {
return introduced[f] <= v && (v < removed[f] || removed[f] == V0)
}

17431
vendor/golang.org/x/tools/internal/stdlib/manifest.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

97
vendor/golang.org/x/tools/internal/stdlib/stdlib.go generated vendored Normal file
View File

@@ -0,0 +1,97 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate go run generate.go
// Package stdlib provides a table of all exported symbols in the
// standard library, along with the version at which they first
// appeared.
package stdlib
import (
"fmt"
"strings"
)
type Symbol struct {
Name string
Kind Kind
Version Version // Go version that first included the symbol
}
// A Kind indicates the kind of a symbol:
// function, variable, constant, type, and so on.
type Kind int8
const (
Invalid Kind = iota // Example name:
Type // "Buffer"
Func // "Println"
Var // "EOF"
Const // "Pi"
Field // "Point.X"
Method // "(*Buffer).Grow"
)
func (kind Kind) String() string {
return [...]string{
Invalid: "invalid",
Type: "type",
Func: "func",
Var: "var",
Const: "const",
Field: "field",
Method: "method",
}[kind]
}
// A Version represents a version of Go of the form "go1.%d".
type Version int8
// String returns a version string of the form "go1.23", without allocating.
func (v Version) String() string { return versions[v] }
var versions [30]string // (increase constant as needed)
func init() {
for i := range versions {
versions[i] = fmt.Sprintf("go1.%d", i)
}
}
// HasPackage reports whether the specified package path is part of
// the standard library's public API.
func HasPackage(path string) bool {
_, ok := PackageSymbols[path]
return ok
}
// SplitField splits the field symbol name into type and field
// components. It must be called only on Field symbols.
//
// Example: "File.Package" -> ("File", "Package")
func (sym *Symbol) SplitField() (typename, name string) {
if sym.Kind != Field {
panic("not a field")
}
typename, name, _ = strings.Cut(sym.Name, ".")
return
}
// SplitMethod splits the method symbol name into pointer, receiver,
// and method components. It must be called only on Method symbols.
//
// Example: "(*Buffer).Grow" -> (true, "Buffer", "Grow")
func (sym *Symbol) SplitMethod() (ptr bool, recv, name string) {
if sym.Kind != Method {
panic("not a method")
}
recv, name, _ = strings.Cut(sym.Name, ".")
recv = recv[len("(") : len(recv)-len(")")]
ptr = recv[0] == '*'
if ptr {
recv = recv[len("*"):]
}
return
}

View File

@@ -1,59 +0,0 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// package tokeninternal provides access to some internal features of the token
// package.
package tokeninternal
import (
"go/token"
"sync"
"unsafe"
)
// GetLines returns the table of line-start offsets from a token.File.
func GetLines(file *token.File) []int {
// token.File has a Lines method on Go 1.21 and later.
if file, ok := (interface{})(file).(interface{ Lines() []int }); ok {
return file.Lines()
}
// This declaration must match that of token.File.
// This creates a risk of dependency skew.
// For now we check that the size of the two
// declarations is the same, on the (fragile) assumption
// that future changes would add fields.
type tokenFile119 struct {
_ string
_ int
_ int
mu sync.Mutex // we're not complete monsters
lines []int
_ []struct{}
}
type tokenFile118 struct {
_ *token.FileSet // deleted in go1.19
tokenFile119
}
type uP = unsafe.Pointer
switch unsafe.Sizeof(*file) {
case unsafe.Sizeof(tokenFile118{}):
var ptr *tokenFile118
*(*uP)(uP(&ptr)) = uP(file)
ptr.mu.Lock()
defer ptr.mu.Unlock()
return ptr.lines
case unsafe.Sizeof(tokenFile119{}):
var ptr *tokenFile119
*(*uP)(uP(&ptr)) = uP(file)
ptr.mu.Lock()
defer ptr.mu.Unlock()
return ptr.lines
default:
panic("unexpected token.File size")
}
}

View File

@@ -2,20 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package typeparams contains common utilities for writing tools that interact
// with generic Go code, as introduced with Go 1.18.
//
// Many of the types and functions in this package are proxies for the new APIs
// introduced in the standard library with Go 1.18. For example, the
// typeparams.Union type is an alias for go/types.Union, and the ForTypeSpec
// function returns the value of the go/ast.TypeSpec.TypeParams field. At Go
// versions older than 1.18 these helpers are implemented as stubs, allowing
// users of this package to write code that handles generic constructs inline,
// even if the Go version being used to compile does not support generics.
//
// Additionally, this package contains common utilities for working with the
// new generic constructs, to supplement the standard library APIs. Notably,
// the StructuralTerms API computes a minimal representation of the structural
// Package typeparams contains common utilities for writing tools that
// interact with generic Go code, as introduced with Go 1.18. It
// supplements the standard library APIs. Notably, the StructuralTerms
// API computes a minimal representation of the structural
// restrictions on a type parameter.
//
// An external version of these APIs is available in the
@@ -41,7 +31,7 @@ func UnpackIndexExpr(n ast.Node) (x ast.Expr, lbrack token.Pos, indices []ast.Ex
switch e := n.(type) {
case *ast.IndexExpr:
return e.X, e.Lbrack, []ast.Expr{e.Index}, e.Rbrack
case *IndexListExpr:
case *ast.IndexListExpr:
return e.X, e.Lbrack, e.Indices, e.Rbrack
}
return nil, token.NoPos, nil, token.NoPos
@@ -62,7 +52,7 @@ func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack toke
Rbrack: rbrack,
}
default:
return &IndexListExpr{
return &ast.IndexListExpr{
X: x,
Lbrack: lbrack,
Indices: indices,
@@ -71,49 +61,17 @@ func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack toke
}
}
// IsTypeParam reports whether t is a type parameter.
// IsTypeParam reports whether t is a type parameter (or an alias of one).
func IsTypeParam(t types.Type) bool {
_, ok := t.(*TypeParam)
_, ok := types.Unalias(t).(*types.TypeParam)
return ok
}
// OriginMethod returns the origin method associated with the method fn.
// For methods on a non-generic receiver base type, this is just
// fn. However, for methods with a generic receiver, OriginMethod returns the
// corresponding method in the method set of the origin type.
//
// As a special case, if fn is not a method (has no receiver), OriginMethod
// returns fn.
func OriginMethod(fn *types.Func) *types.Func {
recv := fn.Type().(*types.Signature).Recv()
if recv == nil {
return fn
}
base := recv.Type()
p, isPtr := base.(*types.Pointer)
if isPtr {
base = p.Elem()
}
named, isNamed := base.(*types.Named)
if !isNamed {
// Receiver is a *types.Interface.
return fn
}
if ForNamed(named).Len() == 0 {
// Receiver base has no type parameters, so we can avoid the lookup below.
return fn
}
orig := NamedTypeOrigin(named)
gfn, _, _ := types.LookupFieldOrMethod(orig, true, fn.Pkg(), fn.Name())
return gfn.(*types.Func)
}
// GenericAssignableTo is a generalization of types.AssignableTo that
// implements the following rule for uninstantiated generic types:
//
// If V and T are generic named types, then V is considered assignable to T if,
// for every possible instantation of V[A_1, ..., A_N], the instantiation
// for every possible instantiation of V[A_1, ..., A_N], the instantiation
// T[A_1, ..., A_N] is valid and V[A_1, ..., A_N] implements T[A_1, ..., A_N].
//
// If T has structural constraints, they must be satisfied by V.
@@ -132,7 +90,10 @@ func OriginMethod(fn *types.Func) *types.Func {
//
// In this case, GenericAssignableTo reports that instantiations of Container
// are assignable to the corresponding instantiation of Interface.
func GenericAssignableTo(ctxt *Context, V, T types.Type) bool {
func GenericAssignableTo(ctxt *types.Context, V, T types.Type) bool {
V = types.Unalias(V)
T = types.Unalias(T)
// If V and T are not both named, or do not have matching non-empty type
// parameter lists, fall back on types.AssignableTo.
@@ -142,9 +103,9 @@ func GenericAssignableTo(ctxt *Context, V, T types.Type) bool {
return types.AssignableTo(V, T)
}
vtparams := ForNamed(VN)
ttparams := ForNamed(TN)
if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || NamedTypeArgs(VN).Len() != 0 || NamedTypeArgs(TN).Len() != 0 {
vtparams := VN.TypeParams()
ttparams := TN.TypeParams()
if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || VN.TypeArgs().Len() != 0 || TN.TypeArgs().Len() != 0 {
return types.AssignableTo(V, T)
}
@@ -157,7 +118,7 @@ func GenericAssignableTo(ctxt *Context, V, T types.Type) bool {
// Minor optimization: ensure we share a context across the two
// instantiations below.
if ctxt == nil {
ctxt = NewContext()
ctxt = types.NewContext()
}
var targs []types.Type
@@ -165,12 +126,12 @@ func GenericAssignableTo(ctxt *Context, V, T types.Type) bool {
targs = append(targs, vtparams.At(i))
}
vinst, err := Instantiate(ctxt, V, targs, true)
vinst, err := types.Instantiate(ctxt, V, targs, true)
if err != nil {
panic("type parameters should satisfy their own constraints")
}
tinst, err := Instantiate(ctxt, T, targs, true)
tinst, err := types.Instantiate(ctxt, T, targs, true)
if err != nil {
return false
}

View File

@@ -5,6 +5,7 @@
package typeparams
import (
"fmt"
"go/types"
)
@@ -17,7 +18,7 @@ func CoreType(T types.Type) types.Type {
return U // for non-interface types,
}
terms, err := _NormalTerms(U)
terms, err := NormalTerms(U)
if len(terms) == 0 || err != nil {
// len(terms) -> empty type set of interface.
// err != nil => U is invalid, exceeds complexity bounds, or has an empty type set.
@@ -63,7 +64,7 @@ func CoreType(T types.Type) types.Type {
return ch
}
// _NormalTerms returns a slice of terms representing the normalized structural
// NormalTerms returns a slice of terms representing the normalized structural
// type restrictions of a type, if any.
//
// For all types other than *types.TypeParam, *types.Interface, and
@@ -81,42 +82,69 @@ func CoreType(T types.Type) types.Type {
// restrictions may be arbitrarily complex. For example, consider the
// following:
//
// type A interface{ ~string|~[]byte }
// type A interface{ ~string|~[]byte }
//
// type B interface{ int|string }
// type B interface{ int|string }
//
// type C interface { ~string|~int }
// type C interface { ~string|~int }
//
// type T[P interface{ A|B; C }] int
// type T[P interface{ A|B; C }] int
//
// In this example, the structural type restriction of P is ~string|int: A|B
// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int,
// which when intersected with C (~string|~int) yields ~string|int.
//
// _NormalTerms computes these expansions and reductions, producing a
// NormalTerms computes these expansions and reductions, producing a
// "normalized" form of the embeddings. A structural restriction is normalized
// if it is a single union containing no interface terms, and is minimal in the
// sense that removing any term changes the set of types satisfying the
// constraint. It is left as a proof for the reader that, modulo sorting, there
// is exactly one such normalized form.
//
// Because the minimal representation always takes this form, _NormalTerms
// Because the minimal representation always takes this form, NormalTerms
// returns a slice of tilde terms corresponding to the terms of the union in
// the normalized structural restriction. An error is returned if the type is
// invalid, exceeds complexity bounds, or has an empty type set. In the latter
// case, _NormalTerms returns ErrEmptyTypeSet.
// case, NormalTerms returns ErrEmptyTypeSet.
//
// _NormalTerms makes no guarantees about the order of terms, except that it
// NormalTerms makes no guarantees about the order of terms, except that it
// is deterministic.
func _NormalTerms(typ types.Type) ([]*Term, error) {
switch typ := typ.(type) {
case *TypeParam:
func NormalTerms(typ types.Type) ([]*types.Term, error) {
switch typ := typ.Underlying().(type) {
case *types.TypeParam:
return StructuralTerms(typ)
case *Union:
case *types.Union:
return UnionTermSet(typ)
case *types.Interface:
return InterfaceTermSet(typ)
default:
return []*Term{NewTerm(false, typ)}, nil
return []*types.Term{types.NewTerm(false, typ)}, nil
}
}
// Deref returns the type of the variable pointed to by t,
// if t's core type is a pointer; otherwise it returns t.
//
// Do not assume that Deref(T)==T implies T is not a pointer:
// consider "type T *T", for example.
//
// TODO(adonovan): ideally this would live in typesinternal, but that
// creates an import cycle. Move there when we melt this package down.
func Deref(t types.Type) types.Type {
if ptr, ok := CoreType(t).(*types.Pointer); ok {
return ptr.Elem()
}
return t
}
// MustDeref returns the type of the variable pointed to by t.
// It panics if t's core type is not a pointer.
//
// TODO(adonovan): ideally this would live in typesinternal, but that
// creates an import cycle. Move there when we melt this package down.
func MustDeref(t types.Type) types.Type {
if ptr, ok := CoreType(t).(*types.Pointer); ok {
return ptr.Elem()
}
panic(fmt.Sprintf("%v is not a pointer", t))
}

View File

@@ -1,12 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.18
// +build !go1.18
package typeparams
// Enabled reports whether type parameters are enabled in the current build
// environment.
const Enabled = false

View File

@@ -1,15 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
package typeparams
// Note: this constant is in a separate file as this is the only acceptable
// diff between the <1.18 API of this package and the 1.18 API.
// Enabled reports whether type parameters are enabled in the current build
// environment.
const Enabled = true

118
vendor/golang.org/x/tools/internal/typeparams/free.go generated vendored Normal file
View File

@@ -0,0 +1,118 @@
// Copyright 2024 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 typeparams
import (
"go/types"
)
// Free is a memoization of the set of free type parameters within a
// type. It makes a sequence of calls to [Free.Has] for overlapping
// types more efficient. The zero value is ready for use.
//
// NOTE: Adapted from go/types/infer.go. If it is later exported, factor.
type Free struct {
seen map[types.Type]bool
}
// Has reports whether the specified type has a free type parameter.
func (w *Free) Has(typ types.Type) (res bool) {
// detect cycles
if x, ok := w.seen[typ]; ok {
return x
}
if w.seen == nil {
w.seen = make(map[types.Type]bool)
}
w.seen[typ] = false
defer func() {
w.seen[typ] = res
}()
switch t := typ.(type) {
case nil, *types.Basic: // TODO(gri) should nil be handled here?
break
case *types.Alias:
return w.Has(types.Unalias(t))
case *types.Array:
return w.Has(t.Elem())
case *types.Slice:
return w.Has(t.Elem())
case *types.Struct:
for i, n := 0, t.NumFields(); i < n; i++ {
if w.Has(t.Field(i).Type()) {
return true
}
}
case *types.Pointer:
return w.Has(t.Elem())
case *types.Tuple:
n := t.Len()
for i := 0; i < n; i++ {
if w.Has(t.At(i).Type()) {
return true
}
}
case *types.Signature:
// t.tparams may not be nil if we are looking at a signature
// of a generic function type (or an interface method) that is
// part of the type we're testing. We don't care about these type
// parameters.
// Similarly, the receiver of a method may declare (rather than
// use) type parameters, we don't care about those either.
// Thus, we only need to look at the input and result parameters.
return w.Has(t.Params()) || w.Has(t.Results())
case *types.Interface:
for i, n := 0, t.NumMethods(); i < n; i++ {
if w.Has(t.Method(i).Type()) {
return true
}
}
terms, err := InterfaceTermSet(t)
if err != nil {
return false // ill typed
}
for _, term := range terms {
if w.Has(term.Type()) {
return true
}
}
case *types.Map:
return w.Has(t.Key()) || w.Has(t.Elem())
case *types.Chan:
return w.Has(t.Elem())
case *types.Named:
args := t.TypeArgs()
// TODO(taking): this does not match go/types/infer.go. Check with rfindley.
if params := t.TypeParams(); params.Len() > args.Len() {
return true
}
for i, n := 0, args.Len(); i < n; i++ {
if w.Has(args.At(i)) {
return true
}
}
return w.Has(t.Underlying()) // recurse for types local to parameterized functions
case *types.TypeParam:
return true
default:
panic(t) // unreachable
}
return false
}

View File

@@ -60,7 +60,7 @@ var ErrEmptyTypeSet = errors.New("empty type set")
//
// StructuralTerms makes no guarantees about the order of terms, except that it
// is deterministic.
func StructuralTerms(tparam *TypeParam) ([]*Term, error) {
func StructuralTerms(tparam *types.TypeParam) ([]*types.Term, error) {
constraint := tparam.Constraint()
if constraint == nil {
return nil, fmt.Errorf("%s has nil constraint", tparam)
@@ -78,7 +78,7 @@ func StructuralTerms(tparam *TypeParam) ([]*Term, error) {
//
// See the documentation of StructuralTerms for more information on
// normalization.
func InterfaceTermSet(iface *types.Interface) ([]*Term, error) {
func InterfaceTermSet(iface *types.Interface) ([]*types.Term, error) {
return computeTermSet(iface)
}
@@ -88,11 +88,11 @@ func InterfaceTermSet(iface *types.Interface) ([]*Term, error) {
//
// See the documentation of StructuralTerms for more information on
// normalization.
func UnionTermSet(union *Union) ([]*Term, error) {
func UnionTermSet(union *types.Union) ([]*types.Term, error) {
return computeTermSet(union)
}
func computeTermSet(typ types.Type) ([]*Term, error) {
func computeTermSet(typ types.Type) ([]*types.Term, error) {
tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0)
if err != nil {
return nil, err
@@ -103,9 +103,9 @@ func computeTermSet(typ types.Type) ([]*Term, error) {
if tset.terms.isAll() {
return nil, nil
}
var terms []*Term
var terms []*types.Term
for _, term := range tset.terms {
terms = append(terms, NewTerm(term.tilde, term.typ))
terms = append(terms, types.NewTerm(term.tilde, term.typ))
}
return terms, nil
}
@@ -162,7 +162,7 @@ func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth in
tset.terms = allTermlist
for i := 0; i < u.NumEmbeddeds(); i++ {
embedded := u.EmbeddedType(i)
if _, ok := embedded.Underlying().(*TypeParam); ok {
if _, ok := embedded.Underlying().(*types.TypeParam); ok {
return nil, fmt.Errorf("invalid embedded type %T", embedded)
}
tset2, err := computeTermSetInternal(embedded, seen, depth+1)
@@ -171,7 +171,7 @@ func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth in
}
tset.terms = tset.terms.intersect(tset2.terms)
}
case *Union:
case *types.Union:
// The term set of a union is the union of term sets of its terms.
tset.terms = nil
for i := 0; i < u.Len(); i++ {
@@ -184,7 +184,7 @@ func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth in
return nil, err
}
terms = tset2.terms
case *TypeParam, *Union:
case *types.TypeParam, *types.Union:
// A stand-alone type parameter or union is not permitted as union
// term.
return nil, fmt.Errorf("invalid union term %T", t)
@@ -199,7 +199,7 @@ func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth in
return nil, fmt.Errorf("exceeded max term count %d", maxTermCount)
}
}
case *TypeParam:
case *types.TypeParam:
panic("unreachable")
default:
// For all other types, the term set is just a single non-tilde term

View File

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

View File

@@ -1,197 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.18
// +build !go1.18
package typeparams
import (
"go/ast"
"go/token"
"go/types"
)
func unsupported() {
panic("type parameters are unsupported at this go version")
}
// IndexListExpr is a placeholder type, as type parameters are not supported at
// this Go version. Its methods panic on use.
type IndexListExpr struct {
ast.Expr
X ast.Expr // expression
Lbrack token.Pos // position of "["
Indices []ast.Expr // index expressions
Rbrack token.Pos // position of "]"
}
// ForTypeSpec returns an empty field list, as type parameters on not supported
// at this Go version.
func ForTypeSpec(*ast.TypeSpec) *ast.FieldList {
return nil
}
// ForFuncType returns an empty field list, as type parameters are not
// supported at this Go version.
func ForFuncType(*ast.FuncType) *ast.FieldList {
return nil
}
// TypeParam is a placeholder type, as type parameters are not supported at
// this Go version. Its methods panic on use.
type TypeParam struct{ types.Type }
func (*TypeParam) Index() int { unsupported(); return 0 }
func (*TypeParam) Constraint() types.Type { unsupported(); return nil }
func (*TypeParam) Obj() *types.TypeName { unsupported(); return nil }
// TypeParamList is a placeholder for an empty type parameter list.
type TypeParamList struct{}
func (*TypeParamList) Len() int { return 0 }
func (*TypeParamList) At(int) *TypeParam { unsupported(); return nil }
// TypeList is a placeholder for an empty type list.
type TypeList struct{}
func (*TypeList) Len() int { return 0 }
func (*TypeList) At(int) types.Type { unsupported(); return nil }
// NewTypeParam is unsupported at this Go version, and panics.
func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam {
unsupported()
return nil
}
// SetTypeParamConstraint is unsupported at this Go version, and panics.
func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) {
unsupported()
}
// NewSignatureType calls types.NewSignature, panicking if recvTypeParams or
// typeParams is non-empty.
func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature {
if len(recvTypeParams) != 0 || len(typeParams) != 0 {
panic("signatures cannot have type parameters at this Go version")
}
return types.NewSignature(recv, params, results, variadic)
}
// ForSignature returns an empty slice.
func ForSignature(*types.Signature) *TypeParamList {
return nil
}
// RecvTypeParams returns a nil slice.
func RecvTypeParams(sig *types.Signature) *TypeParamList {
return nil
}
// IsComparable returns false, as no interfaces are type-restricted at this Go
// version.
func IsComparable(*types.Interface) bool {
return false
}
// IsMethodSet returns true, as no interfaces are type-restricted at this Go
// version.
func IsMethodSet(*types.Interface) bool {
return true
}
// IsImplicit returns false, as no interfaces are implicit at this Go version.
func IsImplicit(*types.Interface) bool {
return false
}
// MarkImplicit does nothing, because this Go version does not have implicit
// interfaces.
func MarkImplicit(*types.Interface) {}
// ForNamed returns an empty type parameter list, as type parameters are not
// supported at this Go version.
func ForNamed(*types.Named) *TypeParamList {
return nil
}
// SetForNamed panics if tparams is non-empty.
func SetForNamed(_ *types.Named, tparams []*TypeParam) {
if len(tparams) > 0 {
unsupported()
}
}
// NamedTypeArgs returns nil.
func NamedTypeArgs(*types.Named) *TypeList {
return nil
}
// NamedTypeOrigin is the identity method at this Go version.
func NamedTypeOrigin(named *types.Named) types.Type {
return named
}
// Term holds information about a structural type restriction.
type Term struct {
tilde bool
typ types.Type
}
func (m *Term) Tilde() bool { return m.tilde }
func (m *Term) Type() types.Type { return m.typ }
func (m *Term) String() string {
pre := ""
if m.tilde {
pre = "~"
}
return pre + m.typ.String()
}
// NewTerm is unsupported at this Go version, and panics.
func NewTerm(tilde bool, typ types.Type) *Term {
return &Term{tilde, typ}
}
// Union is a placeholder type, as type parameters are not supported at this Go
// version. Its methods panic on use.
type Union struct{ types.Type }
func (*Union) Len() int { return 0 }
func (*Union) Term(i int) *Term { unsupported(); return nil }
// NewUnion is unsupported at this Go version, and panics.
func NewUnion(terms []*Term) *Union {
unsupported()
return nil
}
// InitInstanceInfo is a noop at this Go version.
func InitInstanceInfo(*types.Info) {}
// Instance is a placeholder type, as type parameters are not supported at this
// Go version.
type Instance struct {
TypeArgs *TypeList
Type types.Type
}
// GetInstances returns a nil map, as type parameters are not supported at this
// Go version.
func GetInstances(info *types.Info) map[*ast.Ident]Instance { return nil }
// Context is a placeholder type, as type parameters are not supported at
// this Go version.
type Context struct{}
// NewContext returns a placeholder Context instance.
func NewContext() *Context {
return &Context{}
}
// Instantiate is unsupported on this Go version, and panics.
func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) {
unsupported()
return nil, nil
}

View File

@@ -1,151 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
package typeparams
import (
"go/ast"
"go/types"
)
// IndexListExpr is an alias for ast.IndexListExpr.
type IndexListExpr = ast.IndexListExpr
// ForTypeSpec returns n.TypeParams.
func ForTypeSpec(n *ast.TypeSpec) *ast.FieldList {
if n == nil {
return nil
}
return n.TypeParams
}
// ForFuncType returns n.TypeParams.
func ForFuncType(n *ast.FuncType) *ast.FieldList {
if n == nil {
return nil
}
return n.TypeParams
}
// TypeParam is an alias for types.TypeParam
type TypeParam = types.TypeParam
// TypeParamList is an alias for types.TypeParamList
type TypeParamList = types.TypeParamList
// TypeList is an alias for types.TypeList
type TypeList = types.TypeList
// NewTypeParam calls types.NewTypeParam.
func NewTypeParam(name *types.TypeName, constraint types.Type) *TypeParam {
return types.NewTypeParam(name, constraint)
}
// SetTypeParamConstraint calls tparam.SetConstraint(constraint).
func SetTypeParamConstraint(tparam *TypeParam, constraint types.Type) {
tparam.SetConstraint(constraint)
}
// NewSignatureType calls types.NewSignatureType.
func NewSignatureType(recv *types.Var, recvTypeParams, typeParams []*TypeParam, params, results *types.Tuple, variadic bool) *types.Signature {
return types.NewSignatureType(recv, recvTypeParams, typeParams, params, results, variadic)
}
// ForSignature returns sig.TypeParams()
func ForSignature(sig *types.Signature) *TypeParamList {
return sig.TypeParams()
}
// RecvTypeParams returns sig.RecvTypeParams().
func RecvTypeParams(sig *types.Signature) *TypeParamList {
return sig.RecvTypeParams()
}
// IsComparable calls iface.IsComparable().
func IsComparable(iface *types.Interface) bool {
return iface.IsComparable()
}
// IsMethodSet calls iface.IsMethodSet().
func IsMethodSet(iface *types.Interface) bool {
return iface.IsMethodSet()
}
// IsImplicit calls iface.IsImplicit().
func IsImplicit(iface *types.Interface) bool {
return iface.IsImplicit()
}
// MarkImplicit calls iface.MarkImplicit().
func MarkImplicit(iface *types.Interface) {
iface.MarkImplicit()
}
// ForNamed extracts the (possibly empty) type parameter object list from
// named.
func ForNamed(named *types.Named) *TypeParamList {
return named.TypeParams()
}
// SetForNamed sets the type params tparams on n. Each tparam must be of
// dynamic type *types.TypeParam.
func SetForNamed(n *types.Named, tparams []*TypeParam) {
n.SetTypeParams(tparams)
}
// NamedTypeArgs returns named.TypeArgs().
func NamedTypeArgs(named *types.Named) *TypeList {
return named.TypeArgs()
}
// NamedTypeOrigin returns named.Orig().
func NamedTypeOrigin(named *types.Named) types.Type {
return named.Origin()
}
// Term is an alias for types.Term.
type Term = types.Term
// NewTerm calls types.NewTerm.
func NewTerm(tilde bool, typ types.Type) *Term {
return types.NewTerm(tilde, typ)
}
// Union is an alias for types.Union
type Union = types.Union
// NewUnion calls types.NewUnion.
func NewUnion(terms []*Term) *Union {
return types.NewUnion(terms)
}
// InitInstanceInfo initializes info to record information about type and
// function instances.
func InitInstanceInfo(info *types.Info) {
info.Instances = make(map[*ast.Ident]types.Instance)
}
// Instance is an alias for types.Instance.
type Instance = types.Instance
// GetInstances returns info.Instances.
func GetInstances(info *types.Info) map[*ast.Ident]Instance {
return info.Instances
}
// Context is an alias for types.Context.
type Context = types.Context
// NewContext calls types.NewContext.
func NewContext() *Context {
return types.NewContext()
}
// Instantiate calls types.Instantiate.
func Instantiate(ctxt *Context, typ types.Type, targs []types.Type, validate bool) (types.Type, error) {
return types.Instantiate(ctxt, typ, targs, validate)
}

View File

@@ -10,11 +10,10 @@ import "go/types"
// A term describes elementary type sets:
//
// ∅: (*term)(nil) == ∅ // set of no types (empty set)
// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
// T: &term{false, T} == {T} // set of type T
// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
//
// ∅: (*term)(nil) == ∅ // set of no types (empty set)
// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
// T: &term{false, T} == {T} // set of type T
// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
type term struct {
tilde bool // valid if typ != nil
typ types.Type

View File

@@ -0,0 +1,133 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package typesinternal
import (
"fmt"
"go/types"
"golang.org/x/tools/go/types/typeutil"
)
// ForEachElement calls f for type T and each type reachable from its
// type through reflection. It does this by recursively stripping off
// type constructors; in addition, for each named type N, the type *N
// is added to the result as it may have additional methods.
//
// The caller must provide an initially empty set used to de-duplicate
// identical types, potentially across multiple calls to ForEachElement.
// (Its final value holds all the elements seen, matching the arguments
// passed to f.)
//
// TODO(adonovan): share/harmonize with go/callgraph/rta.
func ForEachElement(rtypes *typeutil.Map, msets *typeutil.MethodSetCache, T types.Type, f func(types.Type)) {
var visit func(T types.Type, skip bool)
visit = func(T types.Type, skip bool) {
if !skip {
if seen, _ := rtypes.Set(T, true).(bool); seen {
return // de-dup
}
f(T) // notify caller of new element type
}
// Recursion over signatures of each method.
tmset := msets.MethodSet(T)
for i := 0; i < tmset.Len(); i++ {
sig := tmset.At(i).Type().(*types.Signature)
// It is tempting to call visit(sig, false)
// but, as noted in golang.org/cl/65450043,
// the Signature.Recv field is ignored by
// types.Identical and typeutil.Map, which
// is confusing at best.
//
// More importantly, the true signature rtype
// reachable from a method using reflection
// has no receiver but an extra ordinary parameter.
// For the Read method of io.Reader we want:
// func(Reader, []byte) (int, error)
// but here sig is:
// func([]byte) (int, error)
// with .Recv = Reader (though it is hard to
// notice because it doesn't affect Signature.String
// or types.Identical).
//
// TODO(adonovan): construct and visit the correct
// non-method signature with an extra parameter
// (though since unnamed func types have no methods
// there is essentially no actual demand for this).
//
// TODO(adonovan): document whether or not it is
// safe to skip non-exported methods (as RTA does).
visit(sig.Params(), true) // skip the Tuple
visit(sig.Results(), true) // skip the Tuple
}
switch T := T.(type) {
case *types.Alias:
visit(types.Unalias(T), skip) // emulates the pre-Alias behavior
case *types.Basic:
// nop
case *types.Interface:
// nop---handled by recursion over method set.
case *types.Pointer:
visit(T.Elem(), false)
case *types.Slice:
visit(T.Elem(), false)
case *types.Chan:
visit(T.Elem(), false)
case *types.Map:
visit(T.Key(), false)
visit(T.Elem(), false)
case *types.Signature:
if T.Recv() != nil {
panic(fmt.Sprintf("Signature %s has Recv %s", T, T.Recv()))
}
visit(T.Params(), true) // skip the Tuple
visit(T.Results(), true) // skip the Tuple
case *types.Named:
// A pointer-to-named type can be derived from a named
// type via reflection. It may have methods too.
visit(types.NewPointer(T), false)
// Consider 'type T struct{S}' where S has methods.
// Reflection provides no way to get from T to struct{S},
// only to S, so the method set of struct{S} is unwanted,
// so set 'skip' flag during recursion.
visit(T.Underlying(), true) // skip the unnamed type
case *types.Array:
visit(T.Elem(), false)
case *types.Struct:
for i, n := 0, T.NumFields(); i < n; i++ {
// TODO(adonovan): document whether or not
// it is safe to skip non-exported fields.
visit(T.Field(i).Type(), false)
}
case *types.Tuple:
for i, n := 0, T.Len(); i < n; i++ {
visit(T.At(i).Type(), false)
}
case *types.TypeParam, *types.Union:
// forEachReachable must not be called on parameterized types.
panic(T)
default:
panic(T)
}
}
visit(T, false)
}

View File

@@ -167,7 +167,7 @@ const (
UntypedNilUse
// WrongAssignCount occurs when the number of values on the right-hand side
// of an assignment or or initialization expression does not match the number
// of an assignment or initialization expression does not match the number
// of variables on the left-hand side.
//
// Example:
@@ -838,7 +838,7 @@ const (
// InvalidCap occurs when an argument to the cap built-in function is not of
// supported type.
//
// See https://golang.org/ref/spec#Lengthand_capacity for information on
// See https://golang.org/ref/spec#Length_and_capacity for information on
// which underlying types are supported as arguments to cap and len.
//
// Example:
@@ -859,7 +859,7 @@ const (
// InvalidCopy occurs when the arguments are not of slice type or do not
// have compatible type.
//
// See https://golang.org/ref/spec#Appendingand_copying_slices for more
// See https://golang.org/ref/spec#Appending_and_copying_slices for more
// information on the type requirements for the copy built-in.
//
// Example:
@@ -897,7 +897,7 @@ const (
// InvalidLen occurs when an argument to the len built-in function is not of
// supported type.
//
// See https://golang.org/ref/spec#Lengthand_capacity for information on
// See https://golang.org/ref/spec#Length_and_capacity for information on
// which underlying types are supported as arguments to cap and len.
//
// Example:
@@ -914,7 +914,7 @@ const (
// InvalidMake occurs when make is called with an unsupported type argument.
//
// See https://golang.org/ref/spec#Makingslices_maps_and_channels for
// See https://golang.org/ref/spec#Making_slices_maps_and_channels for
// information on the types that may be created using make.
//
// Example:
@@ -1449,10 +1449,10 @@ const (
NotAGenericType
// WrongTypeArgCount occurs when a type or function is instantiated with an
// incorrent number of type arguments, including when a generic type or
// incorrect number of type arguments, including when a generic type or
// function is used without instantiation.
//
// Errors inolving failed type inference are assigned other error codes.
// Errors involving failed type inference are assigned other error codes.
//
// Example:
// type T[p any] int

View File

@@ -0,0 +1,41 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package typesinternal
import (
"go/types"
)
// ReceiverNamed returns the named type (if any) associated with the
// type of recv, which may be of the form N or *N, or aliases thereof.
// It also reports whether a Pointer was present.
func ReceiverNamed(recv *types.Var) (isPtr bool, named *types.Named) {
t := recv.Type()
if ptr, ok := types.Unalias(t).(*types.Pointer); ok {
isPtr = true
t = ptr.Elem()
}
named, _ = types.Unalias(t).(*types.Named)
return
}
// Unpointer returns T given *T or an alias thereof.
// For all other types it is the identity function.
// It does not look at underlying types.
// The result may be an alias.
//
// Use this function to strip off the optional pointer on a receiver
// in a field or method selection, without losing the named type
// (which is needed to compute the method set).
//
// See also [typeparams.MustDeref], which removes one level of
// indirection from the type, regardless of named types (analogous to
// a LOAD instruction).
func Unpointer(t types.Type) types.Type {
if ptr, ok := types.Unalias(t).(*types.Pointer); ok {
return ptr.Elem()
}
return t
}

View File

@@ -0,0 +1,89 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package typesinternal
import (
"go/types"
"golang.org/x/tools/internal/stdlib"
"golang.org/x/tools/internal/versions"
)
// TooNewStdSymbols computes the set of package-level symbols
// exported by pkg that are not available at the specified version.
// The result maps each symbol to its minimum version.
//
// The pkg is allowed to contain type errors.
func TooNewStdSymbols(pkg *types.Package, version string) map[types.Object]string {
disallowed := make(map[types.Object]string)
// Pass 1: package-level symbols.
symbols := stdlib.PackageSymbols[pkg.Path()]
for _, sym := range symbols {
symver := sym.Version.String()
if versions.Before(version, symver) {
switch sym.Kind {
case stdlib.Func, stdlib.Var, stdlib.Const, stdlib.Type:
disallowed[pkg.Scope().Lookup(sym.Name)] = symver
}
}
}
// Pass 2: fields and methods.
//
// We allow fields and methods if their associated type is
// disallowed, as otherwise we would report false positives
// for compatibility shims. Consider:
//
// //go:build go1.22
// type T struct { F std.Real } // correct new API
//
// //go:build !go1.22
// type T struct { F fake } // shim
// type fake struct { ... }
// func (fake) M () {}
//
// These alternative declarations of T use either the std.Real
// type, introduced in go1.22, or a fake type, for the field
// F. (The fakery could be arbitrarily deep, involving more
// nested fields and methods than are shown here.) Clients
// that use the compatibility shim T will compile with any
// version of go, whether older or newer than go1.22, but only
// the newer version will use the std.Real implementation.
//
// Now consider a reference to method M in new(T).F.M() in a
// module that requires a minimum of go1.21. The analysis may
// occur using a version of Go higher than 1.21, selecting the
// first version of T, so the method M is Real.M. This would
// spuriously cause the analyzer to report a reference to a
// too-new symbol even though this expression compiles just
// fine (with the fake implementation) using go1.21.
for _, sym := range symbols {
symVersion := sym.Version.String()
if !versions.Before(version, symVersion) {
continue // allowed
}
var obj types.Object
switch sym.Kind {
case stdlib.Field:
typename, name := sym.SplitField()
if t := pkg.Scope().Lookup(typename); t != nil && disallowed[t] == "" {
obj, _, _ = types.LookupFieldOrMethod(t.Type(), false, pkg, name)
}
case stdlib.Method:
ptr, recvname, name := sym.SplitMethod()
if t := pkg.Scope().Lookup(recvname); t != nil && disallowed[t] == "" {
obj, _, _ = types.LookupFieldOrMethod(t.Type(), ptr, pkg, name)
}
}
if obj != nil {
disallowed[obj] = symVersion
}
}
return disallowed
}

View File

@@ -49,4 +49,17 @@ func ReadGo116ErrorData(err types.Error) (code ErrorCode, start, end token.Pos,
return ErrorCode(data[0]), token.Pos(data[1]), token.Pos(data[2]), true
}
var SetGoVersion = func(conf *types.Config, version string) bool { return false }
// NameRelativeTo returns a types.Qualifier that qualifies members of
// all packages other than pkg, using only the package name.
// (By contrast, [types.RelativeTo] uses the complete package path,
// which is often excessive.)
//
// If pkg is nil, it is equivalent to [*types.Package.Name].
func NameRelativeTo(pkg *types.Package) types.Qualifier {
return func(other *types.Package) string {
if pkg != nil && pkg == other {
return "" // same package; unqualified
}
return other.Name()
}
}

View File

@@ -1,19 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
package typesinternal
import (
"go/types"
)
func init() {
SetGoVersion = func(conf *types.Config, version string) bool {
conf.GoVersion = version
return true
}
}

View File

@@ -0,0 +1,13 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package versions
import "go/build/constraint"
// ConstraintGoVersion is constraint.GoVersion (if built with go1.21+).
// Otherwise nil.
//
// Deprecate once x/tools is after go1.21.
var ConstraintGoVersion func(x constraint.Expr) string

View File

@@ -0,0 +1,14 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.21
// +build go1.21
package versions
import "go/build/constraint"
func init() {
ConstraintGoVersion = constraint.GoVersion
}

View File

@@ -0,0 +1,43 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package versions
// This file contains predicates for working with file versions to
// decide when a tool should consider a language feature enabled.
// GoVersions that features in x/tools can be gated to.
const (
Go1_18 = "go1.18"
Go1_19 = "go1.19"
Go1_20 = "go1.20"
Go1_21 = "go1.21"
Go1_22 = "go1.22"
)
// Future is an invalid unknown Go version sometime in the future.
// Do not use directly with Compare.
const Future = ""
// AtLeast reports whether the file version v comes after a Go release.
//
// Use this predicate to enable a behavior once a certain Go release
// has happened (and stays enabled in the future).
func AtLeast(v, release string) bool {
if v == Future {
return true // an unknown future version is always after y.
}
return Compare(Lang(v), Lang(release)) >= 0
}
// Before reports whether the file version v is strictly before a Go release.
//
// Use this predicate to disable a behavior once a certain Go release
// has happened (and stays enabled in the future).
func Before(v, release string) bool {
if v == Future {
return false // an unknown future version happens after y.
}
return Compare(Lang(v), Lang(release)) < 0
}

172
vendor/golang.org/x/tools/internal/versions/gover.go generated vendored Normal file
View File

@@ -0,0 +1,172 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This is a fork of internal/gover for use by x/tools until
// go1.21 and earlier are no longer supported by x/tools.
package versions
import "strings"
// A gover is a parsed Go gover: major[.Minor[.Patch]][kind[pre]]
// The numbers are the original decimal strings to avoid integer overflows
// and since there is very little actual math. (Probably overflow doesn't matter in practice,
// but at the time this code was written, there was an existing test that used
// go1.99999999999, which does not fit in an int on 32-bit platforms.
// The "big decimal" representation avoids the problem entirely.)
type gover struct {
major string // decimal
minor string // decimal or ""
patch string // decimal or ""
kind string // "", "alpha", "beta", "rc"
pre string // decimal or ""
}
// compare returns -1, 0, or +1 depending on whether
// x < y, x == y, or x > y, interpreted as toolchain versions.
// The versions x and y must not begin with a "go" prefix: just "1.21" not "go1.21".
// Malformed versions compare less than well-formed versions and equal to each other.
// The language version "1.21" compares less than the release candidate and eventual releases "1.21rc1" and "1.21.0".
func compare(x, y string) int {
vx := parse(x)
vy := parse(y)
if c := cmpInt(vx.major, vy.major); c != 0 {
return c
}
if c := cmpInt(vx.minor, vy.minor); c != 0 {
return c
}
if c := cmpInt(vx.patch, vy.patch); c != 0 {
return c
}
if c := strings.Compare(vx.kind, vy.kind); c != 0 { // "" < alpha < beta < rc
return c
}
if c := cmpInt(vx.pre, vy.pre); c != 0 {
return c
}
return 0
}
// lang returns the Go language version. For example, lang("1.2.3") == "1.2".
func lang(x string) string {
v := parse(x)
if v.minor == "" || v.major == "1" && v.minor == "0" {
return v.major
}
return v.major + "." + v.minor
}
// isValid reports whether the version x is valid.
func isValid(x string) bool {
return parse(x) != gover{}
}
// parse parses the Go version string x into a version.
// It returns the zero version if x is malformed.
func parse(x string) gover {
var v gover
// Parse major version.
var ok bool
v.major, x, ok = cutInt(x)
if !ok {
return gover{}
}
if x == "" {
// Interpret "1" as "1.0.0".
v.minor = "0"
v.patch = "0"
return v
}
// Parse . before minor version.
if x[0] != '.' {
return gover{}
}
// Parse minor version.
v.minor, x, ok = cutInt(x[1:])
if !ok {
return gover{}
}
if x == "" {
// Patch missing is same as "0" for older versions.
// Starting in Go 1.21, patch missing is different from explicit .0.
if cmpInt(v.minor, "21") < 0 {
v.patch = "0"
}
return v
}
// Parse patch if present.
if x[0] == '.' {
v.patch, x, ok = cutInt(x[1:])
if !ok || x != "" {
// Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != "").
// Allowing them would be a bit confusing because we already have:
// 1.21 < 1.21rc1
// But a prerelease of a patch would have the opposite effect:
// 1.21.3rc1 < 1.21.3
// We've never needed them before, so let's not start now.
return gover{}
}
return v
}
// Parse prerelease.
i := 0
for i < len(x) && (x[i] < '0' || '9' < x[i]) {
if x[i] < 'a' || 'z' < x[i] {
return gover{}
}
i++
}
if i == 0 {
return gover{}
}
v.kind, x = x[:i], x[i:]
if x == "" {
return v
}
v.pre, x, ok = cutInt(x)
if !ok || x != "" {
return gover{}
}
return v
}
// cutInt scans the leading decimal number at the start of x to an integer
// and returns that value and the rest of the string.
func cutInt(x string) (n, rest string, ok bool) {
i := 0
for i < len(x) && '0' <= x[i] && x[i] <= '9' {
i++
}
if i == 0 || x[0] == '0' && i != 1 { // no digits or unnecessary leading zero
return "", "", false
}
return x[:i], x[i:], true
}
// cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers.
// (Copied from golang.org/x/mod/semver's compareInt.)
func cmpInt(x, y string) int {
if x == y {
return 0
}
if len(x) < len(y) {
return -1
}
if len(x) > len(y) {
return +1
}
if x < y {
return -1
} else {
return +1
}
}

38
vendor/golang.org/x/tools/internal/versions/types.go generated vendored Normal file
View File

@@ -0,0 +1,38 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package versions
import (
"go/ast"
"go/types"
)
// FileVersion returns a file's Go version.
// The reported version is an unknown Future version if a
// version cannot be determined.
func FileVersion(info *types.Info, file *ast.File) string {
// In tools built with Go >= 1.22, the Go version of a file
// follow a cascades of sources:
// 1) types.Info.FileVersion, which follows the cascade:
// 1.a) file version (ast.File.GoVersion),
// 1.b) the package version (types.Config.GoVersion), or
// 2) is some unknown Future version.
//
// File versions require a valid package version to be provided to types
// in Config.GoVersion. Config.GoVersion is either from the package's module
// or the toolchain (go run). This value should be provided by go/packages
// or unitchecker.Config.GoVersion.
if v := info.FileVersions[file]; IsValid(v) {
return v
}
// Note: we could instead return runtime.Version() [if valid].
// This would act as a max version on what a tool can support.
return Future
}
// InitFileVersions initializes info to record Go versions for Go files.
func InitFileVersions(info *types.Info) {
info.FileVersions = make(map[*ast.File]string)
}

View File

@@ -0,0 +1,57 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package versions
import (
"strings"
)
// Note: If we use build tags to use go/versions when go >=1.22,
// we run into go.dev/issue/53737. Under some operations users would see an
// import of "go/versions" even if they would not compile the file.
// For example, during `go get -u ./...` (go.dev/issue/64490) we do not try to include
// For this reason, this library just a clone of go/versions for the moment.
// Lang returns the Go language version for version x.
// If x is not a valid version, Lang returns the empty string.
// For example:
//
// Lang("go1.21rc2") = "go1.21"
// Lang("go1.21.2") = "go1.21"
// Lang("go1.21") = "go1.21"
// Lang("go1") = "go1"
// Lang("bad") = ""
// Lang("1.21") = ""
func Lang(x string) string {
v := lang(stripGo(x))
if v == "" {
return ""
}
return x[:2+len(v)] // "go"+v without allocation
}
// Compare returns -1, 0, or +1 depending on whether
// x < y, x == y, or x > y, interpreted as Go versions.
// The versions x and y must begin with a "go" prefix: "go1.21" not "1.21".
// Invalid versions, including the empty string, compare less than
// valid versions and equal to each other.
// The language version "go1.21" compares less than the
// release candidate and eventual releases "go1.21rc1" and "go1.21.0".
// Custom toolchain suffixes are ignored during comparison:
// "go1.21.0" and "go1.21.0-bigcorp" are equal.
func Compare(x, y string) int { return compare(stripGo(x), stripGo(y)) }
// IsValid reports whether the version x is valid.
func IsValid(x string) bool { return isValid(stripGo(x)) }
// stripGo converts from a "go1.21" version to a "1.21" version.
// If v does not start with "go", stripGo returns the empty string (a known invalid version).
func stripGo(v string) string {
v, _, _ = strings.Cut(v, "-") // strip -bigcorp suffix.
if len(v) < 2 || v[:2] != "go" {
return ""
}
return v[2:]
}