summaryrefslogtreecommitdiff
path: root/libgo/go/golang.org/x/tools
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2022-02-11 14:53:56 -0800
committerIan Lance Taylor <iant@golang.org>2022-02-11 15:01:19 -0800
commit8dc2499aa62f768c6395c9754b8cabc1ce25c494 (patch)
tree43d7fd2bbfd7ad8c9625a718a5e8718889351994 /libgo/go/golang.org/x/tools
parent9a56779dbc4e2d9c15be8d31e36f2f59be7331a8 (diff)
downloadgcc-8dc2499aa62f768c6395c9754b8cabc1ce25c494.tar.gz
libgo: update to Go1.18beta2
gotools/ * Makefile.am (go_cmd_cgo_files): Add ast_go118.go (check-go-tool): Copy golang.org/x/tools directories. * Makefile.in: Regenerate. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/384695
Diffstat (limited to 'libgo/go/golang.org/x/tools')
-rw-r--r--libgo/go/golang.org/x/tools/cover/profile.go11
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/internal/facts/imports.go33
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go37
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/composite/composite.go73
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/copylock/copylock.go75
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow.go6
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/ifaceassert/ifaceassert.go6
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/ifaceassert/parameterized.go112
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/nilfunc/nilfunc.go7
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/printf/printf.go76
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/printf/types.go213
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/shift/shift.go36
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/stdmethods/stdmethods.go6
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/stringintconv/string.go149
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/testinggoroutine/testinggoroutine.go42
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/tests/tests.go70
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go3
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go6
-rw-r--r--libgo/go/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go3
-rw-r--r--libgo/go/golang.org/x/tools/go/ast/astutil/enclosing.go20
-rw-r--r--libgo/go/golang.org/x/tools/go/ast/astutil/rewrite.go6
-rw-r--r--libgo/go/golang.org/x/tools/go/ast/inspector/typeof.go9
-rw-r--r--libgo/go/golang.org/x/tools/go/types/objectpath/objectpath.go153
-rw-r--r--libgo/go/golang.org/x/tools/go/types/typeutil/callee.go29
-rw-r--r--libgo/go/golang.org/x/tools/go/types/typeutil/map.go138
-rw-r--r--libgo/go/golang.org/x/tools/internal/lsp/fuzzy/input.go37
-rw-r--r--libgo/go/golang.org/x/tools/internal/lsp/fuzzy/matcher.go23
-rw-r--r--libgo/go/golang.org/x/tools/internal/lsp/fuzzy/symbol.go236
-rw-r--r--libgo/go/golang.org/x/tools/internal/typeparams/common.go79
-rw-r--r--libgo/go/golang.org/x/tools/internal/typeparams/enabled_go117.go12
-rw-r--r--libgo/go/golang.org/x/tools/internal/typeparams/enabled_go118.go15
-rw-r--r--libgo/go/golang.org/x/tools/internal/typeparams/normalize.go216
-rw-r--r--libgo/go/golang.org/x/tools/internal/typeparams/termlist.go172
-rw-r--r--libgo/go/golang.org/x/tools/internal/typeparams/typeparams_go117.go192
-rw-r--r--libgo/go/golang.org/x/tools/internal/typeparams/typeparams_go118.go146
-rw-r--r--libgo/go/golang.org/x/tools/internal/typeparams/typeterm.go170
-rw-r--r--libgo/go/golang.org/x/tools/txtar/archive.go140
37 files changed, 2507 insertions, 250 deletions
diff --git a/libgo/go/golang.org/x/tools/cover/profile.go b/libgo/go/golang.org/x/tools/cover/profile.go
index 57195774cea..47a9a541164 100644
--- a/libgo/go/golang.org/x/tools/cover/profile.go
+++ b/libgo/go/golang.org/x/tools/cover/profile.go
@@ -10,6 +10,7 @@ import (
"bufio"
"errors"
"fmt"
+ "io"
"math"
"os"
"sort"
@@ -45,14 +46,18 @@ func ParseProfiles(fileName string) ([]*Profile, error) {
return nil, err
}
defer pf.Close()
+ return ParseProfilesFromReader(pf)
+}
- files := make(map[string]*Profile)
- buf := bufio.NewReader(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
- s := bufio.NewScanner(buf)
+ files := make(map[string]*Profile)
+ s := bufio.NewScanner(rd)
mode := ""
for s.Scan() {
line := s.Text()
diff --git a/libgo/go/golang.org/x/tools/go/analysis/internal/facts/imports.go b/libgo/go/golang.org/x/tools/go/analysis/internal/facts/imports.go
index 34740f48e04..ade0cc6fab4 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/internal/facts/imports.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/internal/facts/imports.go
@@ -4,7 +4,11 @@
package facts
-import "go/types"
+import (
+ "go/types"
+
+ "golang.org/x/tools/internal/typeparams"
+)
// importMap computes the import map for a package by traversing the
// entire exported API each of its imports.
@@ -42,9 +46,20 @@ func importMap(imports []*types.Package) map[string]*types.Package {
// nop
case *types.Named:
if addObj(T.Obj()) {
+ // TODO(taking): Investigate why the Underlying type is not added here.
for i := 0; i < T.NumMethods(); i++ {
addObj(T.Method(i))
}
+ if tparams := typeparams.ForNamed(T); tparams != nil {
+ for i := 0; i < tparams.Len(); i++ {
+ addType(tparams.At(i))
+ }
+ }
+ if targs := typeparams.NamedTypeArgs(T); targs != nil {
+ for i := 0; i < targs.Len(); i++ {
+ addType(targs.At(i))
+ }
+ }
}
case *types.Pointer:
addType(T.Elem())
@@ -60,6 +75,11 @@ func importMap(imports []*types.Package) map[string]*types.Package {
case *types.Signature:
addType(T.Params())
addType(T.Results())
+ if tparams := typeparams.ForSignature(T); tparams != nil {
+ for i := 0; i < tparams.Len(); i++ {
+ addType(tparams.At(i))
+ }
+ }
case *types.Struct:
for i := 0; i < T.NumFields(); i++ {
addObj(T.Field(i))
@@ -72,6 +92,17 @@ func importMap(imports []*types.Package) map[string]*types.Package {
for i := 0; i < T.NumMethods(); i++ {
addObj(T.Method(i))
}
+ for i := 0; i < T.NumEmbeddeds(); i++ {
+ addType(T.EmbeddedType(i)) // walk Embedded for implicits
+ }
+ case *typeparams.Union:
+ for i := 0; i < T.Len(); i++ {
+ addType(T.Term(i).Type())
+ }
+ case *typeparams.TypeParam:
+ if addObj(T.Obj()) {
+ addType(T.Constraint())
+ }
}
}
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go b/libgo/go/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go
index 34c7a36234d..3997ac861df 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go
@@ -51,6 +51,11 @@ type asmArch struct {
bigEndian bool
stack string
lr bool
+ // retRegs is a list of registers for return value in register ABI (ABIInternal).
+ // For now, as we only check whether we write to any result, here we only need to
+ // include the first integer register and first floating-point register. Accessing
+ // any of them counts as writing to result.
+ retRegs []string
// calculated during initialization
sizes types.Sizes
intSize int
@@ -79,8 +84,8 @@ type asmVar struct {
var (
asmArch386 = asmArch{name: "386", bigEndian: false, stack: "SP", lr: false}
asmArchArm = asmArch{name: "arm", bigEndian: false, stack: "R13", lr: true}
- asmArchArm64 = asmArch{name: "arm64", bigEndian: false, stack: "RSP", lr: true}
- asmArchAmd64 = asmArch{name: "amd64", bigEndian: false, stack: "SP", lr: false}
+ asmArchArm64 = asmArch{name: "arm64", bigEndian: false, stack: "RSP", lr: true, retRegs: []string{"R0", "F0"}}
+ asmArchAmd64 = asmArch{name: "amd64", bigEndian: false, stack: "SP", lr: false, retRegs: []string{"AX", "X0"}}
asmArchMips = asmArch{name: "mips", bigEndian: true, stack: "R29", lr: true}
asmArchMipsLE = asmArch{name: "mipsle", bigEndian: false, stack: "R29", lr: true}
asmArchMips64 = asmArch{name: "mips64", bigEndian: true, stack: "R29", lr: true}
@@ -139,7 +144,7 @@ var (
asmSP = re(`[^+\-0-9](([0-9]+)\(([A-Z0-9]+)\))`)
asmOpcode = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`)
ppc64Suff = re(`([BHWD])(ZU|Z|U|BR)?$`)
- abiSuff = re(`^(.+)<ABI.+>$`)
+ abiSuff = re(`^(.+)<(ABI.+)>$`)
)
func run(pass *analysis.Pass) (interface{}, error) {
@@ -187,6 +192,7 @@ Files:
var (
fn *asmFunc
fnName string
+ abi string
localSize, argSize int
wroteSP bool
noframe bool
@@ -197,18 +203,22 @@ Files:
flushRet := func() {
if fn != nil && fn.vars["ret"] != nil && !haveRetArg && len(retLine) > 0 {
v := fn.vars["ret"]
+ resultStr := fmt.Sprintf("%d-byte ret+%d(FP)", v.size, v.off)
+ if abi == "ABIInternal" {
+ resultStr = "result register"
+ }
for _, line := range retLine {
- pass.Reportf(analysisutil.LineStart(tf, line), "[%s] %s: RET without writing to %d-byte ret+%d(FP)", arch, fnName, v.size, v.off)
+ pass.Reportf(analysisutil.LineStart(tf, line), "[%s] %s: RET without writing to %s", arch, fnName, resultStr)
}
}
retLine = nil
}
- trimABI := func(fnName string) string {
+ trimABI := func(fnName string) (string, string) {
m := abiSuff.FindStringSubmatch(fnName)
if m != nil {
- return m[1]
+ return m[1], m[2]
}
- return fnName
+ return fnName, ""
}
for lineno, line := range lines {
lineno++
@@ -275,11 +285,12 @@ Files:
// log.Printf("%s:%d: [%s] cannot check cross-package assembly function: %s is in package %s", fname, lineno, arch, fnName, pkgPath)
fn = nil
fnName = ""
+ abi = ""
continue
}
}
// Trim off optional ABI selector.
- fnName := trimABI(fnName)
+ fnName, abi = trimABI(fnName)
flag := m[3]
fn = knownFunc[fnName][arch]
if fn != nil {
@@ -307,6 +318,7 @@ Files:
flushRet()
fn = nil
fnName = ""
+ abi = ""
continue
}
@@ -337,6 +349,15 @@ Files:
haveRetArg = true
}
+ if abi == "ABIInternal" && !haveRetArg {
+ for _, reg := range archDef.retRegs {
+ if strings.Contains(line, reg) {
+ haveRetArg = true
+ break
+ }
+ }
+ }
+
for _, m := range asmSP.FindAllStringSubmatch(line, -1) {
if m[3] != archDef.stack || wroteSP || noframe {
continue
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/composite/composite.go b/libgo/go/golang.org/x/tools/go/analysis/passes/composite/composite.go
index 4c3ac6647f6..d3670aca97a 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/passes/composite/composite.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/composite/composite.go
@@ -14,6 +14,7 @@ import (
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
+ "golang.org/x/tools/internal/typeparams"
)
const Doc = `check for unkeyed composite literals
@@ -67,41 +68,61 @@ func run(pass *analysis.Pass) (interface{}, error) {
// skip whitelisted types
return
}
- under := typ.Underlying()
- for {
- ptr, ok := under.(*types.Pointer)
- if !ok {
- break
+ var structuralTypes []types.Type
+ switch typ := typ.(type) {
+ case *typeparams.TypeParam:
+ terms, err := typeparams.StructuralTerms(typ)
+ if err != nil {
+ return // invalid type
}
- under = ptr.Elem().Underlying()
- }
- if _, ok := under.(*types.Struct); !ok {
- // skip non-struct composite literals
- return
- }
- if isLocalType(pass, typ) {
- // allow unkeyed locally defined composite literal
- return
+ for _, term := range terms {
+ structuralTypes = append(structuralTypes, term.Type())
+ }
+ default:
+ structuralTypes = append(structuralTypes, typ)
}
+ for _, typ := range structuralTypes {
+ under := deref(typ.Underlying())
+ if _, ok := under.(*types.Struct); !ok {
+ // skip non-struct composite literals
+ continue
+ }
+ if isLocalType(pass, typ) {
+ // allow unkeyed locally defined composite literal
+ continue
+ }
- // check if the CompositeLit contains an unkeyed field
- allKeyValue := true
- for _, e := range cl.Elts {
- if _, ok := e.(*ast.KeyValueExpr); !ok {
- allKeyValue = false
- break
+ // check if the CompositeLit contains an unkeyed field
+ allKeyValue := true
+ for _, e := range cl.Elts {
+ if _, ok := e.(*ast.KeyValueExpr); !ok {
+ allKeyValue = false
+ break
+ }
}
- }
- if allKeyValue {
- // all the composite literal fields are keyed
+ if allKeyValue {
+ // all the composite literal fields are keyed
+ continue
+ }
+
+ pass.ReportRangef(cl, "%s composite literal uses unkeyed fields", typeName)
return
}
-
- pass.ReportRangef(cl, "%s composite literal uses unkeyed fields", typeName)
})
return nil, nil
}
+func deref(typ types.Type) types.Type {
+ for {
+ ptr, ok := typ.(*types.Pointer)
+ if !ok {
+ break
+ }
+ typ = ptr.Elem().Underlying()
+ }
+ return typ
+}
+
func isLocalType(pass *analysis.Pass, typ types.Type) bool {
switch x := typ.(type) {
case *types.Struct:
@@ -112,6 +133,8 @@ func isLocalType(pass *analysis.Pass, typ types.Type) bool {
case *types.Named:
// names in package foo are local to foo_test too
return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test")
+ case *typeparams.TypeParam:
+ return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test")
}
return false
}
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/copylock/copylock.go b/libgo/go/golang.org/x/tools/go/analysis/passes/copylock/copylock.go
index c4ebf785710..350dc4e0fec 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/passes/copylock/copylock.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/copylock/copylock.go
@@ -17,6 +17,7 @@ import (
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
+ "golang.org/x/tools/internal/typeparams"
)
const Doc = `check for locks erroneously passed by value
@@ -145,7 +146,7 @@ func checkCopyLocksCallExpr(pass *analysis.Pass, ce *ast.CallExpr) {
func checkCopyLocksFunc(pass *analysis.Pass, name string, recv *ast.FieldList, typ *ast.FuncType) {
if recv != nil && len(recv.List) > 0 {
expr := recv.List[0].Type
- if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil {
+ if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type, nil); path != nil {
pass.ReportRangef(expr, "%s passes lock by value: %v", name, path)
}
}
@@ -153,7 +154,7 @@ func checkCopyLocksFunc(pass *analysis.Pass, name string, recv *ast.FieldList, t
if typ.Params != nil {
for _, field := range typ.Params.List {
expr := field.Type
- if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil {
+ if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type, nil); path != nil {
pass.ReportRangef(expr, "%s passes lock by value: %v", name, path)
}
}
@@ -199,12 +200,12 @@ func checkCopyLocksRangeVar(pass *analysis.Pass, rtok token.Token, e ast.Expr) {
if typ == nil {
return
}
- if path := lockPath(pass.Pkg, typ); path != nil {
+ if path := lockPath(pass.Pkg, typ, nil); path != nil {
pass.Reportf(e.Pos(), "range var %s copies lock: %v", analysisutil.Format(pass.Fset, e), path)
}
}
-type typePath []types.Type
+type typePath []string
// String pretty-prints a typePath.
func (path typePath) String() string {
@@ -215,7 +216,7 @@ func (path typePath) String() string {
fmt.Fprint(&buf, " contains ")
}
// The human-readable path is in reverse order, outermost to innermost.
- fmt.Fprint(&buf, path[n-i-1].String())
+ fmt.Fprint(&buf, path[n-i-1])
}
return buf.String()
}
@@ -234,16 +235,57 @@ func lockPathRhs(pass *analysis.Pass, x ast.Expr) typePath {
return nil
}
}
- return lockPath(pass.Pkg, pass.TypesInfo.Types[x].Type)
+ return lockPath(pass.Pkg, pass.TypesInfo.Types[x].Type, nil)
}
// lockPath returns a typePath describing the location of a lock value
// contained in typ. If there is no contained lock, it returns nil.
-func lockPath(tpkg *types.Package, typ types.Type) typePath {
+//
+// The seenTParams map is used to short-circuit infinite recursion via type
+// parameters.
+func lockPath(tpkg *types.Package, typ types.Type, seenTParams map[*typeparams.TypeParam]bool) typePath {
if typ == nil {
return nil
}
+ if tpar, ok := typ.(*typeparams.TypeParam); ok {
+ if seenTParams == nil {
+ // Lazily allocate seenTParams, since the common case will not involve
+ // any type parameters.
+ seenTParams = make(map[*typeparams.TypeParam]bool)
+ }
+ if seenTParams[tpar] {
+ return nil
+ }
+ seenTParams[tpar] = true
+ terms, err := typeparams.StructuralTerms(tpar)
+ if err != nil {
+ return nil // invalid type
+ }
+ for _, term := range terms {
+ subpath := lockPath(tpkg, term.Type(), seenTParams)
+ if len(subpath) > 0 {
+ if term.Tilde() {
+ // Prepend a tilde to our lock path entry to clarify the resulting
+ // diagnostic message. Consider the following example:
+ //
+ // func _[Mutex interface{ ~sync.Mutex; M() }](m Mutex) {}
+ //
+ // Here the naive error message will be something like "passes lock
+ // by value: Mutex contains sync.Mutex". This is misleading because
+ // the local type parameter doesn't actually contain sync.Mutex,
+ // which lacks the M method.
+ //
+ // With tilde, it is clearer that the containment is via an
+ // approximation element.
+ subpath[len(subpath)-1] = "~" + subpath[len(subpath)-1]
+ }
+ return append(subpath, typ.String())
+ }
+ }
+ return nil
+ }
+
for {
atyp, ok := typ.Underlying().(*types.Array)
if !ok {
@@ -252,6 +294,17 @@ func lockPath(tpkg *types.Package, typ types.Type) typePath {
typ = atyp.Elem()
}
+ ttyp, ok := typ.Underlying().(*types.Tuple)
+ if ok {
+ for i := 0; i < ttyp.Len(); i++ {
+ subpath := lockPath(tpkg, ttyp.At(i).Type(), seenTParams)
+ if subpath != nil {
+ return append(subpath, typ.String())
+ }
+ }
+ return nil
+ }
+
// We're only interested in the case in which the underlying
// type is a struct. (Interfaces and pointers are safe to copy.)
styp, ok := typ.Underlying().(*types.Struct)
@@ -263,7 +316,7 @@ func lockPath(tpkg *types.Package, typ types.Type) typePath {
// is a sync.Locker, but a value is not. This differentiates
// embedded interfaces from embedded values.
if types.Implements(types.NewPointer(typ), lockerType) && !types.Implements(typ, lockerType) {
- return []types.Type{typ}
+ return []string{typ.String()}
}
// In go1.10, sync.noCopy did not implement Locker.
@@ -272,15 +325,15 @@ func lockPath(tpkg *types.Package, typ types.Type) typePath {
if named, ok := typ.(*types.Named); ok &&
named.Obj().Name() == "noCopy" &&
named.Obj().Pkg().Path() == "sync" {
- return []types.Type{typ}
+ return []string{typ.String()}
}
nfields := styp.NumFields()
for i := 0; i < nfields; i++ {
ftyp := styp.Field(i).Type()
- subpath := lockPath(tpkg, ftyp)
+ subpath := lockPath(tpkg, ftyp, seenTParams)
if subpath != nil {
- return append(subpath, typ)
+ return append(subpath, typ.String())
}
}
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow.go b/libgo/go/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow.go
index 51600ffc7eb..73746d6f04d 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow.go
@@ -187,7 +187,11 @@ func (c *CFGs) callMayReturn(call *ast.CallExpr) (r bool) {
return false // panic never returns
}
- // Is this a static call?
+ // Is this a static call? Also includes static functions
+ // parameterized by a type. Such functions may or may not
+ // return depending on the parameter type, but in some
+ // cases the answer is definite. We let ctrlflow figure
+ // that out.
fn := typeutil.StaticCallee(c.pass.TypesInfo, call)
if fn == nil {
return true // callee not statically known; be conservative
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/ifaceassert/ifaceassert.go b/libgo/go/golang.org/x/tools/go/analysis/passes/ifaceassert/ifaceassert.go
index fd2285332cc..30130f63ea6 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/passes/ifaceassert/ifaceassert.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/ifaceassert/ifaceassert.go
@@ -51,6 +51,12 @@ func assertableTo(v, t types.Type) *types.Func {
if V == nil || T == nil {
return nil
}
+
+ // Mitigations for interface comparisons and generics.
+ // TODO(https://github.com/golang/go/issues/50658): Support more precise conclusion.
+ if isParameterized(V) || isParameterized(T) {
+ return nil
+ }
if f, wrongType := types.MissingMethod(V, T, false); wrongType {
return f
}
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/ifaceassert/parameterized.go b/libgo/go/golang.org/x/tools/go/analysis/passes/ifaceassert/parameterized.go
new file mode 100644
index 00000000000..1285ecf1367
--- /dev/null
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/ifaceassert/parameterized.go
@@ -0,0 +1,112 @@
+// 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.
+package ifaceassert
+
+import (
+ "go/types"
+
+ "golang.org/x/tools/internal/typeparams"
+)
+
+// isParameterized reports whether typ contains any of the type parameters of tparams.
+//
+// NOTE: Adapted from go/types/infer.go. If that is exported in a future release remove this copy.
+func isParameterized(typ types.Type) bool {
+ w := tpWalker{
+ seen: make(map[types.Type]bool),
+ }
+ return w.isParameterized(typ)
+}
+
+type tpWalker struct {
+ seen map[types.Type]bool
+}
+
+func (w *tpWalker) isParameterized(typ types.Type) (res bool) {
+ // detect cycles
+ if x, ok := w.seen[typ]; ok {
+ return x
+ }
+ 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.Array:
+ return w.isParameterized(t.Elem())
+
+ case *types.Slice:
+ return w.isParameterized(t.Elem())
+
+ case *types.Struct:
+ for i, n := 0, t.NumFields(); i < n; i++ {
+ if w.isParameterized(t.Field(i).Type()) {
+ return true
+ }
+ }
+
+ case *types.Pointer:
+ return w.isParameterized(t.Elem())
+
+ case *types.Tuple:
+ n := t.Len()
+ for i := 0; i < n; i++ {
+ if w.isParameterized(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 then
+ // use) type parameters, we don't care about those either.
+ // Thus, we only need to look at the input and result parameters.
+ return w.isParameterized(t.Params()) || w.isParameterized(t.Results())
+
+ case *types.Interface:
+ for i, n := 0, t.NumMethods(); i < n; i++ {
+ if w.isParameterized(t.Method(i).Type()) {
+ return true
+ }
+ }
+ terms, err := typeparams.InterfaceTermSet(t)
+ if err != nil {
+ panic(err)
+ }
+ for _, term := range terms {
+ if w.isParameterized(term.Type()) {
+ return true
+ }
+ }
+
+ case *types.Map:
+ return w.isParameterized(t.Key()) || w.isParameterized(t.Elem())
+
+ case *types.Chan:
+ return w.isParameterized(t.Elem())
+
+ case *types.Named:
+ list := typeparams.NamedTypeArgs(t)
+ for i, n := 0, list.Len(); i < n; i++ {
+ if w.isParameterized(list.At(i)) {
+ return true
+ }
+ }
+
+ case *typeparams.TypeParam:
+ return true
+
+ default:
+ panic(t) // unreachable
+ }
+
+ return false
+}
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/nilfunc/nilfunc.go b/libgo/go/golang.org/x/tools/go/analysis/passes/nilfunc/nilfunc.go
index cd42c9897f2..e4c66df6d6d 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/passes/nilfunc/nilfunc.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/nilfunc/nilfunc.go
@@ -14,6 +14,7 @@ import (
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
+ "golang.org/x/tools/internal/typeparams"
)
const Doc = `check for useless comparisons between functions and nil
@@ -59,6 +60,12 @@ func run(pass *analysis.Pass) (interface{}, error) {
obj = pass.TypesInfo.Uses[v]
case *ast.SelectorExpr:
obj = pass.TypesInfo.Uses[v.Sel]
+ case *ast.IndexExpr, *typeparams.IndexListExpr:
+ // Check generic functions such as "f[T1,T2]".
+ x, _, _, _ := typeparams.UnpackIndexExpr(v)
+ if id, ok := x.(*ast.Ident); ok {
+ obj = pass.TypesInfo.Uses[id]
+ }
default:
return
}
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/printf/printf.go b/libgo/go/golang.org/x/tools/go/analysis/passes/printf/printf.go
index 822820f06e9..dee37d78ae0 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/passes/printf/printf.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/printf/printf.go
@@ -25,6 +25,7 @@ import (
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/go/types/typeutil"
+ "golang.org/x/tools/internal/typeparams"
)
func init() {
@@ -452,8 +453,15 @@ func stringConstantArg(pass *analysis.Pass, call *ast.CallExpr, idx int) (string
if idx >= len(call.Args) {
return "", false
}
- arg := call.Args[idx]
- lit := pass.TypesInfo.Types[arg].Value
+ return stringConstantExpr(pass, call.Args[idx])
+}
+
+// stringConstantExpr returns expression's string constant value.
+//
+// ("", false) is returned if expression isn't a string
+// constant.
+func stringConstantExpr(pass *analysis.Pass, expr ast.Expr) (string, bool) {
+ lit := pass.TypesInfo.Types[expr].Value
if lit != nil && lit.Kind() == constant.String {
return constant.StringVal(lit), true
}
@@ -490,7 +498,7 @@ func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func,
_, ok = isPrint[strings.ToLower(fn.Name())]
}
if ok {
- if fn.Name() == "Errorf" {
+ if fn.FullName() == "fmt.Errorf" {
kind = KindErrorf
} else if strings.HasSuffix(fn.Name(), "f") {
kind = KindPrintf
@@ -513,7 +521,12 @@ func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func,
func isFormatter(typ types.Type) bool {
// If the type is an interface, the value it holds might satisfy fmt.Formatter.
if _, ok := typ.Underlying().(*types.Interface); ok {
- return true
+ // Don't assume type parameters could be formatters. With the greater
+ // expressiveness of constraint interface syntax we expect more type safety
+ // when using type parameters.
+ if !typeparams.IsTypeParam(typ) {
+ return true
+ }
}
obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "Format")
fn, ok := obj.(*types.Func)
@@ -555,7 +568,7 @@ func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.F
format, idx := formatString(pass, call)
if idx < 0 {
if false {
- pass.Reportf(call.Lparen, "can't check non-constant format in call to %s", fn.Name())
+ pass.Reportf(call.Lparen, "can't check non-constant format in call to %s", fn.FullName())
}
return
}
@@ -563,7 +576,7 @@ func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.F
firstArg := idx + 1 // Arguments are immediately after format string.
if !strings.Contains(format, "%") {
if len(call.Args) > firstArg {
- pass.Reportf(call.Lparen, "%s call has arguments but no formatting directives", fn.Name())
+ pass.Reportf(call.Lparen, "%s call has arguments but no formatting directives", fn.FullName())
}
return
}
@@ -577,7 +590,7 @@ func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.F
if format[i] != '%' {
continue
}
- state := parsePrintfVerb(pass, call, fn.Name(), format[i:], firstArg, argNum)
+ state := parsePrintfVerb(pass, call, fn.FullName(), format[i:], firstArg, argNum)
if state == nil {
return
}
@@ -589,8 +602,9 @@ func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.F
anyIndex = true
}
if state.verb == 'w' {
- if kind != KindErrorf {
- pass.Reportf(call.Pos(), "%s call has error-wrapping directive %%w, which is only supported by Errorf", state.name)
+ switch kind {
+ case KindNone, KindPrint, KindPrintf:
+ pass.Reportf(call.Pos(), "%s does not support error-wrapping directive %%w", state.name)
return
}
if anyW {
@@ -621,7 +635,7 @@ func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.F
if maxArgNum != len(call.Args) {
expect := maxArgNum - firstArg
numArgs := len(call.Args) - firstArg
- pass.ReportRangef(call, "%s call needs %v but has %v", fn.Name(), count(expect, "arg"), count(numArgs, "arg"))
+ pass.ReportRangef(call, "%s call needs %v but has %v", fn.FullName(), count(expect, "arg"), count(numArgs, "arg"))
}
}
@@ -833,8 +847,9 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
}
// Could current arg implement fmt.Formatter?
+ // Skip check for the %w verb, which requires an error.
formatter := false
- if state.argNum < len(call.Args) {
+ if v.typ != argError && state.argNum < len(call.Args) {
if tv, ok := pass.TypesInfo.Types[call.Args[state.argNum]]; ok {
formatter = isFormatter(tv.Type)
}
@@ -870,8 +885,12 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
return
}
arg := call.Args[argNum]
- if !matchArgType(pass, argInt, nil, arg) {
- pass.ReportRangef(call, "%s format %s uses non-int %s as argument of *", state.name, state.format, analysisutil.Format(pass.Fset, arg))
+ if reason, ok := matchArgType(pass, argInt, arg); !ok {
+ details := ""
+ if reason != "" {
+ details = " (" + reason + ")"
+ }
+ pass.ReportRangef(call, "%s format %s uses non-int %s%s as argument of *", state.name, state.format, analysisutil.Format(pass.Fset, arg), details)
return false
}
}
@@ -888,12 +907,16 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
pass.ReportRangef(call, "%s format %s arg %s is a func value, not called", state.name, state.format, analysisutil.Format(pass.Fset, arg))
return false
}
- if !matchArgType(pass, v.typ, nil, arg) {
+ if reason, ok := matchArgType(pass, v.typ, arg); !ok {
typeString := ""
if typ := pass.TypesInfo.Types[arg].Type; typ != nil {
typeString = typ.String()
}
- pass.ReportRangef(call, "%s format %s has arg %s of wrong type %s", state.name, state.format, analysisutil.Format(pass.Fset, arg), typeString)
+ details := ""
+ if reason != "" {
+ details = " (" + reason + ")"
+ }
+ pass.ReportRangef(call, "%s format %s has arg %s of wrong type %s%s", state.name, state.format, analysisutil.Format(pass.Fset, arg), typeString, details)
return false
}
if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) {
@@ -949,7 +972,7 @@ func recursiveStringer(pass *analysis.Pass, e ast.Expr) (string, bool) {
}
if id, ok := e.(*ast.Ident); ok {
if pass.TypesInfo.Uses[id] == sig.Recv() {
- return method.Name(), true
+ return method.FullName(), true
}
}
return "", false
@@ -1044,40 +1067,39 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) {
if sel, ok := call.Args[0].(*ast.SelectorExpr); ok {
if x, ok := sel.X.(*ast.Ident); ok {
if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") {
- pass.ReportRangef(call, "%s does not take io.Writer but has first arg %s", fn.Name(), analysisutil.Format(pass.Fset, call.Args[0]))
+ pass.ReportRangef(call, "%s does not take io.Writer but has first arg %s", fn.FullName(), analysisutil.Format(pass.Fset, call.Args[0]))
}
}
}
}
arg := args[0]
- if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
- // Ignore trailing % character in lit.Value.
+ if s, ok := stringConstantExpr(pass, arg); ok {
+ // Ignore trailing % character
// The % in "abc 0.0%" couldn't be a formatting directive.
- s := strings.TrimSuffix(lit.Value, `%"`)
+ s = strings.TrimSuffix(s, "%")
if strings.Contains(s, "%") {
m := printFormatRE.FindStringSubmatch(s)
if m != nil {
- pass.ReportRangef(call, "%s call has possible formatting directive %s", fn.Name(), m[0])
+ pass.ReportRangef(call, "%s call has possible formatting directive %s", fn.FullName(), m[0])
}
}
}
if strings.HasSuffix(fn.Name(), "ln") {
// The last item, if a string, should not have a newline.
arg = args[len(args)-1]
- if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
- str, _ := strconv.Unquote(lit.Value)
- if strings.HasSuffix(str, "\n") {
- pass.ReportRangef(call, "%s arg list ends with redundant newline", fn.Name())
+ if s, ok := stringConstantExpr(pass, arg); ok {
+ if strings.HasSuffix(s, "\n") {
+ pass.ReportRangef(call, "%s arg list ends with redundant newline", fn.FullName())
}
}
}
for _, arg := range args {
if isFunctionValue(pass, arg) {
- pass.ReportRangef(call, "%s arg %s is a func value, not called", fn.Name(), analysisutil.Format(pass.Fset, arg))
+ pass.ReportRangef(call, "%s arg %s is a func value, not called", fn.FullName(), analysisutil.Format(pass.Fset, arg))
}
if methodName, ok := recursiveStringer(pass, arg); ok {
- pass.ReportRangef(call, "%s arg %s causes recursive call to %s method", fn.Name(), analysisutil.Format(pass.Fset, arg), methodName)
+ pass.ReportRangef(call, "%s arg %s causes recursive call to %s method", fn.FullName(), analysisutil.Format(pass.Fset, arg), methodName)
}
}
}
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/printf/types.go b/libgo/go/golang.org/x/tools/go/analysis/passes/printf/types.go
index 6a5fae44f46..270e917c809 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/passes/printf/types.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/printf/types.go
@@ -5,45 +5,60 @@
package printf
import (
+ "fmt"
"go/ast"
"go/types"
"golang.org/x/tools/go/analysis"
- "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
+ "golang.org/x/tools/internal/typeparams"
)
var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
-// matchArgType reports an error if printf verb t is not appropriate
-// for operand arg.
+// matchArgType reports an error if printf verb t is not appropriate for
+// operand arg.
//
-// typ is used only for recursive calls; external callers must supply nil.
-//
-// (Recursion arises from the compound types {map,chan,slice} which
-// may be printed with %d etc. if that is appropriate for their element
-// types.)
-func matchArgType(pass *analysis.Pass, t printfArgType, typ types.Type, arg ast.Expr) bool {
- return matchArgTypeInternal(pass, t, typ, arg, make(map[types.Type]bool))
-}
-
-// matchArgTypeInternal is the internal version of matchArgType. It carries a map
-// remembering what types are in progress so we don't recur when faced with recursive
-// types or mutually recursive types.
-func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type, arg ast.Expr, inProgress map[types.Type]bool) bool {
+// If arg is a type parameter, the verb t must be appropriate for every type in
+// the type parameter type set.
+func matchArgType(pass *analysis.Pass, t printfArgType, arg ast.Expr) (reason string, ok bool) {
// %v, %T accept any argument type.
if t == anyType {
- return true
+ return "", true
}
+
+ typ := pass.TypesInfo.Types[arg].Type
if typ == nil {
- // external call
- typ = pass.TypesInfo.Types[arg].Type
- if typ == nil {
- return true // probably a type check problem
- }
+ return "", true // probably a type check problem
}
+ m := &argMatcher{t: t, seen: make(map[types.Type]bool)}
+ ok = m.match(typ, true)
+ return m.reason, ok
+}
+
+// argMatcher recursively matches types against the printfArgType t.
+//
+// To short-circuit recursion, it keeps track of types that have already been
+// matched (or are in the process of being matched) via the seen map. Recursion
+// arises from the compound types {map,chan,slice} which may be printed with %d
+// etc. if that is appropriate for their element types, as well as from type
+// parameters, which are expanded to the constituents of their type set.
+//
+// The reason field may be set to report the cause of the mismatch.
+type argMatcher struct {
+ t printfArgType
+ seen map[types.Type]bool
+ reason string
+}
+
+// match checks if typ matches m's printf arg type. If topLevel is true, typ is
+// the actual type of the printf arg, for which special rules apply. As a
+// special case, top level type parameters pass topLevel=true when checking for
+// matches among the constituents of their type set, as type arguments will
+// replace the type parameter at compile time.
+func (m *argMatcher) match(typ types.Type, topLevel bool) bool {
// %w accepts only errors.
- if t == argError {
+ if m.t == argError {
return types.ConvertibleTo(typ, errorType)
}
@@ -51,65 +66,122 @@ func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type,
if isFormatter(typ) {
return true
}
+
// If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
- if t&argString != 0 && isConvertibleToString(pass, typ) {
+ if m.t&argString != 0 && isConvertibleToString(typ) {
+ return true
+ }
+
+ if typ, _ := typ.(*typeparams.TypeParam); typ != nil {
+ // Avoid infinite recursion through type parameters.
+ if m.seen[typ] {
+ return true
+ }
+ m.seen[typ] = true
+ terms, err := typeparams.StructuralTerms(typ)
+ if err != nil {
+ return true // invalid type (possibly an empty type set)
+ }
+
+ if len(terms) == 0 {
+ // No restrictions on the underlying of typ. Type parameters implementing
+ // error, fmt.Formatter, or fmt.Stringer were handled above, and %v and
+ // %T was handled in matchType. We're about to check restrictions the
+ // underlying; if the underlying type is unrestricted there must be an
+ // element of the type set that violates one of the arg type checks
+ // below, so we can safely return false here.
+
+ if m.t == anyType { // anyType must have already been handled.
+ panic("unexpected printfArgType")
+ }
+ return false
+ }
+
+ // Only report a reason if typ is the argument type, otherwise it won't
+ // make sense. Note that it is not sufficient to check if topLevel == here,
+ // as type parameters can have a type set consisting of other type
+ // parameters.
+ reportReason := len(m.seen) == 1
+
+ for _, term := range terms {
+ if !m.match(term.Type(), topLevel) {
+ if reportReason {
+ if term.Tilde() {
+ m.reason = fmt.Sprintf("contains ~%s", term.Type())
+ } else {
+ m.reason = fmt.Sprintf("contains %s", term.Type())
+ }
+ }
+ return false
+ }
+ }
return true
}
typ = typ.Underlying()
- if inProgress[typ] {
- // We're already looking at this type. The call that started it will take care of it.
+ if m.seen[typ] {
+ // We've already considered typ, or are in the process of considering it.
+ // In case we've already considered typ, it must have been valid (else we
+ // would have stopped matching). In case we're in the process of
+ // considering it, we must avoid infinite recursion.
+ //
+ // There are some pathological cases where returning true here is
+ // incorrect, for example `type R struct { F []R }`, but these are
+ // acceptable false negatives.
return true
}
- inProgress[typ] = true
+ m.seen[typ] = true
switch typ := typ.(type) {
case *types.Signature:
- return t == argPointer
+ return m.t == argPointer
case *types.Map:
- return t == argPointer ||
- // Recur: map[int]int matches %d.
- (matchArgTypeInternal(pass, t, typ.Key(), arg, inProgress) && matchArgTypeInternal(pass, t, typ.Elem(), arg, inProgress))
+ if m.t == argPointer {
+ return true
+ }
+ // Recur: map[int]int matches %d.
+ return m.match(typ.Key(), false) && m.match(typ.Elem(), false)
case *types.Chan:
- return t&argPointer != 0
+ return m.t&argPointer != 0
case *types.Array:
// Same as slice.
- if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
+ if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && m.t&argString != 0 {
return true // %s matches []byte
}
// Recur: []int matches %d.
- return matchArgTypeInternal(pass, t, typ.Elem(), arg, inProgress)
+ return m.match(typ.Elem(), false)
case *types.Slice:
// Same as array.
- if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
+ if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && m.t&argString != 0 {
return true // %s matches []byte
}
- if t == argPointer {
+ if m.t == argPointer {
return true // %p prints a slice's 0th element
}
// Recur: []int matches %d. But watch out for
// type T []T
// If the element is a pointer type (type T[]*T), it's handled fine by the Pointer case below.
- return matchArgTypeInternal(pass, t, typ.Elem(), arg, inProgress)
+ return m.match(typ.Elem(), false)
case *types.Pointer:
// Ugly, but dealing with an edge case: a known pointer to an invalid type,
// probably something from a failed import.
- if typ.Elem().String() == "invalid type" {
- if false {
- pass.Reportf(arg.Pos(), "printf argument %v is pointer to invalid or unknown type", analysisutil.Format(pass.Fset, arg))
- }
+ if typ.Elem() == types.Typ[types.Invalid] {
return true // special case
}
// If it's actually a pointer with %p, it prints as one.
- if t == argPointer {
+ if m.t == argPointer {
return true
}
+ if typeparams.IsTypeParam(typ.Elem()) {
+ return true // We don't know whether the logic below applies. Give up.
+ }
+
under := typ.Elem().Underlying()
switch under.(type) {
case *types.Struct: // see below
@@ -118,19 +190,31 @@ func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type,
case *types.Map: // see below
default:
// Check whether the rest can print pointers.
- return t&argPointer != 0
+ return m.t&argPointer != 0
}
- // If it's a top-level pointer to a struct, array, slice, or
+ // If it's a top-level pointer to a struct, array, slice, type param, or
// map, that's equivalent in our analysis to whether we can
// print the type being pointed to. Pointers in nested levels
// are not supported to minimize fmt running into loops.
- if len(inProgress) > 1 {
+ if !topLevel {
return false
}
- return matchArgTypeInternal(pass, t, under, arg, inProgress)
+ return m.match(under, false)
case *types.Struct:
- return matchStructArgType(pass, t, typ, arg, inProgress)
+ // report whether all the elements of the struct match the expected type. For
+ // instance, with "%d" all the elements must be printable with the "%d" format.
+ for i := 0; i < typ.NumFields(); i++ {
+ typf := typ.Field(i)
+ if !m.match(typf.Type(), false) {
+ return false
+ }
+ if m.t&argString != 0 && !typf.Exported() && isConvertibleToString(typf.Type()) {
+ // Issue #17798: unexported Stringer or error cannot be properly formatted.
+ return false
+ }
+ }
+ return true
case *types.Interface:
// There's little we can do.
@@ -142,7 +226,7 @@ func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type,
switch typ.Kind() {
case types.UntypedBool,
types.Bool:
- return t&argBool != 0
+ return m.t&argBool != 0
case types.UntypedInt,
types.Int,
@@ -156,35 +240,32 @@ func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type,
types.Uint32,
types.Uint64,
types.Uintptr:
- return t&argInt != 0
+ return m.t&argInt != 0
case types.UntypedFloat,
types.Float32,
types.Float64:
- return t&argFloat != 0
+ return m.t&argFloat != 0
case types.UntypedComplex,
types.Complex64,
types.Complex128:
- return t&argComplex != 0
+ return m.t&argComplex != 0
case types.UntypedString,
types.String:
- return t&argString != 0
+ return m.t&argString != 0
case types.UnsafePointer:
- return t&(argPointer|argInt) != 0
+ return m.t&(argPointer|argInt) != 0
case types.UntypedRune:
- return t&(argInt|argRune) != 0
+ return m.t&(argInt|argRune) != 0
case types.UntypedNil:
return false
case types.Invalid:
- if false {
- pass.Reportf(arg.Pos(), "printf argument %v has invalid or unknown type", analysisutil.Format(pass.Fset, arg))
- }
return true // Probably a type check problem.
}
panic("unreachable")
@@ -193,7 +274,7 @@ func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type,
return false
}
-func isConvertibleToString(pass *analysis.Pass, typ types.Type) bool {
+func isConvertibleToString(typ types.Type) bool {
if bt, ok := typ.(*types.Basic); ok && bt.Kind() == types.UntypedNil {
// We explicitly don't want untyped nil, which is
// convertible to both of the interfaces below, as it
@@ -228,19 +309,3 @@ func hasBasicType(pass *analysis.Pass, x ast.Expr, kind types.BasicKind) bool {
b, ok := t.(*types.Basic)
return ok && b.Kind() == kind
}
-
-// matchStructArgType reports whether all the elements of the struct match the expected
-// type. For instance, with "%d" all the elements must be printable with the "%d" format.
-func matchStructArgType(pass *analysis.Pass, t printfArgType, typ *types.Struct, arg ast.Expr, inProgress map[types.Type]bool) bool {
- for i := 0; i < typ.NumFields(); i++ {
- typf := typ.Field(i)
- if !matchArgTypeInternal(pass, t, typf.Type(), arg, inProgress) {
- return false
- }
- if t&argString != 0 && !typf.Exported() && isConvertibleToString(pass, typf.Type()) {
- // Issue #17798: unexported Stringer or error cannot be properly formatted.
- return false
- }
- }
- return true
-}
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/shift/shift.go b/libgo/go/golang.org/x/tools/go/analysis/passes/shift/shift.go
index 1f3df07ccd1..e968f27b403 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/passes/shift/shift.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/shift/shift.go
@@ -14,11 +14,14 @@ import (
"go/ast"
"go/constant"
"go/token"
+ "go/types"
+ "math"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
+ "golang.org/x/tools/internal/typeparams"
)
const Doc = "check for shifts that equal or exceed the width of the integer"
@@ -93,9 +96,36 @@ func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) {
if t == nil {
return
}
- size := 8 * pass.TypesSizes.Sizeof(t)
- if amt >= size {
+ var structuralTypes []types.Type
+ switch t := t.(type) {
+ case *typeparams.TypeParam:
+ terms, err := typeparams.StructuralTerms(t)
+ if err != nil {
+ return // invalid type
+ }
+ for _, term := range terms {
+ structuralTypes = append(structuralTypes, term.Type())
+ }
+ default:
+ structuralTypes = append(structuralTypes, t)
+ }
+ sizes := make(map[int64]struct{})
+ for _, t := range structuralTypes {
+ size := 8 * pass.TypesSizes.Sizeof(t)
+ sizes[size] = struct{}{}
+ }
+ minSize := int64(math.MaxInt64)
+ for size := range sizes {
+ if size < minSize {
+ minSize = size
+ }
+ }
+ if amt >= minSize {
ident := analysisutil.Format(pass.Fset, x)
- pass.ReportRangef(node, "%s (%d bits) too small for shift of %d", ident, size, amt)
+ qualifier := ""
+ if len(sizes) > 1 {
+ qualifier = "may be "
+ }
+ pass.ReportRangef(node, "%s (%s%d bits) too small for shift of %d", ident, qualifier, minSize, amt)
}
}
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/stdmethods/stdmethods.go b/libgo/go/golang.org/x/tools/go/analysis/passes/stdmethods/stdmethods.go
index 64a28ac0b97..cc9497179da 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/passes/stdmethods/stdmethods.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/stdmethods/stdmethods.go
@@ -61,7 +61,7 @@ var Analyzer = &analysis.Analyzer{
// we let it go. But if it does have a fmt.ScanState, then the
// rest has to match.
var canonicalMethods = map[string]struct{ args, results []string }{
- "As": {[]string{"interface{}"}, []string{"bool"}}, // errors.As
+ "As": {[]string{"any"}, []string{"bool"}}, // errors.As
// "Flush": {{}, {"error"}}, // http.Flusher and jpeg.writer conflict
"Format": {[]string{"=fmt.State", "rune"}, []string{}}, // fmt.Formatter
"GobDecode": {[]string{"[]byte"}, []string{"error"}}, // gob.GobDecoder
@@ -194,7 +194,9 @@ func matchParams(pass *analysis.Pass, expect []string, actual *types.Tuple, pref
func matchParamType(expect string, actual types.Type) bool {
expect = strings.TrimPrefix(expect, "=")
// Overkill but easy.
- return typeString(actual) == expect
+ t := typeString(actual)
+ return t == expect ||
+ (t == "any" || t == "interface{}") && (expect == "any" || expect == "interface{}")
}
var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/stringintconv/string.go b/libgo/go/golang.org/x/tools/go/analysis/passes/stringintconv/string.go
index 7a005901e84..e41de809de1 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/passes/stringintconv/string.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/stringintconv/string.go
@@ -10,10 +10,12 @@ import (
"fmt"
"go/ast"
"go/types"
+ "strings"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
+ "golang.org/x/tools/internal/typeparams"
)
const Doc = `check for string(int) conversions
@@ -36,6 +38,35 @@ var Analyzer = &analysis.Analyzer{
Run: run,
}
+// describe returns a string describing the type typ contained within the type
+// set of inType. If non-empty, inName is used as the name of inType (this is
+// necessary so that we can use alias type names that may not be reachable from
+// inType itself).
+func describe(typ, inType types.Type, inName string) string {
+ name := inName
+ if typ != inType {
+ name = typeName(typ)
+ }
+ if name == "" {
+ return ""
+ }
+
+ var parentheticals []string
+ if underName := typeName(typ.Underlying()); underName != "" && underName != name {
+ parentheticals = append(parentheticals, underName)
+ }
+
+ if typ != inType && inName != "" && inName != name {
+ parentheticals = append(parentheticals, "in "+inName)
+ }
+
+ if len(parentheticals) > 0 {
+ name += " (" + strings.Join(parentheticals, ", ") + ")"
+ }
+
+ return name
+}
+
func typeName(typ types.Type) string {
if v, _ := typ.(interface{ Name() string }); v != nil {
return v.Name()
@@ -54,6 +85,11 @@ func run(pass *analysis.Pass) (interface{}, error) {
inspect.Preorder(nodeFilter, func(n ast.Node) {
call := n.(*ast.CallExpr)
+ if len(call.Args) != 1 {
+ return
+ }
+ arg := call.Args[0]
+
// Retrieve target type name.
var tname *types.TypeName
switch fun := call.Fun.(type) {
@@ -65,62 +101,119 @@ func run(pass *analysis.Pass) (interface{}, error) {
if tname == nil {
return
}
- target := tname.Name()
- // Check that target type T in T(v) has an underlying type of string.
- T, _ := tname.Type().Underlying().(*types.Basic)
- if T == nil || T.Kind() != types.String {
- return
+ // In the conversion T(v) of a value v of type V to a target type T, we
+ // look for types T0 in the type set of T and V0 in the type set of V, such
+ // that V0->T0 is a problematic conversion. If T and V are not type
+ // parameters, this amounts to just checking if V->T is a problematic
+ // conversion.
+
+ // First, find a type T0 in T that has an underlying type of string.
+ T := tname.Type()
+ ttypes, err := structuralTypes(T)
+ if err != nil {
+ return // invalid type
}
- if s := T.Name(); target != s {
- target += " (" + s + ")"
+
+ var T0 types.Type // string type in the type set of T
+
+ for _, tt := range ttypes {
+ u, _ := tt.Underlying().(*types.Basic)
+ if u != nil && u.Kind() == types.String {
+ T0 = tt
+ break
+ }
}
- // Check that type V of v has an underlying integral type that is not byte or rune.
- if len(call.Args) != 1 {
+ if T0 == nil {
+ // No target types have an underlying type of string.
return
}
- v := call.Args[0]
- vtyp := pass.TypesInfo.TypeOf(v)
- V, _ := vtyp.Underlying().(*types.Basic)
- if V == nil || V.Info()&types.IsInteger == 0 {
- return
+
+ // Next, find a type V0 in V that has an underlying integral type that is
+ // not byte or rune.
+ V := pass.TypesInfo.TypeOf(arg)
+ vtypes, err := structuralTypes(V)
+ if err != nil {
+ return // invalid type
}
- switch V.Kind() {
- case types.Byte, types.Rune, types.UntypedRune:
- return
+
+ var V0 types.Type // integral type in the type set of V
+
+ for _, vt := range vtypes {
+ u, _ := vt.Underlying().(*types.Basic)
+ if u != nil && u.Info()&types.IsInteger != 0 {
+ switch u.Kind() {
+ case types.Byte, types.Rune, types.UntypedRune:
+ continue
+ }
+ V0 = vt
+ break
+ }
}
- // Retrieve source type name.
- source := typeName(vtyp)
- if source == "" {
+ if V0 == nil {
+ // No source types are non-byte or rune integer types.
return
}
- if s := V.Name(); source != s {
- source += " (" + s + ")"
+
+ convertibleToRune := true // if true, we can suggest a fix
+ for _, t := range vtypes {
+ if !types.ConvertibleTo(t, types.Typ[types.Rune]) {
+ convertibleToRune = false
+ break
+ }
+ }
+
+ target := describe(T0, T, tname.Name())
+ source := describe(V0, V, typeName(V))
+
+ if target == "" || source == "" {
+ return // something went wrong
}
+
diag := analysis.Diagnostic{
Pos: n.Pos(),
Message: fmt.Sprintf("conversion from %s to %s yields a string of one rune, not a string of digits (did you mean fmt.Sprint(x)?)", source, target),
- SuggestedFixes: []analysis.SuggestedFix{
+ }
+
+ if convertibleToRune {
+ diag.SuggestedFixes = []analysis.SuggestedFix{
{
Message: "Did you mean to convert a rune to a string?",
TextEdits: []analysis.TextEdit{
{
- Pos: v.Pos(),
- End: v.Pos(),
+ Pos: arg.Pos(),
+ End: arg.Pos(),
NewText: []byte("rune("),
},
{
- Pos: v.End(),
- End: v.End(),
+ Pos: arg.End(),
+ End: arg.End(),
NewText: []byte(")"),
},
},
},
- },
+ }
}
pass.Report(diag)
})
return nil, nil
}
+
+func structuralTypes(t types.Type) ([]types.Type, error) {
+ var structuralTypes []types.Type
+ switch t := t.(type) {
+ case *typeparams.TypeParam:
+ terms, err := typeparams.StructuralTerms(t)
+ if err != nil {
+ return nil, err
+ }
+ for _, term := range terms {
+ structuralTypes = append(structuralTypes, term.Type())
+ }
+ default:
+ structuralTypes = append(structuralTypes, t)
+ }
+ return structuralTypes, nil
+}
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/testinggoroutine/testinggoroutine.go b/libgo/go/golang.org/x/tools/go/analysis/passes/testinggoroutine/testinggoroutine.go
index d2b9a5640d9..7ea8f77e335 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/passes/testinggoroutine/testinggoroutine.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/testinggoroutine/testinggoroutine.go
@@ -11,6 +11,7 @@ import (
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
+ "golang.org/x/tools/internal/typeparams"
)
const Doc = `report calls to (*testing.T).Fatal from goroutines started by a test.
@@ -119,11 +120,44 @@ func typeIsTestingDotTOrB(expr ast.Expr) (string, bool) {
return varTypeName, ok
}
+// goStmtFunc returns the ast.Node of a call expression
+// that was invoked as a go statement. Currently, only
+// function literals declared in the same function, and
+// static calls within the same package are supported.
+func goStmtFun(goStmt *ast.GoStmt) ast.Node {
+ switch fun := goStmt.Call.Fun.(type) {
+ case *ast.IndexExpr, *typeparams.IndexListExpr:
+ x, _, _, _ := typeparams.UnpackIndexExpr(fun)
+ id, _ := x.(*ast.Ident)
+ if id == nil {
+ break
+ }
+ if id.Obj == nil {
+ break
+ }
+ if funDecl, ok := id.Obj.Decl.(ast.Node); ok {
+ return funDecl
+ }
+ case *ast.Ident:
+ // TODO(cuonglm): improve this once golang/go#48141 resolved.
+ if fun.Obj == nil {
+ break
+ }
+ if funDecl, ok := fun.Obj.Decl.(ast.Node); ok {
+ return funDecl
+ }
+ case *ast.FuncLit:
+ return goStmt.Call.Fun
+ }
+ return goStmt.Call
+}
+
// checkGoStmt traverses the goroutine and checks for the
// use of the forbidden *testing.(B, T) methods.
func checkGoStmt(pass *analysis.Pass, goStmt *ast.GoStmt) {
+ fn := goStmtFun(goStmt)
// Otherwise examine the goroutine to check for the forbidden methods.
- ast.Inspect(goStmt, func(n ast.Node) bool {
+ ast.Inspect(fn, func(n ast.Node) bool {
selExpr, ok := n.(*ast.SelectorExpr)
if !ok {
return true
@@ -147,7 +181,11 @@ func checkGoStmt(pass *analysis.Pass, goStmt *ast.GoStmt) {
return true
}
if typeName, ok := typeIsTestingDotTOrB(field.Type); ok {
- pass.ReportRangef(selExpr, "call to (*%s).%s from a non-test goroutine", typeName, selExpr.Sel)
+ var fnRange analysis.Range = goStmt
+ if _, ok := fn.(*ast.FuncLit); ok {
+ fnRange = selExpr
+ }
+ pass.ReportRangef(fnRange, "call to (*%s).%s from a non-test goroutine", typeName, selExpr.Sel)
}
return true
})
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/tests/tests.go b/libgo/go/golang.org/x/tools/go/analysis/passes/tests/tests.go
index 8232276186a..2c878824966 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/passes/tests/tests.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/tests/tests.go
@@ -8,12 +8,15 @@ package tests
import (
"go/ast"
+ "go/token"
"go/types"
+ "regexp"
"strings"
"unicode"
"unicode/utf8"
"golang.org/x/tools/go/analysis"
+ "golang.org/x/tools/internal/typeparams"
)
const Doc = `check for common mistaken usages of tests and examples
@@ -42,10 +45,10 @@ func run(pass *analysis.Pass) (interface{}, error) {
// Ignore non-functions or functions with receivers.
continue
}
-
switch {
case strings.HasPrefix(fn.Name.Name, "Example"):
- checkExample(pass, fn)
+ checkExampleName(pass, fn)
+ checkExampleOutput(pass, fn, f.Comments)
case strings.HasPrefix(fn.Name.Name, "Test"):
checkTest(pass, fn, "Test")
case strings.HasPrefix(fn.Name.Name, "Benchmark"):
@@ -108,7 +111,59 @@ func lookup(pkg *types.Package, name string) []types.Object {
return ret
}
-func checkExample(pass *analysis.Pass, fn *ast.FuncDecl) {
+// This pattern is taken from /go/src/go/doc/example.go
+var outputRe = regexp.MustCompile(`(?i)^[[:space:]]*(unordered )?output:`)
+
+type commentMetadata struct {
+ isOutput bool
+ pos token.Pos
+}
+
+func checkExampleOutput(pass *analysis.Pass, fn *ast.FuncDecl, fileComments []*ast.CommentGroup) {
+ commentsInExample := []commentMetadata{}
+ numOutputs := 0
+
+ // Find the comment blocks that are in the example. These comments are
+ // guaranteed to be in order of appearance.
+ for _, cg := range fileComments {
+ if cg.Pos() < fn.Pos() {
+ continue
+ } else if cg.End() > fn.End() {
+ break
+ }
+
+ isOutput := outputRe.MatchString(cg.Text())
+ if isOutput {
+ numOutputs++
+ }
+
+ commentsInExample = append(commentsInExample, commentMetadata{
+ isOutput: isOutput,
+ pos: cg.Pos(),
+ })
+ }
+
+ // Change message based on whether there are multiple output comment blocks.
+ msg := "output comment block must be the last comment block"
+ if numOutputs > 1 {
+ msg = "there can only be one output comment block per example"
+ }
+
+ for i, cg := range commentsInExample {
+ // Check for output comments that are not the last comment in the example.
+ isLast := (i == len(commentsInExample)-1)
+ if cg.isOutput && !isLast {
+ pass.Report(
+ analysis.Diagnostic{
+ Pos: cg.pos,
+ Message: msg,
+ },
+ )
+ }
+ }
+}
+
+func checkExampleName(pass *analysis.Pass, fn *ast.FuncDecl) {
fnName := fn.Name.Name
if params := fn.Type.Params; len(params.List) != 0 {
pass.Reportf(fn.Pos(), "%s should be niladic", fnName)
@@ -116,6 +171,9 @@ func checkExample(pass *analysis.Pass, fn *ast.FuncDecl) {
if results := fn.Type.Results; results != nil && len(results.List) != 0 {
pass.Reportf(fn.Pos(), "%s should return nothing", fnName)
}
+ if tparams := typeparams.ForFuncType(fn.Type); tparams != nil && len(tparams.List) > 0 {
+ pass.Reportf(fn.Pos(), "%s should not have type params", fnName)
+ }
if fnName == "Example" {
// Nothing more to do.
@@ -182,6 +240,12 @@ func checkTest(pass *analysis.Pass, fn *ast.FuncDecl, prefix string) {
return
}
+ if tparams := typeparams.ForFuncType(fn.Type); tparams != nil && len(tparams.List) > 0 {
+ // Note: cmd/go/internal/load also errors about TestXXX and BenchmarkXXX functions with type parameters.
+ // We have currently decided to also warn before compilation/package loading. This can help users in IDEs.
+ pass.Reportf(fn.Pos(), "%s has type parameters: it will not be run by go test as a %sXXX function", fn.Name.Name, prefix)
+ }
+
if !isTestSuffix(fn.Name.Name[len(prefix):]) {
pass.Reportf(fn.Pos(), "%s has malformed name: first letter after '%s' must not be lowercase", fn.Name.Name, prefix)
}
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go b/libgo/go/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go
index 92b37caff9f..5129048a076 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go
@@ -14,6 +14,7 @@ import (
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/go/types/typeutil"
+ "golang.org/x/tools/internal/typeparams"
)
const Doc = `report passing non-pointer or non-interface values to unmarshal
@@ -85,7 +86,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
t := pass.TypesInfo.Types[call.Args[argidx]].Type
switch t.Underlying().(type) {
- case *types.Pointer, *types.Interface:
+ case *types.Pointer, *types.Interface, *typeparams.TypeParam:
return
}
diff --git a/libgo/go/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go b/libgo/go/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go
index bececee7e93..06747ba72b9 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go
@@ -17,6 +17,7 @@ import (
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
+ "golang.org/x/tools/internal/typeparams"
)
// TODO(adonovan): make this analysis modular: export a mustUseResult
@@ -70,6 +71,11 @@ func run(pass *analysis.Pass) (interface{}, error) {
return // a conversion, not a call
}
+ x, _, _, _ := typeparams.UnpackIndexExpr(fun)
+ if x != nil {
+ fun = x // If this is generic function or method call, skip the instantiation arguments
+ }
+
selector, ok := fun.(*ast.SelectorExpr)
if !ok {
return // neither a method call nor a qualified ident
diff --git a/libgo/go/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go b/libgo/go/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go
index 323282136fb..393954a238b 100644
--- a/libgo/go/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go
+++ b/libgo/go/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go
@@ -51,6 +51,7 @@ import (
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/internal/analysisflags"
"golang.org/x/tools/go/analysis/internal/facts"
+ "golang.org/x/tools/internal/typeparams"
)
// A Config describes a compilation unit to be analyzed.
@@ -233,6 +234,8 @@ func run(fset *token.FileSet, cfg *Config, analyzers []*analysis.Analyzer) ([]re
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
}
+ typeparams.InitInstanceInfo(info)
+
pkg, err := tc.Check(cfg.ImportPath, fset, files, info)
if err != nil {
if cfg.SucceedOnTypecheckFailure {
diff --git a/libgo/go/golang.org/x/tools/go/ast/astutil/enclosing.go b/libgo/go/golang.org/x/tools/go/ast/astutil/enclosing.go
index 6b7052b892c..a5c6d6d4fa0 100644
--- a/libgo/go/golang.org/x/tools/go/ast/astutil/enclosing.go
+++ b/libgo/go/golang.org/x/tools/go/ast/astutil/enclosing.go
@@ -11,6 +11,8 @@ import (
"go/ast"
"go/token"
"sort"
+
+ "golang.org/x/tools/internal/typeparams"
)
// PathEnclosingInterval returns the node that encloses the source
@@ -294,8 +296,8 @@ func childrenOf(n ast.Node) []ast.Node {
case *ast.FieldList:
children = append(children,
- tok(n.Opening, len("(")),
- tok(n.Closing, len(")")))
+ tok(n.Opening, len("(")), // or len("[")
+ tok(n.Closing, len(")"))) // or len("]")
case *ast.File:
// TODO test: Doc
@@ -322,6 +324,9 @@ 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 {
+ children = append(children, tparams)
+ }
if n.Type.Params != nil {
children = append(children, n.Type.Params)
}
@@ -371,8 +376,13 @@ func childrenOf(n ast.Node) []ast.Node {
case *ast.IndexExpr:
children = append(children,
- tok(n.Lbrack, len("{")),
- tok(n.Rbrack, len("}")))
+ tok(n.Lbrack, len("[")),
+ tok(n.Rbrack, len("]")))
+
+ case *typeparams.IndexListExpr:
+ children = append(children,
+ tok(n.Lbrack, len("[")),
+ tok(n.Rbrack, len("]")))
case *ast.InterfaceType:
children = append(children,
@@ -581,6 +591,8 @@ func NodeDescription(n ast.Node) string {
return "decrement statement"
case *ast.IndexExpr:
return "index expression"
+ case *typeparams.IndexListExpr:
+ return "index list expression"
case *ast.InterfaceType:
return "interface type"
case *ast.KeyValueExpr:
diff --git a/libgo/go/golang.org/x/tools/go/ast/astutil/rewrite.go b/libgo/go/golang.org/x/tools/go/ast/astutil/rewrite.go
index cf72ea990bd..6d9ca23e2b0 100644
--- a/libgo/go/golang.org/x/tools/go/ast/astutil/rewrite.go
+++ b/libgo/go/golang.org/x/tools/go/ast/astutil/rewrite.go
@@ -9,6 +9,8 @@ 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,
@@ -251,6 +253,10 @@ 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:
+ a.apply(n, "X", nil, n.X)
+ a.applyList(n, "Indices")
+
case *ast.SliceExpr:
a.apply(n, "X", nil, n.X)
a.apply(n, "Low", nil, n.Low)
diff --git a/libgo/go/golang.org/x/tools/go/ast/inspector/typeof.go b/libgo/go/golang.org/x/tools/go/ast/inspector/typeof.go
index b6b00cf2e1e..11f4fc369a0 100644
--- a/libgo/go/golang.org/x/tools/go/ast/inspector/typeof.go
+++ b/libgo/go/golang.org/x/tools/go/ast/inspector/typeof.go
@@ -9,7 +9,11 @@ package inspector
// The initial map-based implementation was too slow;
// see https://go-review.googlesource.com/c/tools/+/135655/1/go/ast/inspector/inspector.go#196
-import "go/ast"
+import (
+ "go/ast"
+
+ "golang.org/x/tools/internal/typeparams"
+)
const (
nArrayType = iota
@@ -47,6 +51,7 @@ const (
nImportSpec
nIncDecStmt
nIndexExpr
+ nIndexListExpr
nInterfaceType
nKeyValueExpr
nLabeledStmt
@@ -164,6 +169,8 @@ func typeOf(n ast.Node) uint64 {
return 1 << nIncDecStmt
case *ast.IndexExpr:
return 1 << nIndexExpr
+ case *typeparams.IndexListExpr:
+ return 1 << nIndexListExpr
case *ast.InterfaceType:
return 1 << nInterfaceType
case *ast.KeyValueExpr:
diff --git a/libgo/go/golang.org/x/tools/go/types/objectpath/objectpath.go b/libgo/go/golang.org/x/tools/go/types/objectpath/objectpath.go
index cffd7acbee7..7e96fc234e5 100644
--- a/libgo/go/golang.org/x/tools/go/types/objectpath/objectpath.go
+++ b/libgo/go/golang.org/x/tools/go/types/objectpath/objectpath.go
@@ -23,10 +23,12 @@ package objectpath
import (
"fmt"
+ "go/types"
+ "sort"
"strconv"
"strings"
- "go/types"
+ "golang.org/x/tools/internal/typeparams"
)
// A Path is an opaque name that identifies a types.Object
@@ -57,12 +59,16 @@ type Path string
// - 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 [EKPRU].
-// - The OT operators are encoded as [AFMO];
+// - The TT operators are encoded as [EKPRUTC];
+// one of these (TypeParam) requires an integer operand,
+// which is encoded as a string of decimal digits.
+// - The TO operators are encoded as [AFMO];
// three of these (At,Field,Method) require an integer operand,
// which is encoded as a string of decimal digits.
// These indices are stable across different representations
// of the same package, even source and export data.
+// The indices used are implementation specific and may not correspond to
+// the argument to the go/types function.
//
// In the example below,
//
@@ -89,17 +95,19 @@ const (
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)
+ opElem = 'E' // .Elem() (Pointer, Slice, Array, Chan, Map)
+ opKey = 'K' // .Key() (Map)
+ opParams = 'P' // .Params() (Signature)
+ opResults = 'R' // .Results() (Signature)
+ opUnderlying = 'U' // .Underlying() (Named)
+ opTypeParam = 'T' // .TypeParams.At(i) (Named, Signature)
+ opConstraint = 'C' // .Constraint() (TypeParam)
// type->object operators
- opAt = 'A' // .At(i) (Tuple)
- opField = 'F' // .Field(i) (Struct)
- opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored)
- opObj = 'O' // .Obj() (Named)
+ 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)
)
// The For function returns the path to an object relative to its package,
@@ -190,10 +198,15 @@ func For(obj types.Object) (Path, error) {
// 3. Not a package-level object.
// Reject obviously non-viable cases.
switch obj := obj.(type) {
+ case *types.TypeName:
+ if _, ok := obj.Type().(*typeparams.TypeParam); !ok {
+ // With the exception of type parameters, only package-level type names
+ // have a path.
+ return "", fmt.Errorf("no path for %v", obj)
+ }
case *types.Const, // Only package-level constants have a path.
- *types.TypeName, // Only package-level types have a path.
- *types.Label, // Labels are function-local.
- *types.PkgName: // PkgNames are file-local.
+ *types.Label, // Labels are function-local.
+ *types.PkgName: // PkgNames are file-local.
return "", fmt.Errorf("no path for %v", obj)
case *types.Var:
@@ -245,6 +258,12 @@ func For(obj types.Object) (Path, error) {
return Path(r), nil
}
} else {
+ if named, _ := T.(*types.Named); named != nil {
+ if r := findTypeParam(obj, typeparams.ForNamed(named), path); r != nil {
+ // generic named type
+ return Path(r), nil
+ }
+ }
// defined (named) type
if r := find(obj, T.Underlying(), append(path, opUnderlying)); r != nil {
return Path(r), nil
@@ -270,8 +289,12 @@ func For(obj types.Object) (Path, error) {
// Inspect declared methods of defined types.
if T, ok := o.Type().(*types.Named); ok {
path = append(path, opType)
- for i := 0; i < T.NumMethods(); i++ {
- m := T.Method(i)
+ // Note that method index here is always with respect
+ // to canonical ordering of methods, regardless of how
+ // they appear in the underlying type.
+ canonical := canonicalize(T)
+ for i := 0; i < len(canonical); i++ {
+ m := canonical[i]
path2 := appendOpArg(path, opMethod, i)
if m == obj {
return Path(path2), nil // found declared method
@@ -313,6 +336,9 @@ func find(obj types.Object, T types.Type, path []byte) []byte {
}
return find(obj, T.Elem(), append(path, opElem))
case *types.Signature:
+ if r := findTypeParam(obj, typeparams.ForSignature(T), path); r != nil {
+ return r
+ }
if r := find(obj, T.Params(), append(path, opParams)); r != nil {
return r
}
@@ -353,10 +379,30 @@ func find(obj types.Object, T types.Type, path []byte) []byte {
}
}
return nil
+ case *typeparams.TypeParam:
+ name := T.Obj()
+ if name == obj {
+ return append(path, opObj)
+ }
+ if r := find(obj, T.Constraint(), append(path, opConstraint)); r != nil {
+ return r
+ }
+ return nil
}
panic(T)
}
+func findTypeParam(obj types.Object, list *typeparams.TypeParamList, path []byte) []byte {
+ for i := 0; i < list.Len(); i++ {
+ tparam := list.At(i)
+ path2 := appendOpArg(path, opTypeParam, i)
+ if r := find(obj, tparam, path2); r != nil {
+ return r
+ }
+ }
+ return nil
+}
+
// Object returns the object denoted by path p within the package pkg.
func Object(pkg *types.Package, p Path) (types.Object, error) {
if p == "" {
@@ -381,10 +427,13 @@ func Object(pkg *types.Package, p Path) (types.Object, error) {
type hasElem interface {
Elem() types.Type
}
- // abstraction of *types.{Interface,Named}
- type hasMethods interface {
- Method(int) *types.Func
- NumMethods() int
+ // abstraction of *types.{Named,Signature}
+ type hasTypeParams interface {
+ TypeParams() *typeparams.TypeParamList
+ }
+ // abstraction of *types.{Named,TypeParam}
+ type hasObj interface {
+ Obj() *types.TypeName
}
// The loop state is the pair (t, obj),
@@ -401,7 +450,7 @@ func Object(pkg *types.Package, p Path) (types.Object, error) {
// Codes [AFM] have an integer operand.
var index int
switch code {
- case opAt, opField, opMethod:
+ case opAt, opField, opMethod, opTypeParam:
rest := strings.TrimLeft(suffix, "0123456789")
numerals := suffix[:len(suffix)-len(rest)]
suffix = rest
@@ -466,14 +515,32 @@ func Object(pkg *types.Package, p Path) (types.Object, error) {
case opUnderlying:
named, ok := t.(*types.Named)
if !ok {
- return nil, fmt.Errorf("cannot apply %q to %s (got %s, want named)", code, t, t)
+ return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named)", code, t, t)
}
t = named.Underlying()
+ case opTypeParam:
+ hasTypeParams, ok := t.(hasTypeParams) // Named, Signature
+ if !ok {
+ return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or signature)", code, t, t)
+ }
+ tparams := hasTypeParams.TypeParams()
+ if n := tparams.Len(); index >= n {
+ return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
+ }
+ t = tparams.At(index)
+
+ case opConstraint:
+ tparam, ok := t.(*typeparams.TypeParam)
+ if !ok {
+ return nil, fmt.Errorf("cannot apply %q to %s (got %T, want type parameter)", code, t, t)
+ }
+ t = tparam.Constraint()
+
case opAt:
tuple, ok := t.(*types.Tuple)
if !ok {
- return nil, fmt.Errorf("cannot apply %q to %s (got %s, want tuple)", code, t, t)
+ 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)
@@ -495,20 +562,21 @@ func Object(pkg *types.Package, p Path) (types.Object, error) {
case opMethod:
hasMethods, ok := t.(hasMethods) // Interface or Named
if !ok {
- return nil, fmt.Errorf("cannot apply %q to %s (got %s, want interface or named)", code, t, t)
+ return nil, fmt.Errorf("cannot apply %q to %s (got %T, want interface or named)", code, t, t)
}
- if n := hasMethods.NumMethods(); index >= n {
+ canonical := canonicalize(hasMethods)
+ if n := len(canonical); index >= n {
return nil, fmt.Errorf("method index %d out of range [0-%d)", index, n)
}
- obj = hasMethods.Method(index)
+ obj = canonical[index]
t = nil
case opObj:
- named, ok := t.(*types.Named)
+ hasObj, ok := t.(hasObj)
if !ok {
- return nil, fmt.Errorf("cannot apply %q to %s (got %s, want named)", code, t, t)
+ return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or type param)", code, t, t)
}
- obj = named.Obj()
+ obj = hasObj.Obj()
t = nil
default:
@@ -522,3 +590,28 @@ func Object(pkg *types.Package, p Path) (types.Object, error) {
return obj, nil // success
}
+
+// hasMethods is an abstraction of *types.{Interface,Named}. This is pulled up
+// because it is used by methodOrdering, which is in turn used by both encoding
+// and decoding.
+type hasMethods interface {
+ Method(int) *types.Func
+ NumMethods() int
+}
+
+// canonicalize returns a canonical order for the methods in a hasMethod.
+func canonicalize(hm hasMethods) []*types.Func {
+ count := hm.NumMethods()
+ if count <= 0 {
+ return nil
+ }
+ canon := make([]*types.Func, count)
+ for i := 0; i < count; i++ {
+ canon[i] = hm.Method(i)
+ }
+ less := func(i, j int) bool {
+ return canon[i].Id() < canon[j].Id()
+ }
+ sort.Slice(canon, less)
+ return canon
+}
diff --git a/libgo/go/golang.org/x/tools/go/types/typeutil/callee.go b/libgo/go/golang.org/x/tools/go/types/typeutil/callee.go
index 38f596daf9e..90b3ab0e21c 100644
--- a/libgo/go/golang.org/x/tools/go/types/typeutil/callee.go
+++ b/libgo/go/golang.org/x/tools/go/types/typeutil/callee.go
@@ -9,13 +9,29 @@ import (
"go/types"
"golang.org/x/tools/go/ast/astutil"
+ "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 := astutil.Unparen(call.Fun)
+
+ // Look through type instantiation if necessary.
+ isInstance := false
+ switch fun.(type) {
+ case *ast.IndexExpr, *typeparams.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 := astutil.Unparen(call.Fun).(type) {
+ switch fun := fun.(type) {
case *ast.Ident:
obj = info.Uses[fun] // type, var, builtin, or declared func
case *ast.SelectorExpr:
@@ -28,11 +44,18 @@ func Callee(info *types.Info, call *ast.CallExpr) types.Object {
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.
+// 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
diff --git a/libgo/go/golang.org/x/tools/go/types/typeutil/map.go b/libgo/go/golang.org/x/tools/go/types/typeutil/map.go
index c7f75450064..490ee904a62 100644
--- a/libgo/go/golang.org/x/tools/go/types/typeutil/map.go
+++ b/libgo/go/golang.org/x/tools/go/types/typeutil/map.go
@@ -11,6 +11,8 @@ import (
"fmt"
"go/types"
"reflect"
+
+ "golang.org/x/tools/internal/typeparams"
)
// Map is a hash-table-based mapping from types (types.Type) to
@@ -211,11 +213,29 @@ func (m *Map) KeysString() string {
// Call MakeHasher to create a Hasher.
type Hasher struct {
memo map[types.Type]uint32
+
+ // ptrMap records pointer identity.
+ ptrMap map[interface{}]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 *typeparams.TypeParamList
}
// MakeHasher returns a new Hasher instance.
func MakeHasher() Hasher {
- return Hasher{make(map[types.Type]uint32)}
+ return Hasher{
+ memo: make(map[types.Type]uint32),
+ ptrMap: make(map[interface{}]uint32),
+ sigTParams: nil,
+ }
}
// Hash computes a hash value for the given type t such that
@@ -273,17 +293,62 @@ func (h Hasher) hashFor(t types.Type) uint32 {
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 := typeparams.ForSignature(t)
+ 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 *typeparams.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++ {
- // See go/types.identicalMethods for rationale.
// Method order is not significant.
// Ignore m.Pkg().
m := t.Method(i)
hash += 3*hashString(m.Name()) + 5*h.Hash(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:
@@ -293,13 +358,22 @@ func (h Hasher) hashFor(t types.Type) uint32 {
return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem())
case *types.Named:
- // Not safe with a copying GC; objects may move.
- return uint32(reflect.ValueOf(t.Obj()).Pointer())
+ hash := h.hashPtr(t.Obj())
+ targs := typeparams.NamedTypeArgs(t)
+ for i := 0; i < targs.Len(); i++ {
+ targ := targs.At(i)
+ hash += 2 * h.Hash(targ)
+ }
+ return hash
+
+ case *typeparams.TypeParam:
+ return h.hashTypeParam(t)
case *types.Tuple:
return h.hashTuple(t)
}
- panic(t)
+
+ panic(fmt.Sprintf("%T: %v", t, t))
}
func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
@@ -311,3 +385,57 @@ func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
}
return hash
}
+
+func (h Hasher) hashUnion(t *typeparams.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 []*typeparams.Term) uint32 {
+ var hash uint32 = 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.
+//
+// Otherwise the hash of t depends only on t's pointer identity.
+func (h Hasher) hashTypeParam(t *typeparams.TypeParam) uint32 {
+ if h.sigTParams != nil {
+ i := t.Index()
+ if i >= 0 && i < h.sigTParams.Len() && t == h.sigTParams.At(i) {
+ return 9173 + 2*h.Hash(t.Constraint()) + 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 interface{}) uint32 {
+ if hash, ok := h.ptrMap[ptr]; ok {
+ return hash
+ }
+ hash := uint32(reflect.ValueOf(ptr).Pointer())
+ h.ptrMap[ptr] = hash
+ return hash
+}
diff --git a/libgo/go/golang.org/x/tools/internal/lsp/fuzzy/input.go b/libgo/go/golang.org/x/tools/internal/lsp/fuzzy/input.go
index ac377035ec6..c1038163f1a 100644
--- a/libgo/go/golang.org/x/tools/internal/lsp/fuzzy/input.go
+++ b/libgo/go/golang.org/x/tools/internal/lsp/fuzzy/input.go
@@ -27,23 +27,23 @@ const (
// RuneRoles detects the roles of each byte rune in an input string and stores it in the output
// slice. The rune role depends on the input type. Stops when it parsed all the runes in the string
// or when it filled the output. If output is nil, then it gets created.
-func RuneRoles(str string, reuse []RuneRole) []RuneRole {
+func RuneRoles(candidate []byte, reuse []RuneRole) []RuneRole {
var output []RuneRole
- if cap(reuse) < len(str) {
- output = make([]RuneRole, 0, len(str))
+ if cap(reuse) < len(candidate) {
+ output = make([]RuneRole, 0, len(candidate))
} else {
output = reuse[:0]
}
prev, prev2 := rtNone, rtNone
- for i := 0; i < len(str); i++ {
- r := rune(str[i])
+ for i := 0; i < len(candidate); i++ {
+ r := rune(candidate[i])
role := RNone
curr := rtLower
- if str[i] <= unicode.MaxASCII {
- curr = runeType(rt[str[i]] - '0')
+ if candidate[i] <= unicode.MaxASCII {
+ curr = runeType(rt[candidate[i]] - '0')
}
if curr == rtLower {
@@ -58,7 +58,7 @@ func RuneRoles(str string, reuse []RuneRole) []RuneRole {
if prev == rtUpper {
// This and previous characters are both upper case.
- if i+1 == len(str) {
+ if i+1 == len(candidate) {
// This is last character, previous was also uppercase -> this is UCTail
// i.e., (current char is C): aBC / BC / ABC
role = RUCTail
@@ -118,11 +118,26 @@ func LastSegment(input string, roles []RuneRole) string {
return input[start+1 : end+1]
}
-// ToLower transforms the input string to lower case, which is stored in the output byte slice.
+// fromChunks copies string chunks into the given buffer.
+func fromChunks(chunks []string, buffer []byte) []byte {
+ ii := 0
+ for _, chunk := range chunks {
+ for i := 0; i < len(chunk); i++ {
+ if ii >= cap(buffer) {
+ break
+ }
+ buffer[ii] = chunk[i]
+ ii++
+ }
+ }
+ return buffer[:ii]
+}
+
+// toLower transforms the input string to lower case, which is stored in the output byte slice.
// The lower casing considers only ASCII values - non ASCII values are left unmodified.
// Stops when parsed all input or when it filled the output slice. If output is nil, then it gets
// created.
-func ToLower(input string, reuse []byte) []byte {
+func toLower(input []byte, reuse []byte) []byte {
output := reuse
if cap(reuse) < len(input) {
output = make([]byte, len(input))
@@ -130,7 +145,7 @@ func ToLower(input string, reuse []byte) []byte {
for i := 0; i < len(input); i++ {
r := rune(input[i])
- if r <= unicode.MaxASCII {
+ if input[i] <= unicode.MaxASCII {
if 'A' <= r && r <= 'Z' {
r += 'a' - 'A'
}
diff --git a/libgo/go/golang.org/x/tools/internal/lsp/fuzzy/matcher.go b/libgo/go/golang.org/x/tools/internal/lsp/fuzzy/matcher.go
index 16a643097de..265cdcf1604 100644
--- a/libgo/go/golang.org/x/tools/internal/lsp/fuzzy/matcher.go
+++ b/libgo/go/golang.org/x/tools/internal/lsp/fuzzy/matcher.go
@@ -51,8 +51,12 @@ type Matcher struct {
lastCandidateLen int // in bytes
lastCandidateMatched bool
- // Here we save the last candidate in lower-case. This is basically a byte slice we reuse for
- // performance reasons, so the slice is not reallocated for every candidate.
+ // Reusable buffers to avoid allocating for every candidate.
+ // - inputBuf stores the concatenated input chunks
+ // - lowerBuf stores the last candidate in lower-case
+ // - rolesBuf stores the calculated roles for each rune in the last
+ // candidate.
+ inputBuf [MaxInputSize]byte
lowerBuf [MaxInputSize]byte
rolesBuf [MaxInputSize]RuneRole
}
@@ -72,7 +76,7 @@ func NewMatcher(pattern string) *Matcher {
m := &Matcher{
pattern: pattern,
- patternLower: ToLower(pattern, nil),
+ patternLower: toLower([]byte(pattern), nil),
}
for i, c := range m.patternLower {
@@ -88,7 +92,7 @@ func NewMatcher(pattern string) *Matcher {
m.patternShort = m.patternLower
}
- m.patternRoles = RuneRoles(pattern, nil)
+ m.patternRoles = RuneRoles([]byte(pattern), nil)
if len(pattern) > 0 {
maxCharScore := 4
@@ -102,10 +106,15 @@ func NewMatcher(pattern string) *Matcher {
// This is not designed for parallel use. Multiple candidates must be scored sequentially.
// Returns a score between 0 and 1 (0 - no match, 1 - perfect match).
func (m *Matcher) Score(candidate string) float32 {
+ return m.ScoreChunks([]string{candidate})
+}
+
+func (m *Matcher) ScoreChunks(chunks []string) float32 {
+ candidate := fromChunks(chunks, m.inputBuf[:])
if len(candidate) > MaxInputSize {
candidate = candidate[:MaxInputSize]
}
- lower := ToLower(candidate, m.lowerBuf[:])
+ lower := toLower(candidate, m.lowerBuf[:])
m.lastCandidateLen = len(candidate)
if len(m.pattern) == 0 {
@@ -174,7 +183,7 @@ func (m *Matcher) MatchedRanges() []int {
return ret
}
-func (m *Matcher) match(candidate string, candidateLower []byte) bool {
+func (m *Matcher) match(candidate []byte, candidateLower []byte) bool {
i, j := 0, 0
for ; i < len(candidateLower) && j < len(m.patternLower); i++ {
if candidateLower[i] == m.patternLower[j] {
@@ -192,7 +201,7 @@ func (m *Matcher) match(candidate string, candidateLower []byte) bool {
return true
}
-func (m *Matcher) computeScore(candidate string, candidateLower []byte) int {
+func (m *Matcher) computeScore(candidate []byte, candidateLower []byte) int {
pattLen, candLen := len(m.pattern), len(candidate)
for j := 0; j <= len(m.pattern); j++ {
diff --git a/libgo/go/golang.org/x/tools/internal/lsp/fuzzy/symbol.go b/libgo/go/golang.org/x/tools/internal/lsp/fuzzy/symbol.go
new file mode 100644
index 00000000000..df9fbd51410
--- /dev/null
+++ b/libgo/go/golang.org/x/tools/internal/lsp/fuzzy/symbol.go
@@ -0,0 +1,236 @@
+// 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.
+
+package fuzzy
+
+import (
+ "unicode"
+)
+
+// SymbolMatcher implements a fuzzy matching algorithm optimized for Go symbols
+// of the form:
+// example.com/path/to/package.object.field
+//
+// Knowing that we are matching symbols like this allows us to make the
+// following optimizations:
+// - We can incorporate right-to-left relevance directly into the score
+// calculation.
+// - We can match from right to left, discarding leading bytes if the input is
+// too long.
+// - We just take the right-most match without losing too much precision. This
+// allows us to use an O(n) algorithm.
+// - We can operate directly on chunked strings; in many cases we will
+// be storing the package path and/or package name separately from the
+// symbol or identifiers, so doing this avoids allocating strings.
+// - We can return the index of the right-most match, allowing us to trim
+// irrelevant qualification.
+//
+// This implementation is experimental, serving as a reference fast algorithm
+// to compare to the fuzzy algorithm implemented by Matcher.
+type SymbolMatcher struct {
+ // Using buffers of length 256 is both a reasonable size for most qualified
+ // symbols, and makes it easy to avoid bounds checks by using uint8 indexes.
+ pattern [256]rune
+ patternLen uint8
+ inputBuffer [256]rune // avoid allocating when considering chunks
+ roles [256]uint32 // which roles does a rune play (word start, etc.)
+ segments [256]uint8 // how many segments from the right is each rune
+}
+
+const (
+ segmentStart uint32 = 1 << iota
+ wordStart
+ separator
+)
+
+// NewSymbolMatcher creates a SymbolMatcher that may be used to match the given
+// search pattern.
+//
+// Currently this matcher only accepts case-insensitive fuzzy patterns.
+//
+// An empty pattern matches no input.
+func NewSymbolMatcher(pattern string) *SymbolMatcher {
+ m := &SymbolMatcher{}
+ for _, p := range pattern {
+ m.pattern[m.patternLen] = unicode.ToLower(p)
+ m.patternLen++
+ if m.patternLen == 255 || int(m.patternLen) == len(pattern) {
+ // break at 255 so that we can represent patternLen with a uint8.
+ break
+ }
+ }
+ return m
+}
+
+// Match looks for the right-most match of the search pattern within the symbol
+// represented by concatenating the given chunks, returning its offset and
+// score.
+//
+// If a match is found, the first return value will hold the absolute byte
+// offset within all chunks for the start of the symbol. In other words, the
+// index of the match within strings.Join(chunks, ""). If no match is found,
+// the first return value will be -1.
+//
+// The second return value will be the score of the match, which is always
+// between 0 and 1, inclusive. A score of 0 indicates no match.
+func (m *SymbolMatcher) Match(chunks []string) (int, float64) {
+ // Explicit behavior for an empty pattern.
+ //
+ // As a minor optimization, this also avoids nilness checks later on, since
+ // the compiler can prove that m != nil.
+ if m.patternLen == 0 {
+ return -1, 0
+ }
+
+ // First phase: populate the input buffer with lower-cased runes.
+ //
+ // We could also check for a forward match here, but since we'd have to write
+ // the entire input anyway this has negligible impact on performance.
+
+ var (
+ inputLen = uint8(0)
+ modifiers = wordStart | segmentStart
+ )
+
+input:
+ for _, chunk := range chunks {
+ for _, r := range chunk {
+ if r == '.' || r == '/' {
+ modifiers |= separator
+ }
+ // optimization: avoid calls to unicode.ToLower, which can't be inlined.
+ l := r
+ if r <= unicode.MaxASCII {
+ if 'A' <= r && r <= 'Z' {
+ l = r + 'a' - 'A'
+ }
+ } else {
+ l = unicode.ToLower(r)
+ }
+ if l != r {
+ modifiers |= wordStart
+ }
+ m.inputBuffer[inputLen] = l
+ m.roles[inputLen] = modifiers
+ inputLen++
+ if m.roles[inputLen-1]&separator != 0 {
+ modifiers = wordStart | segmentStart
+ } else {
+ modifiers = 0
+ }
+ // TODO: we should prefer the right-most input if it overflows, rather
+ // than the left-most as we're doing here.
+ if inputLen == 255 {
+ break input
+ }
+ }
+ }
+
+ // Second phase: find the right-most match, and count segments from the
+ // right.
+
+ var (
+ pi = uint8(m.patternLen - 1) // pattern index
+ p = m.pattern[pi] // pattern rune
+ start = -1 // start offset of match
+ rseg = uint8(0)
+ )
+ const maxSeg = 3 // maximum number of segments from the right to count, for scoring purposes.
+
+ for ii := inputLen - 1; ; ii-- {
+ r := m.inputBuffer[ii]
+ if rseg < maxSeg && m.roles[ii]&separator != 0 {
+ rseg++
+ }
+ m.segments[ii] = rseg
+ if p == r {
+ if pi == 0 {
+ start = int(ii)
+ break
+ }
+ pi--
+ p = m.pattern[pi]
+ }
+ // Don't check ii >= 0 in the loop condition: ii is a uint8.
+ if ii == 0 {
+ break
+ }
+ }
+
+ if start < 0 {
+ // no match: skip scoring
+ return -1, 0
+ }
+
+ // Third phase: find the shortest match, and compute the score.
+
+ // Score is the average score for each character.
+ //
+ // A character score is the multiple of:
+ // 1. 1.0 if the character starts a segment, .8 if the character start a
+ // mid-segment word, otherwise 0.6. This carries over to immediately
+ // following characters.
+ // 2. For the final character match, the multiplier from (1) is reduced to
+ // .8 if the next character in the input is a mid-segment word, or 0.6 if
+ // the next character in the input is not a word or segment start. This
+ // ensures that we favor whole-word or whole-segment matches over prefix
+ // matches.
+ // 3. 1.0 if the character is part of the last segment, otherwise
+ // 1.0-.2*<segments from the right>, with a max segment count of 3.
+ //
+ // This is a very naive algorithm, but it is fast. There's lots of prior art
+ // here, and we should leverage it. For example, we could explicitly consider
+ // character distance, and exact matches of words or segments.
+ //
+ // Also note that this might not actually find the highest scoring match, as
+ // doing so could require a non-linear algorithm, depending on how the score
+ // is calculated.
+
+ pi = 0
+ p = m.pattern[pi]
+
+ const (
+ segStreak = 1.0
+ wordStreak = 0.8
+ noStreak = 0.6
+ perSegment = 0.2 // we count at most 3 segments above
+ )
+
+ streakBonus := noStreak
+ totScore := 0.0
+ for ii := uint8(start); ii < inputLen; ii++ {
+ r := m.inputBuffer[ii]
+ if r == p {
+ pi++
+ p = m.pattern[pi]
+ // Note: this could be optimized with some bit operations.
+ switch {
+ case m.roles[ii]&segmentStart != 0 && segStreak > streakBonus:
+ streakBonus = segStreak
+ case m.roles[ii]&wordStart != 0 && wordStreak > streakBonus:
+ streakBonus = wordStreak
+ }
+ finalChar := pi >= m.patternLen
+ // finalCost := 1.0
+ if finalChar && streakBonus > noStreak {
+ switch {
+ case ii == inputLen-1 || m.roles[ii+1]&segmentStart != 0:
+ // Full segment: no reduction
+ case m.roles[ii+1]&wordStart != 0:
+ streakBonus = wordStreak
+ default:
+ streakBonus = noStreak
+ }
+ }
+ totScore += streakBonus * (1.0 - float64(m.segments[ii])*perSegment)
+ if finalChar {
+ break
+ }
+ } else {
+ streakBonus = noStreak
+ }
+ }
+
+ return start, totScore / float64(m.patternLen)
+}
diff --git a/libgo/go/golang.org/x/tools/internal/typeparams/common.go b/libgo/go/golang.org/x/tools/internal/typeparams/common.go
new file mode 100644
index 00000000000..1222764b6a3
--- /dev/null
+++ b/libgo/go/golang.org/x/tools/internal/typeparams/common.go
@@ -0,0 +1,79 @@
+// 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.
+
+// 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
+// restrictions on a type parameter. In the future, this API may be available
+// from go/types.
+//
+// See the example/README.md for a more detailed guide on how to update tools
+// to support generics.
+package typeparams
+
+import (
+ "go/ast"
+ "go/token"
+ "go/types"
+)
+
+// UnpackIndexExpr extracts data from AST nodes that represent index
+// expressions.
+//
+// For an ast.IndexExpr, the resulting indices slice will contain exactly one
+// index expression. For an ast.IndexListExpr (go1.18+), it may have a variable
+// number of index expressions.
+//
+// For nodes that don't represent index expressions, the first return value of
+// UnpackIndexExpr will be nil.
+func UnpackIndexExpr(n ast.Node) (x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) {
+ switch e := n.(type) {
+ case *ast.IndexExpr:
+ return e.X, e.Lbrack, []ast.Expr{e.Index}, e.Rbrack
+ case *IndexListExpr:
+ return e.X, e.Lbrack, e.Indices, e.Rbrack
+ }
+ return nil, token.NoPos, nil, token.NoPos
+}
+
+// PackIndexExpr returns an *ast.IndexExpr or *ast.IndexListExpr, depending on
+// the cardinality of indices. Calling PackIndexExpr with len(indices) == 0
+// will panic.
+func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) ast.Expr {
+ switch len(indices) {
+ case 0:
+ panic("empty indices")
+ case 1:
+ return &ast.IndexExpr{
+ X: x,
+ Lbrack: lbrack,
+ Index: indices[0],
+ Rbrack: rbrack,
+ }
+ default:
+ return &IndexListExpr{
+ X: x,
+ Lbrack: lbrack,
+ Indices: indices,
+ Rbrack: rbrack,
+ }
+ }
+}
+
+// IsTypeParam reports whether t is a type parameter.
+func IsTypeParam(t types.Type) bool {
+ _, ok := t.(*TypeParam)
+ return ok
+}
diff --git a/libgo/go/golang.org/x/tools/internal/typeparams/enabled_go117.go b/libgo/go/golang.org/x/tools/internal/typeparams/enabled_go117.go
new file mode 100644
index 00000000000..18212390e19
--- /dev/null
+++ b/libgo/go/golang.org/x/tools/internal/typeparams/enabled_go117.go
@@ -0,0 +1,12 @@
+// 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
diff --git a/libgo/go/golang.org/x/tools/internal/typeparams/enabled_go118.go b/libgo/go/golang.org/x/tools/internal/typeparams/enabled_go118.go
new file mode 100644
index 00000000000..d67148823c4
--- /dev/null
+++ b/libgo/go/golang.org/x/tools/internal/typeparams/enabled_go118.go
@@ -0,0 +1,15 @@
+// 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
diff --git a/libgo/go/golang.org/x/tools/internal/typeparams/normalize.go b/libgo/go/golang.org/x/tools/internal/typeparams/normalize.go
new file mode 100644
index 00000000000..090f142a5f3
--- /dev/null
+++ b/libgo/go/golang.org/x/tools/internal/typeparams/normalize.go
@@ -0,0 +1,216 @@
+// 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.
+
+package typeparams
+
+import (
+ "errors"
+ "fmt"
+ "go/types"
+ "os"
+ "strings"
+)
+
+//go:generate go run copytermlist.go
+
+const debug = false
+
+var ErrEmptyTypeSet = errors.New("empty type set")
+
+// StructuralTerms returns a slice of terms representing the normalized
+// structural type restrictions of a type parameter, if any.
+//
+// Structural type restrictions of a type parameter are created via
+// non-interface types embedded in its constraint interface (directly, or via a
+// chain of interface embeddings). For example, in the declaration
+// type T[P interface{~int; m()}] int
+// the structural restriction of the type parameter P is ~int.
+//
+// With interface embedding and unions, the specification of structural type
+// restrictions may be arbitrarily complex. For example, consider the
+// following:
+//
+// type A interface{ ~string|~[]byte }
+//
+// type B interface{ int|string }
+//
+// type C interface { ~string|~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.
+//
+// StructuralTerms 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, StructuralTerms
+// returns a slice of tilde terms corresponding to the terms of the union in
+// the normalized structural restriction. An error is returned if the
+// constraint interface is invalid, exceeds complexity bounds, or has an empty
+// type set. In the latter case, StructuralTerms returns ErrEmptyTypeSet.
+//
+// StructuralTerms makes no guarantees about the order of terms, except that it
+// is deterministic.
+func StructuralTerms(tparam *TypeParam) ([]*Term, error) {
+ constraint := tparam.Constraint()
+ if constraint == nil {
+ return nil, fmt.Errorf("%s has nil constraint", tparam)
+ }
+ iface, _ := constraint.Underlying().(*types.Interface)
+ if iface == nil {
+ return nil, fmt.Errorf("constraint is %T, not *types.Interface", constraint.Underlying())
+ }
+ return InterfaceTermSet(iface)
+}
+
+// InterfaceTermSet computes the normalized terms for a constraint interface,
+// returning an error if the term set cannot be computed or is empty. In the
+// latter case, the error will be ErrEmptyTypeSet.
+//
+// See the documentation of StructuralTerms for more information on
+// normalization.
+func InterfaceTermSet(iface *types.Interface) ([]*Term, error) {
+ return computeTermSet(iface)
+}
+
+// UnionTermSet computes the normalized terms for a union, returning an error
+// if the term set cannot be computed or is empty. In the latter case, the
+// error will be ErrEmptyTypeSet.
+//
+// See the documentation of StructuralTerms for more information on
+// normalization.
+func UnionTermSet(union *Union) ([]*Term, error) {
+ return computeTermSet(union)
+}
+
+func computeTermSet(typ types.Type) ([]*Term, error) {
+ tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0)
+ if err != nil {
+ return nil, err
+ }
+ if tset.terms.isEmpty() {
+ return nil, ErrEmptyTypeSet
+ }
+ if tset.terms.isAll() {
+ return nil, nil
+ }
+ var terms []*Term
+ for _, term := range tset.terms {
+ terms = append(terms, NewTerm(term.tilde, term.typ))
+ }
+ return terms, nil
+}
+
+// A termSet holds the normalized set of terms for a given type.
+//
+// The name termSet is intentionally distinct from 'type set': a type set is
+// all types that implement a type (and includes method restrictions), whereas
+// a term set just represents the structural restrictions on a type.
+type termSet struct {
+ complete bool
+ terms termlist
+}
+
+func indentf(depth int, format string, args ...interface{}) {
+ fmt.Fprintf(os.Stderr, strings.Repeat(".", depth)+format+"\n", args...)
+}
+
+func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) {
+ if t == nil {
+ panic("nil type")
+ }
+
+ if debug {
+ indentf(depth, "%s", t.String())
+ defer func() {
+ if err != nil {
+ indentf(depth, "=> %s", err)
+ } else {
+ indentf(depth, "=> %s", res.terms.String())
+ }
+ }()
+ }
+
+ const maxTermCount = 100
+ if tset, ok := seen[t]; ok {
+ if !tset.complete {
+ return nil, fmt.Errorf("cycle detected in the declaration of %s", t)
+ }
+ return tset, nil
+ }
+
+ // Mark the current type as seen to avoid infinite recursion.
+ tset := new(termSet)
+ defer func() {
+ tset.complete = true
+ }()
+ seen[t] = tset
+
+ switch u := t.Underlying().(type) {
+ case *types.Interface:
+ // The term set of an interface is the intersection of the term sets of its
+ // embedded types.
+ tset.terms = allTermlist
+ for i := 0; i < u.NumEmbeddeds(); i++ {
+ embedded := u.EmbeddedType(i)
+ if _, ok := embedded.Underlying().(*TypeParam); ok {
+ return nil, fmt.Errorf("invalid embedded type %T", embedded)
+ }
+ tset2, err := computeTermSetInternal(embedded, seen, depth+1)
+ if err != nil {
+ return nil, err
+ }
+ tset.terms = tset.terms.intersect(tset2.terms)
+ }
+ case *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++ {
+ t := u.Term(i)
+ var terms termlist
+ switch t.Type().Underlying().(type) {
+ case *types.Interface:
+ tset2, err := computeTermSetInternal(t.Type(), seen, depth+1)
+ if err != nil {
+ return nil, err
+ }
+ terms = tset2.terms
+ case *TypeParam, *Union:
+ // A stand-alone type parameter or union is not permitted as union
+ // term.
+ return nil, fmt.Errorf("invalid union term %T", t)
+ default:
+ if t.Type() == types.Typ[types.Invalid] {
+ continue
+ }
+ terms = termlist{{t.Tilde(), t.Type()}}
+ }
+ tset.terms = tset.terms.union(terms)
+ if len(tset.terms) > maxTermCount {
+ return nil, fmt.Errorf("exceeded max term count %d", maxTermCount)
+ }
+ }
+ case *TypeParam:
+ panic("unreachable")
+ default:
+ // For all other types, the term set is just a single non-tilde term
+ // holding the type itself.
+ if u != types.Typ[types.Invalid] {
+ tset.terms = termlist{{false, t}}
+ }
+ }
+ return tset, nil
+}
+
+// under is a facade for the go/types internal function of the same name. It is
+// used by typeterm.go.
+func under(t types.Type) types.Type {
+ return t.Underlying()
+}
diff --git a/libgo/go/golang.org/x/tools/internal/typeparams/termlist.go b/libgo/go/golang.org/x/tools/internal/typeparams/termlist.go
new file mode 100644
index 00000000000..10857d504c4
--- /dev/null
+++ b/libgo/go/golang.org/x/tools/internal/typeparams/termlist.go
@@ -0,0 +1,172 @@
+// 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.
+
+// Code generated by copytermlist.go DO NOT EDIT.
+
+package typeparams
+
+import (
+ "bytes"
+ "go/types"
+)
+
+// A termlist represents the type set represented by the union
+// t1 βˆͺ y2 βˆͺ ... tn of the type sets of the terms t1 to tn.
+// A termlist is in normal form if all terms are disjoint.
+// termlist operations don't require the operands to be in
+// normal form.
+type termlist []*term
+
+// allTermlist represents the set of all types.
+// It is in normal form.
+var allTermlist = termlist{new(term)}
+
+// String prints the termlist exactly (without normalization).
+func (xl termlist) String() string {
+ if len(xl) == 0 {
+ return "βˆ…"
+ }
+ var buf bytes.Buffer
+ for i, x := range xl {
+ if i > 0 {
+ buf.WriteString(" βˆͺ ")
+ }
+ buf.WriteString(x.String())
+ }
+ return buf.String()
+}
+
+// isEmpty reports whether the termlist xl represents the empty set of types.
+func (xl termlist) isEmpty() bool {
+ // If there's a non-nil term, the entire list is not empty.
+ // If the termlist is in normal form, this requires at most
+ // one iteration.
+ for _, x := range xl {
+ if x != nil {
+ return false
+ }
+ }
+ return true
+}
+
+// isAll reports whether the termlist xl represents the set of all types.
+func (xl termlist) isAll() bool {
+ // If there's a 𝓀 term, the entire list is 𝓀.
+ // If the termlist is in normal form, this requires at most
+ // one iteration.
+ for _, x := range xl {
+ if x != nil && x.typ == nil {
+ return true
+ }
+ }
+ return false
+}
+
+// norm returns the normal form of xl.
+func (xl termlist) norm() termlist {
+ // Quadratic algorithm, but good enough for now.
+ // TODO(gri) fix asymptotic performance
+ used := make([]bool, len(xl))
+ var rl termlist
+ for i, xi := range xl {
+ if xi == nil || used[i] {
+ continue
+ }
+ for j := i + 1; j < len(xl); j++ {
+ xj := xl[j]
+ if xj == nil || used[j] {
+ continue
+ }
+ if u1, u2 := xi.union(xj); u2 == nil {
+ // If we encounter a 𝓀 term, the entire list is 𝓀.
+ // Exit early.
+ // (Note that this is not just an optimization;
+ // if we continue, we may end up with a 𝓀 term
+ // and other terms and the result would not be
+ // in normal form.)
+ if u1.typ == nil {
+ return allTermlist
+ }
+ xi = u1
+ used[j] = true // xj is now unioned into xi - ignore it in future iterations
+ }
+ }
+ rl = append(rl, xi)
+ }
+ return rl
+}
+
+// If the type set represented by xl is specified by a single (non-𝓀) term,
+// structuralType returns that type. Otherwise it returns nil.
+func (xl termlist) structuralType() types.Type {
+ if nl := xl.norm(); len(nl) == 1 {
+ return nl[0].typ // if nl.isAll() then typ is nil, which is ok
+ }
+ return nil
+}
+
+// union returns the union xl βˆͺ yl.
+func (xl termlist) union(yl termlist) termlist {
+ return append(xl, yl...).norm()
+}
+
+// intersect returns the intersection xl ∩ yl.
+func (xl termlist) intersect(yl termlist) termlist {
+ if xl.isEmpty() || yl.isEmpty() {
+ return nil
+ }
+
+ // Quadratic algorithm, but good enough for now.
+ // TODO(gri) fix asymptotic performance
+ var rl termlist
+ for _, x := range xl {
+ for _, y := range yl {
+ if r := x.intersect(y); r != nil {
+ rl = append(rl, r)
+ }
+ }
+ }
+ return rl.norm()
+}
+
+// equal reports whether xl and yl represent the same type set.
+func (xl termlist) equal(yl termlist) bool {
+ // TODO(gri) this should be more efficient
+ return xl.subsetOf(yl) && yl.subsetOf(xl)
+}
+
+// includes reports whether t ∈ xl.
+func (xl termlist) includes(t types.Type) bool {
+ for _, x := range xl {
+ if x.includes(t) {
+ return true
+ }
+ }
+ return false
+}
+
+// supersetOf reports whether y βŠ† xl.
+func (xl termlist) supersetOf(y *term) bool {
+ for _, x := range xl {
+ if y.subsetOf(x) {
+ return true
+ }
+ }
+ return false
+}
+
+// subsetOf reports whether xl βŠ† yl.
+func (xl termlist) subsetOf(yl termlist) bool {
+ if yl.isEmpty() {
+ return xl.isEmpty()
+ }
+
+ // each term x of xl must be a subset of yl
+ for _, x := range xl {
+ if !yl.supersetOf(x) {
+ return false // x is not a subset yl
+ }
+ }
+ return true
+}
diff --git a/libgo/go/golang.org/x/tools/internal/typeparams/typeparams_go117.go b/libgo/go/golang.org/x/tools/internal/typeparams/typeparams_go117.go
new file mode 100644
index 00000000000..5fd3fc35156
--- /dev/null
+++ b/libgo/go/golang.org/x/tools/internal/typeparams/typeparams_go117.go
@@ -0,0 +1,192 @@
+// 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{}
+
+// 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
+}
diff --git a/libgo/go/golang.org/x/tools/internal/typeparams/typeparams_go118.go b/libgo/go/golang.org/x/tools/internal/typeparams/typeparams_go118.go
new file mode 100644
index 00000000000..7470aed8c99
--- /dev/null
+++ b/libgo/go/golang.org/x/tools/internal/typeparams/typeparams_go118.go
@@ -0,0 +1,146 @@
+// 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
+
+// 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)
+}
diff --git a/libgo/go/golang.org/x/tools/internal/typeparams/typeterm.go b/libgo/go/golang.org/x/tools/internal/typeparams/typeterm.go
new file mode 100644
index 00000000000..7ddee28d987
--- /dev/null
+++ b/libgo/go/golang.org/x/tools/internal/typeparams/typeterm.go
@@ -0,0 +1,170 @@
+// 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.
+
+// Code generated by copytermlist.go DO NOT EDIT.
+
+package typeparams
+
+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
+//
+type term struct {
+ tilde bool // valid if typ != nil
+ typ types.Type
+}
+
+func (x *term) String() string {
+ switch {
+ case x == nil:
+ return "βˆ…"
+ case x.typ == nil:
+ return "𝓀"
+ case x.tilde:
+ return "~" + x.typ.String()
+ default:
+ return x.typ.String()
+ }
+}
+
+// equal reports whether x and y represent the same type set.
+func (x *term) equal(y *term) bool {
+ // easy cases
+ switch {
+ case x == nil || y == nil:
+ return x == y
+ case x.typ == nil || y.typ == nil:
+ return x.typ == y.typ
+ }
+ // βˆ… βŠ‚ x, y βŠ‚ 𝓀
+
+ return x.tilde == y.tilde && types.Identical(x.typ, y.typ)
+}
+
+// union returns the union x βˆͺ y: zero, one, or two non-nil terms.
+func (x *term) union(y *term) (_, _ *term) {
+ // easy cases
+ switch {
+ case x == nil && y == nil:
+ return nil, nil // βˆ… βˆͺ βˆ… == βˆ…
+ case x == nil:
+ return y, nil // βˆ… βˆͺ y == y
+ case y == nil:
+ return x, nil // x βˆͺ βˆ… == x
+ case x.typ == nil:
+ return x, nil // 𝓀 βˆͺ y == 𝓀
+ case y.typ == nil:
+ return y, nil // x βˆͺ 𝓀 == 𝓀
+ }
+ // βˆ… βŠ‚ x, y βŠ‚ 𝓀
+
+ if x.disjoint(y) {
+ return x, y // x βˆͺ y == (x, y) if x ∩ y == βˆ…
+ }
+ // x.typ == y.typ
+
+ // ~t βˆͺ ~t == ~t
+ // ~t βˆͺ T == ~t
+ // T βˆͺ ~t == ~t
+ // T βˆͺ T == T
+ if x.tilde || !y.tilde {
+ return x, nil
+ }
+ return y, nil
+}
+
+// intersect returns the intersection x ∩ y.
+func (x *term) intersect(y *term) *term {
+ // easy cases
+ switch {
+ case x == nil || y == nil:
+ return nil // βˆ… ∩ y == βˆ… and ∩ βˆ… == βˆ…
+ case x.typ == nil:
+ return y // 𝓀 ∩ y == y
+ case y.typ == nil:
+ return x // x ∩ 𝓀 == x
+ }
+ // βˆ… βŠ‚ x, y βŠ‚ 𝓀
+
+ if x.disjoint(y) {
+ return nil // x ∩ y == βˆ… if x ∩ y == βˆ…
+ }
+ // x.typ == y.typ
+
+ // ~t ∩ ~t == ~t
+ // ~t ∩ T == T
+ // T ∩ ~t == T
+ // T ∩ T == T
+ if !x.tilde || y.tilde {
+ return x
+ }
+ return y
+}
+
+// includes reports whether t ∈ x.
+func (x *term) includes(t types.Type) bool {
+ // easy cases
+ switch {
+ case x == nil:
+ return false // t ∈ βˆ… == false
+ case x.typ == nil:
+ return true // t ∈ 𝓀 == true
+ }
+ // βˆ… βŠ‚ x βŠ‚ 𝓀
+
+ u := t
+ if x.tilde {
+ u = under(u)
+ }
+ return types.Identical(x.typ, u)
+}
+
+// subsetOf reports whether x βŠ† y.
+func (x *term) subsetOf(y *term) bool {
+ // easy cases
+ switch {
+ case x == nil:
+ return true // βˆ… βŠ† y == true
+ case y == nil:
+ return false // x βŠ† βˆ… == false since x != βˆ…
+ case y.typ == nil:
+ return true // x βŠ† 𝓀 == true
+ case x.typ == nil:
+ return false // 𝓀 βŠ† y == false since y != 𝓀
+ }
+ // βˆ… βŠ‚ x, y βŠ‚ 𝓀
+
+ if x.disjoint(y) {
+ return false // x βŠ† y == false if x ∩ y == βˆ…
+ }
+ // x.typ == y.typ
+
+ // ~t βŠ† ~t == true
+ // ~t βŠ† T == false
+ // T βŠ† ~t == true
+ // T βŠ† T == true
+ return !x.tilde || y.tilde
+}
+
+// disjoint reports whether x ∩ y == βˆ….
+// x.typ and y.typ must not be nil.
+func (x *term) disjoint(y *term) bool {
+ if debug && (x.typ == nil || y.typ == nil) {
+ panic("invalid argument(s)")
+ }
+ ux := x.typ
+ if y.tilde {
+ ux = under(ux)
+ }
+ uy := y.typ
+ if x.tilde {
+ uy = under(uy)
+ }
+ return !types.Identical(ux, uy)
+}
diff --git a/libgo/go/golang.org/x/tools/txtar/archive.go b/libgo/go/golang.org/x/tools/txtar/archive.go
new file mode 100644
index 00000000000..214256617b5
--- /dev/null
+++ b/libgo/go/golang.org/x/tools/txtar/archive.go
@@ -0,0 +1,140 @@
+// 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 txtar implements a trivial text-based file archive format.
+//
+// The goals for the format are:
+//
+// - be trivial enough to create and edit by hand.
+// - be able to store trees of text files describing go command test cases.
+// - diff nicely in git history and code reviews.
+//
+// Non-goals include being a completely general archive format,
+// storing binary data, storing file modes, storing special files like
+// symbolic links, and so on.
+//
+// Txtar format
+//
+// A txtar archive is zero or more comment lines and then a sequence of file entries.
+// Each file entry begins with a file marker line of the form "-- FILENAME --"
+// and is followed by zero or more file content lines making up the file data.
+// The comment or file content ends at the next file marker line.
+// The file marker line must begin with the three-byte sequence "-- "
+// and end with the three-byte sequence " --", but the enclosed
+// file name can be surrounding by additional white space,
+// all of which is stripped.
+//
+// If the txtar file is missing a trailing newline on the final line,
+// parsers should consider a final newline to be present anyway.
+//
+// There are no possible syntax errors in a txtar archive.
+package txtar
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "strings"
+)
+
+// An Archive is a collection of files.
+type Archive struct {
+ Comment []byte
+ Files []File
+}
+
+// A File is a single file in an archive.
+type File struct {
+ Name string // name of file ("foo/bar.txt")
+ Data []byte // text content of file
+}
+
+// Format returns the serialized form of an Archive.
+// It is assumed that the Archive data structure is well-formed:
+// a.Comment and all a.File[i].Data contain no file marker lines,
+// and all a.File[i].Name is non-empty.
+func Format(a *Archive) []byte {
+ var buf bytes.Buffer
+ buf.Write(fixNL(a.Comment))
+ for _, f := range a.Files {
+ fmt.Fprintf(&buf, "-- %s --\n", f.Name)
+ buf.Write(fixNL(f.Data))
+ }
+ return buf.Bytes()
+}
+
+// ParseFile parses the named file as an archive.
+func ParseFile(file string) (*Archive, error) {
+ data, err := ioutil.ReadFile(file)
+ if err != nil {
+ return nil, err
+ }
+ return Parse(data), nil
+}
+
+// Parse parses the serialized form of an Archive.
+// The returned Archive holds slices of data.
+func Parse(data []byte) *Archive {
+ a := new(Archive)
+ var name string
+ a.Comment, name, data = findFileMarker(data)
+ for name != "" {
+ f := File{name, nil}
+ f.Data, name, data = findFileMarker(data)
+ a.Files = append(a.Files, f)
+ }
+ return a
+}
+
+var (
+ newlineMarker = []byte("\n-- ")
+ marker = []byte("-- ")
+ markerEnd = []byte(" --")
+)
+
+// findFileMarker finds the next file marker in data,
+// extracts the file name, and returns the data before the marker,
+// the file name, and the data after the marker.
+// If there is no next marker, findFileMarker returns before = fixNL(data), name = "", after = nil.
+func findFileMarker(data []byte) (before []byte, name string, after []byte) {
+ var i int
+ for {
+ if name, after = isMarker(data[i:]); name != "" {
+ return data[:i], name, after
+ }
+ j := bytes.Index(data[i:], newlineMarker)
+ if j < 0 {
+ return fixNL(data), "", nil
+ }
+ i += j + 1 // positioned at start of new possible marker
+ }
+}
+
+// isMarker checks whether data begins with a file marker line.
+// If so, it returns the name from the line and the data after the line.
+// Otherwise it returns name == "" with an unspecified after.
+func isMarker(data []byte) (name string, after []byte) {
+ if !bytes.HasPrefix(data, marker) {
+ return "", nil
+ }
+ if i := bytes.IndexByte(data, '\n'); i >= 0 {
+ data, after = data[:i], data[i+1:]
+ }
+ if !(bytes.HasSuffix(data, markerEnd) && len(data) >= len(marker)+len(markerEnd)) {
+ return "", nil
+ }
+ return strings.TrimSpace(string(data[len(marker) : len(data)-len(markerEnd)])), after
+}
+
+// If data is empty or ends in \n, fixNL returns data.
+// Otherwise fixNL returns a new slice consisting of data with a final \n added.
+func fixNL(data []byte) []byte {
+ if len(data) == 0 || data[len(data)-1] == '\n' {
+ return data
+ }
+ d := make([]byte, len(data)+1)
+ copy(d, data)
+ d[len(data)] = '\n'
+ return d
+}