1
0
mirror of https://github.com/golang/go synced 2024-07-08 12:18:55 +00:00

cmd: update vendored x/tools to 904c6ba

This fixes one of the many obstacles to enabling
types.Alias by updating vet.

The 'loopclosure' checker (formerly 'rangeloop') no longer
reports any findings with go1.22, so the test needed work
to ensure that it still runs on files with go1.21 semantics.

Updates #65294

Change-Id: I987ff529d4e165b56b7241563c6003e63bf92bb1
Reviewed-on: https://go-review.googlesource.com/c/go/+/575315
Auto-Submit: Alan Donovan <adonovan@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Alan Donovan 2024-03-29 15:49:38 -04:00 committed by Gopher Robot
parent e565720e49
commit 3b29222ffd
50 changed files with 19862 additions and 279 deletions

View File

@ -31,13 +31,13 @@ Maintaining vendor directories
Before updating vendor directories, ensure that module mode is enabled.
Make sure that GO111MODULE is not set in the environment, or that it is
set to 'on' or 'auto'.
set to 'on' or 'auto', and if you use a go.work file, set GOWORK=off.
Requirements may be added, updated, and removed with 'go get'.
The vendor directory may be updated with 'go mod vendor'.
A typical sequence might be:
cd src
cd src # or src/cmd
go get golang.org/x/net@master
go mod tidy
go mod vendor

View File

@ -11,7 +11,7 @@ require (
golang.org/x/sys v0.18.0
golang.org/x/telemetry v0.0.0-20240401194020-3640ba572dd1
golang.org/x/term v0.18.0
golang.org/x/tools v0.18.0
golang.org/x/tools v0.19.1-0.20240329171618-904c6baa6e14
)
require (

View File

@ -38,7 +38,7 @@ golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
golang.org/x/tools v0.19.1-0.20240329171618-904c6baa6e14 h1:apiqzCtEqbg/NjzevIbwubwnRo7WZDOgdr8s6ZvIKi0=
golang.org/x/tools v0.19.1-0.20240329171618-904c6baa6e14/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
rsc.io/markdown v0.0.0-20240117044121-669d2fdf1650 h1:fuOABZYWclLVNotDsHVaFixLdtoC7+UQZJ0KSC1ocm0=
rsc.io/markdown v0.0.0-20240117044121-669d2fdf1650/go.mod h1:8xcPgWmwlZONN1D9bjxtHEjrUtSEa3fakVF8iaewYKQ=

View File

@ -1,12 +0,0 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.19
// +build !go1.19
package asmdecl
func additionalArches() []*asmArch {
return nil
}

View File

@ -1,14 +0,0 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.19
// +build go1.19
package asmdecl
var asmArchLoong64 = asmArch{name: "loong64", bigEndian: false, stack: "R3", lr: true}
func additionalArches() []*asmArch {
return []*asmArch{&asmArchLoong64}
}

View File

@ -96,6 +96,7 @@ var (
asmArchRISCV64 = asmArch{name: "riscv64", bigEndian: false, stack: "SP", lr: true, retRegs: []string{"X10", "F10"}}
asmArchS390X = asmArch{name: "s390x", bigEndian: true, stack: "R15", lr: true}
asmArchWasm = asmArch{name: "wasm", bigEndian: false, stack: "SP", lr: false}
asmArchLoong64 = asmArch{name: "loong64", bigEndian: false, stack: "R3", lr: true}
arches = []*asmArch{
&asmArch386,
@ -111,11 +112,11 @@ var (
&asmArchRISCV64,
&asmArchS390X,
&asmArchWasm,
&asmArchLoong64,
}
)
func init() {
arches = append(arches, additionalArches()...)
for _, arch := range arches {
arch.sizes = types.SizesFor("gc", arch.name)
if arch.sizes == nil {

View File

@ -15,6 +15,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/aliases"
"golang.org/x/tools/internal/typeparams"
)
@ -71,7 +72,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
return
}
var structuralTypes []types.Type
switch typ := typ.(type) {
switch typ := aliases.Unalias(typ).(type) {
case *types.TypeParam:
terms, err := typeparams.StructuralTerms(typ)
if err != nil {
@ -83,9 +84,9 @@ func run(pass *analysis.Pass) (interface{}, error) {
default:
structuralTypes = append(structuralTypes, typ)
}
for _, typ := range structuralTypes {
under := deref(typ.Underlying())
strct, ok := under.(*types.Struct)
strct, ok := typeparams.Deref(typ).Underlying().(*types.Struct)
if !ok {
// skip non-struct composite literals
continue
@ -142,29 +143,18 @@ func run(pass *analysis.Pass) (interface{}, error) {
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
}
// isLocalType reports whether typ belongs to the same package as pass.
// TODO(adonovan): local means "internal to a function"; rename to isSamePackageType.
func isLocalType(pass *analysis.Pass, typ types.Type) bool {
switch x := typ.(type) {
switch x := aliases.Unalias(typ).(type) {
case *types.Struct:
// struct literals are local types
return true
case *types.Pointer:
return isLocalType(pass, x.Elem())
case *types.Named:
case interface{ Obj() *types.TypeName }: // *Named or *TypeParam (aliases were removed already)
// 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 *types.TypeParam:
return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test")
}
return false
}

View File

@ -18,6 +18,7 @@ import (
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/typeparams"
)
@ -255,7 +256,7 @@ func lockPath(tpkg *types.Package, typ types.Type, seen map[types.Type]bool) typ
}
seen[typ] = true
if tpar, ok := typ.(*types.TypeParam); ok {
if tpar, ok := aliases.Unalias(typ).(*types.TypeParam); ok {
terms, err := typeparams.StructuralTerms(tpar)
if err != nil {
return nil // invalid type

View File

@ -14,6 +14,8 @@ 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/aliases"
"golang.org/x/tools/internal/typesinternal"
)
const Doc = `check for mistakes using HTTP responses
@ -116,7 +118,8 @@ func isHTTPFuncOrMethodOnClient(info *types.Info, expr *ast.CallExpr) bool {
if res.Len() != 2 {
return false // the function called does not return two values.
}
if ptr, ok := res.At(0).Type().(*types.Pointer); !ok || !analysisutil.IsNamedType(ptr.Elem(), "net/http", "Response") {
isPtr, named := typesinternal.ReceiverNamed(res.At(0))
if !isPtr || named == nil || !analysisutil.IsNamedType(named, "net/http", "Response") {
return false // the first return type is not *http.Response.
}
@ -134,7 +137,7 @@ func isHTTPFuncOrMethodOnClient(info *types.Info, expr *ast.CallExpr) bool {
if analysisutil.IsNamedType(typ, "net/http", "Client") {
return true // method on http.Client.
}
ptr, ok := typ.(*types.Pointer)
ptr, ok := aliases.Unalias(typ).(*types.Pointer)
return ok && analysisutil.IsNamedType(ptr.Elem(), "net/http", "Client") // method on *http.Client.
}

View File

@ -13,6 +13,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"
)
//go:embed doc.go
@ -28,7 +29,7 @@ var Analyzer = &analysis.Analyzer{
// assertableTo checks whether interface v can be asserted into t. It returns
// nil on success, or the first conflicting method on failure.
func assertableTo(v, t types.Type) *types.Func {
func assertableTo(free *typeparams.Free, v, t types.Type) *types.Func {
if t == nil || v == nil {
// not assertable to, but there is no missing method
return nil
@ -42,7 +43,7 @@ func assertableTo(v, t types.Type) *types.Func {
// Mitigations for interface comparisons and generics.
// TODO(https://github.com/golang/go/issues/50658): Support more precise conclusion.
if isParameterized(V) || isParameterized(T) {
if free.Has(V) || free.Has(T) {
return nil
}
if f, wrongType := types.MissingMethod(V, T, false); wrongType {
@ -57,6 +58,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
(*ast.TypeAssertExpr)(nil),
(*ast.TypeSwitchStmt)(nil),
}
var free typeparams.Free
inspect.Preorder(nodeFilter, func(n ast.Node) {
var (
assert *ast.TypeAssertExpr // v.(T) expression
@ -86,7 +88,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
V := pass.TypesInfo.TypeOf(assert.X)
for _, target := range targets {
T := pass.TypesInfo.TypeOf(target)
if f := assertableTo(V, T); f != nil {
if f := assertableTo(&free, V, T); f != nil {
pass.Reportf(
target.Pos(),
"impossible type assertion: no type can implement both %v and %v (conflicting types for %v method)",

View File

@ -14,6 +14,7 @@ import (
"go/types"
"os"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/analysisinternal"
)
@ -115,7 +116,7 @@ func Imports(pkg *types.Package, path string) bool {
// This function avoids allocating the concatenation of "pkg.Name",
// which is important for the performance of syntax matching.
func IsNamedType(t types.Type, pkgPath string, names ...string) bool {
n, ok := t.(*types.Named)
n, ok := aliases.Unalias(t).(*types.Named)
if !ok {
return false
}

View File

@ -14,6 +14,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/typesinternal"
"golang.org/x/tools/internal/versions"
)
@ -54,9 +55,8 @@ func run(pass *analysis.Pass) (interface{}, error) {
switch n := n.(type) {
case *ast.File:
// Only traverse the file if its goversion is strictly before go1.22.
goversion := versions.Lang(versions.FileVersions(pass.TypesInfo, n))
// goversion is empty for older go versions (or the version is invalid).
return goversion == "" || versions.Compare(goversion, "go1.22") < 0
goversion := versions.FileVersion(pass.TypesInfo, n)
return versions.Before(goversion, versions.Go1_22)
case *ast.RangeStmt:
body = n.Body
addVar(n.Key)
@ -367,9 +367,6 @@ func isMethodCall(info *types.Info, expr ast.Expr, pkgPath, typeName, method str
// Check that the receiver is a <pkgPath>.<typeName> or
// *<pkgPath>.<typeName>.
rtype := recv.Type()
if ptr, ok := recv.Type().(*types.Pointer); ok {
rtype = ptr.Elem()
}
return analysisutil.IsNamedType(rtype, pkgPath, typeName)
_, named := typesinternal.ReceiverNamed(recv)
return analysisutil.IsNamedType(named, pkgPath, typeName)
}

View File

@ -24,6 +24,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/aliases"
"golang.org/x/tools/internal/typeparams"
)
@ -959,6 +960,8 @@ func isStringer(sig *types.Signature) bool {
// It is almost always a mistake to print a function value.
func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {
if typ := pass.TypesInfo.Types[e].Type; typ != nil {
// Don't call Underlying: a named func type with a String method is ok.
// TODO(adonovan): it would be more precise to check isStringer.
_, ok := typ.(*types.Signature)
return ok
}
@ -1010,7 +1013,7 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) {
// Skip checking functions with unknown type.
return
}
if sig, ok := typ.(*types.Signature); ok {
if sig, ok := typ.Underlying().(*types.Signature); ok {
if !sig.Variadic() {
// Skip checking non-variadic functions.
return
@ -1020,7 +1023,7 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) {
typ := params.At(firstArg).Type()
typ = typ.(*types.Slice).Elem()
it, ok := typ.(*types.Interface)
it, ok := aliases.Unalias(typ).(*types.Interface)
if !ok || !it.Empty() {
// Skip variadic functions accepting non-interface{} args.
return

View File

@ -10,6 +10,7 @@ import (
"go/types"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/typeparams"
)
@ -72,7 +73,7 @@ func (m *argMatcher) match(typ types.Type, topLevel bool) bool {
return true
}
if typ, _ := typ.(*types.TypeParam); typ != nil {
if typ, _ := aliases.Unalias(typ).(*types.TypeParam); typ != nil {
// Avoid infinite recursion through type parameters.
if m.seen[typ] {
return true
@ -275,7 +276,7 @@ func (m *argMatcher) match(typ types.Type, topLevel bool) bool {
}
func isConvertibleToString(typ types.Type) bool {
if bt, ok := typ.(*types.Basic); ok && bt.Kind() == types.UntypedNil {
if bt, ok := aliases.Unalias(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
// would just panic anyway.

View File

@ -21,6 +21,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/aliases"
"golang.org/x/tools/internal/typeparams"
)
@ -89,7 +90,8 @@ func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) {
if v == nil {
return
}
amt, ok := constant.Int64Val(v)
u := constant.ToInt(v) // either an Int or Unknown
amt, ok := constant.Int64Val(u)
if !ok {
return
}
@ -98,7 +100,7 @@ func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) {
return
}
var structuralTypes []types.Type
switch t := t.(type) {
switch t := aliases.Unalias(t).(type) {
case *types.TypeParam:
terms, err := typeparams.StructuralTerms(t)
if err != nil {

View File

@ -20,6 +20,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/typesinternal"
)
//go:embed doc.go
@ -48,6 +49,7 @@ const (
)
func run(pass *analysis.Pass) (any, error) {
var attrType types.Type // The type of slog.Attr
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
nodeFilter := []ast.Node{
(*ast.CallExpr)(nil),
@ -66,6 +68,11 @@ func run(pass *analysis.Pass) (any, error) {
// Not a slog function that takes key-value pairs.
return
}
// Here we know that fn.Pkg() is "log/slog".
if attrType == nil {
attrType = fn.Pkg().Scope().Lookup("Attr").Type()
}
if isMethodExpr(pass.TypesInfo, call) {
// Call is to a method value. Skip the first argument.
skipArgs++
@ -91,8 +98,19 @@ func run(pass *analysis.Pass) (any, error) {
pos = key
case types.IsInterface(t):
// As we do not do dataflow, we do not know what the dynamic type is.
// It could be a string or an Attr so we don't know what to expect next.
pos = unknown
// But we might be able to learn enough to make a decision.
if types.AssignableTo(stringType, t) {
// t must be an empty interface. So it can also be an Attr.
// We don't know enough to make an assumption.
pos = unknown
continue
} else if attrType != nil && types.AssignableTo(attrType, t) {
// Assume it is an Attr.
pos = key
continue
}
// Can't be either a string or Attr. Definitely an error.
fallthrough
default:
if unknownArg == nil {
pass.ReportRangef(arg, "%s arg %q should be a string or a slog.Attr (possible missing key or value)",
@ -150,14 +168,10 @@ func isAttr(t types.Type) bool {
func shortName(fn *types.Func) string {
var r string
if recv := fn.Type().(*types.Signature).Recv(); recv != nil {
t := recv.Type()
if pt, ok := t.(*types.Pointer); ok {
t = pt.Elem()
}
if nt, ok := t.(*types.Named); ok {
r = nt.Obj().Name()
if _, named := typesinternal.ReceiverNamed(recv); named != nil {
r = named.Obj().Name()
} else {
r = recv.Type().String()
r = recv.Type().String() // anon struct/interface
}
r += "."
}
@ -173,17 +187,12 @@ func kvFuncSkipArgs(fn *types.Func) (int, bool) {
return 0, false
}
var recvName string // by default a slog package function
recv := fn.Type().(*types.Signature).Recv()
if recv != nil {
t := recv.Type()
if pt, ok := t.(*types.Pointer); ok {
t = pt.Elem()
}
if nt, ok := t.(*types.Named); !ok {
return 0, false
} else {
recvName = nt.Obj().Name()
if recv := fn.Type().(*types.Signature).Recv(); recv != nil {
_, named := typesinternal.ReceiverNamed(recv)
if named == nil {
return 0, false // anon struct/interface
}
recvName = named.Obj().Name()
}
skip, ok := kvFuncs[recvName][fn.Name()]
return skip, ok

View File

@ -60,10 +60,12 @@ func describe(typ, inType types.Type, inName string) string {
}
func typeName(typ types.Type) string {
if v, _ := typ.(interface{ Name() string }); v != nil {
typ = aliases.Unalias(typ)
// TODO(adonovan): don't discard alias type, return its name.
if v, _ := typ.(*types.Basic); v != nil {
return v.Name()
}
if v, _ := typ.(interface{ Obj() *types.TypeName }); v != nil {
if v, _ := typ.(interface{ Obj() *types.TypeName }); v != nil { // Named, TypeParam
return v.Obj().Name()
}
return ""

View File

@ -17,6 +17,7 @@ import (
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/aliases"
)
//go:embed doc.go
@ -270,7 +271,7 @@ func forbiddenMethod(info *types.Info, call *ast.CallExpr) (*types.Var, *types.S
func formatMethod(sel *types.Selection, fn *types.Func) string {
var ptr string
rtype := sel.Recv()
if p, ok := rtype.(*types.Pointer); ok {
if p, ok := aliases.Unalias(rtype).(*types.Pointer); ok {
ptr = "*"
rtype = p.Elem()
}

View File

@ -30,7 +30,7 @@ func localFunctionDecls(info *types.Info, files []*ast.File) func(*types.Func) *
}
}
}
// TODO: once we only support go1.19+, set f = f.Origin() here.
// TODO: set f = f.Origin() here.
return fnDecls[f]
}
}

View File

@ -252,6 +252,8 @@ func validateFuzzArgs(pass *analysis.Pass, params *types.Tuple, expr ast.Expr) b
}
func isTestingType(typ types.Type, testingType string) bool {
// No Unalias here: I doubt "go test" recognizes
// "type A = *testing.T; func Test(A) {}" as a test.
ptr, ok := typ.(*types.Pointer)
if !ok {
return false

View File

@ -107,7 +107,7 @@ func badFormatAt(info *types.Info, e ast.Expr) int {
return -1
}
t, ok := tv.Type.(*types.Basic)
t, ok := tv.Type.(*types.Basic) // sic, no unalias
if !ok || t.Info()&types.IsString == 0 {
return -1
}

View File

@ -14,6 +14,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/typesinternal"
)
//go:embed doc.go
@ -69,12 +70,8 @@ func run(pass *analysis.Pass) (interface{}, error) {
// (*"encoding/json".Decoder).Decode
// (* "encoding/gob".Decoder).Decode
// (* "encoding/xml".Decoder).Decode
t := recv.Type()
if ptr, ok := t.(*types.Pointer); ok {
t = ptr.Elem()
}
tname := t.(*types.Named).Obj()
if tname.Name() == "Decoder" {
_, named := typesinternal.ReceiverNamed(recv)
if tname := named.Obj(); tname.Name() == "Decoder" {
switch tname.Pkg().Path() {
case "encoding/json", "encoding/xml", "encoding/gob":
argidx = 0 // func(interface{})

View File

@ -17,6 +17,7 @@ import (
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/internal/aliases"
)
//go:embed doc.go
@ -88,7 +89,7 @@ func isSafeUintptr(info *types.Info, x ast.Expr) bool {
// by the time we get to the conversion at the end.
// For now approximate by saying that *Header is okay
// but Header is not.
pt, ok := info.Types[x.X].Type.(*types.Pointer)
pt, ok := aliases.Unalias(info.Types[x.X].Type).(*types.Pointer)
if ok && isReflectHeader(pt.Elem()) {
return true
}

View File

@ -16,8 +16,8 @@ type builder struct {
cfg *CFG
mayReturn func(*ast.CallExpr) bool
current *Block
lblocks map[*ast.Object]*lblock // labeled blocks
targets *targets // linked stack of branch targets
lblocks map[string]*lblock // labeled blocks
targets *targets // linked stack of branch targets
}
func (b *builder) stmt(_s ast.Stmt) {
@ -42,7 +42,7 @@ start:
b.add(s)
if call, ok := s.X.(*ast.CallExpr); ok && !b.mayReturn(call) {
// Calls to panic, os.Exit, etc, never return.
b.current = b.newBlock("unreachable.call")
b.current = b.newBlock(KindUnreachable, s)
}
case *ast.DeclStmt:
@ -57,7 +57,7 @@ start:
}
case *ast.LabeledStmt:
label = b.labeledBlock(s.Label)
label = b.labeledBlock(s.Label, s)
b.jump(label._goto)
b.current = label._goto
_s = s.Stmt
@ -65,7 +65,7 @@ start:
case *ast.ReturnStmt:
b.add(s)
b.current = b.newBlock("unreachable.return")
b.current = b.newBlock(KindUnreachable, s)
case *ast.BranchStmt:
b.branchStmt(s)
@ -77,11 +77,11 @@ start:
if s.Init != nil {
b.stmt(s.Init)
}
then := b.newBlock("if.then")
done := b.newBlock("if.done")
then := b.newBlock(KindIfThen, s)
done := b.newBlock(KindIfDone, s)
_else := done
if s.Else != nil {
_else = b.newBlock("if.else")
_else = b.newBlock(KindIfElse, s)
}
b.add(s.Cond)
b.ifelse(then, _else)
@ -128,7 +128,7 @@ func (b *builder) branchStmt(s *ast.BranchStmt) {
switch s.Tok {
case token.BREAK:
if s.Label != nil {
if lb := b.labeledBlock(s.Label); lb != nil {
if lb := b.labeledBlock(s.Label, nil); lb != nil {
block = lb._break
}
} else {
@ -139,7 +139,7 @@ func (b *builder) branchStmt(s *ast.BranchStmt) {
case token.CONTINUE:
if s.Label != nil {
if lb := b.labeledBlock(s.Label); lb != nil {
if lb := b.labeledBlock(s.Label, nil); lb != nil {
block = lb._continue
}
} else {
@ -155,14 +155,14 @@ func (b *builder) branchStmt(s *ast.BranchStmt) {
case token.GOTO:
if s.Label != nil {
block = b.labeledBlock(s.Label)._goto
block = b.labeledBlock(s.Label, nil)._goto
}
}
if block == nil {
block = b.newBlock("undefined.branch")
if block == nil { // ill-typed (e.g. undefined label)
block = b.newBlock(KindUnreachable, s)
}
b.jump(block)
b.current = b.newBlock("unreachable.branch")
b.current = b.newBlock(KindUnreachable, s)
}
func (b *builder) switchStmt(s *ast.SwitchStmt, label *lblock) {
@ -172,7 +172,7 @@ func (b *builder) switchStmt(s *ast.SwitchStmt, label *lblock) {
if s.Tag != nil {
b.add(s.Tag)
}
done := b.newBlock("switch.done")
done := b.newBlock(KindSwitchDone, s)
if label != nil {
label._break = done
}
@ -188,13 +188,13 @@ func (b *builder) switchStmt(s *ast.SwitchStmt, label *lblock) {
for i, clause := range s.Body.List {
body := fallthru
if body == nil {
body = b.newBlock("switch.body") // first case only
body = b.newBlock(KindSwitchCaseBody, clause) // first case only
}
// Preallocate body block for the next case.
fallthru = done
if i+1 < ncases {
fallthru = b.newBlock("switch.body")
fallthru = b.newBlock(KindSwitchCaseBody, s.Body.List[i+1])
}
cc := clause.(*ast.CaseClause)
@ -208,7 +208,7 @@ func (b *builder) switchStmt(s *ast.SwitchStmt, label *lblock) {
var nextCond *Block
for _, cond := range cc.List {
nextCond = b.newBlock("switch.next")
nextCond = b.newBlock(KindSwitchNextCase, cc)
b.add(cond) // one half of the tag==cond condition
b.ifelse(body, nextCond)
b.current = nextCond
@ -247,7 +247,7 @@ func (b *builder) typeSwitchStmt(s *ast.TypeSwitchStmt, label *lblock) {
b.add(s.Assign)
}
done := b.newBlock("typeswitch.done")
done := b.newBlock(KindSwitchDone, s)
if label != nil {
label._break = done
}
@ -258,10 +258,10 @@ func (b *builder) typeSwitchStmt(s *ast.TypeSwitchStmt, label *lblock) {
default_ = cc
continue
}
body := b.newBlock("typeswitch.body")
body := b.newBlock(KindSwitchCaseBody, cc)
var next *Block
for _, casetype := range cc.List {
next = b.newBlock("typeswitch.next")
next = b.newBlock(KindSwitchNextCase, cc)
// casetype is a type, so don't call b.add(casetype).
// This block logically contains a type assertion,
// x.(casetype), but it's unclear how to represent x.
@ -300,7 +300,7 @@ func (b *builder) selectStmt(s *ast.SelectStmt, label *lblock) {
}
}
done := b.newBlock("select.done")
done := b.newBlock(KindSelectDone, s)
if label != nil {
label._break = done
}
@ -312,8 +312,8 @@ func (b *builder) selectStmt(s *ast.SelectStmt, label *lblock) {
defaultBody = &clause.Body
continue
}
body := b.newBlock("select.body")
next := b.newBlock("select.next")
body := b.newBlock(KindSelectCaseBody, clause)
next := b.newBlock(KindSelectAfterCase, clause)
b.ifelse(body, next)
b.current = body
b.targets = &targets{
@ -358,15 +358,15 @@ func (b *builder) forStmt(s *ast.ForStmt, label *lblock) {
if s.Init != nil {
b.stmt(s.Init)
}
body := b.newBlock("for.body")
done := b.newBlock("for.done") // target of 'break'
loop := body // target of back-edge
body := b.newBlock(KindForBody, s)
done := b.newBlock(KindForDone, s) // target of 'break'
loop := body // target of back-edge
if s.Cond != nil {
loop = b.newBlock("for.loop")
loop = b.newBlock(KindForLoop, s)
}
cont := loop // target of 'continue'
if s.Post != nil {
cont = b.newBlock("for.post")
cont = b.newBlock(KindForPost, s)
}
if label != nil {
label._break = done
@ -414,12 +414,12 @@ func (b *builder) rangeStmt(s *ast.RangeStmt, label *lblock) {
// jump loop
// done: (target of break)
loop := b.newBlock("range.loop")
loop := b.newBlock(KindRangeLoop, s)
b.jump(loop)
b.current = loop
body := b.newBlock("range.body")
done := b.newBlock("range.done")
body := b.newBlock(KindRangeBody, s)
done := b.newBlock(KindRangeDone, s)
b.ifelse(body, done)
b.current = body
@ -461,14 +461,19 @@ type lblock struct {
// labeledBlock returns the branch target associated with the
// specified label, creating it if needed.
func (b *builder) labeledBlock(label *ast.Ident) *lblock {
lb := b.lblocks[label.Obj]
func (b *builder) labeledBlock(label *ast.Ident, stmt *ast.LabeledStmt) *lblock {
lb := b.lblocks[label.Name]
if lb == nil {
lb = &lblock{_goto: b.newBlock(label.Name)}
lb = &lblock{_goto: b.newBlock(KindLabel, nil)}
if b.lblocks == nil {
b.lblocks = make(map[*ast.Object]*lblock)
b.lblocks = make(map[string]*lblock)
}
b.lblocks[label.Obj] = lb
b.lblocks[label.Name] = lb
}
// Fill in the label later (in case of forward goto).
// Stmt may be set already if labels are duplicated (ill-typed).
if stmt != nil && lb._goto.Stmt == nil {
lb._goto.Stmt = stmt
}
return lb
}
@ -477,11 +482,12 @@ func (b *builder) labeledBlock(label *ast.Ident) *lblock {
// slice and returns it.
// It does not automatically become the current block.
// comment is an optional string for more readable debugging output.
func (b *builder) newBlock(comment string) *Block {
func (b *builder) newBlock(kind BlockKind, stmt ast.Stmt) *Block {
g := b.cfg
block := &Block{
Index: int32(len(g.Blocks)),
comment: comment,
Index: int32(len(g.Blocks)),
Kind: kind,
Stmt: stmt,
}
block.Succs = block.succs2[:0]
g.Blocks = append(g.Blocks, block)

View File

@ -9,7 +9,10 @@
//
// The blocks of the CFG contain all the function's non-control
// statements. The CFG does not contain control statements such as If,
// Switch, Select, and Branch, but does contain their subexpressions.
// Switch, Select, and Branch, but does contain their subexpressions;
// also, each block records the control statement (Block.Stmt) that
// gave rise to it and its relationship (Block.Kind) to that statement.
//
// For example, this source code:
//
// if x := f(); x != nil {
@ -20,14 +23,14 @@
//
// produces this CFG:
//
// 1: x := f()
// 1: x := f() Body
// x != nil
// succs: 2, 3
// 2: T()
// 2: T() IfThen
// succs: 4
// 3: F()
// 3: F() IfElse
// succs: 4
// 4:
// 4: IfDone
//
// The CFG does contain Return statements; even implicit returns are
// materialized (at the position of the function's closing brace).
@ -50,6 +53,7 @@ import (
//
// The entry point is Blocks[0]; there may be multiple return blocks.
type CFG struct {
fset *token.FileSet
Blocks []*Block // block[0] is entry; order otherwise undefined
}
@ -64,9 +68,63 @@ type Block struct {
Succs []*Block // successor nodes in the graph
Index int32 // index within CFG.Blocks
Live bool // block is reachable from entry
Kind BlockKind // block kind
Stmt ast.Stmt // statement that gave rise to this block (see BlockKind for details)
comment string // for debugging
succs2 [2]*Block // underlying array for Succs
succs2 [2]*Block // underlying array for Succs
}
// A BlockKind identifies the purpose of a block.
// It also determines the possible types of its Stmt field.
type BlockKind uint8
const (
KindInvalid BlockKind = iota // Stmt=nil
KindUnreachable // unreachable block after {Branch,Return}Stmt / no-return call ExprStmt
KindBody // function body BlockStmt
KindForBody // body of ForStmt
KindForDone // block after ForStmt
KindForLoop // head of ForStmt
KindForPost // post condition of ForStmt
KindIfDone // block after IfStmt
KindIfElse // else block of IfStmt
KindIfThen // then block of IfStmt
KindLabel // labeled block of BranchStmt (Stmt may be nil for dangling label)
KindRangeBody // body of RangeStmt
KindRangeDone // block after RangeStmt
KindRangeLoop // head of RangeStmt
KindSelectCaseBody // body of SelectStmt
KindSelectDone // block after SelectStmt
KindSelectAfterCase // block after a CommClause
KindSwitchCaseBody // body of CaseClause
KindSwitchDone // block after {Type.}SwitchStmt
KindSwitchNextCase // secondary expression of a multi-expression CaseClause
)
func (kind BlockKind) String() string {
return [...]string{
KindInvalid: "Invalid",
KindUnreachable: "Unreachable",
KindBody: "Body",
KindForBody: "ForBody",
KindForDone: "ForDone",
KindForLoop: "ForLoop",
KindForPost: "ForPost",
KindIfDone: "IfDone",
KindIfElse: "IfElse",
KindIfThen: "IfThen",
KindLabel: "Label",
KindRangeBody: "RangeBody",
KindRangeDone: "RangeDone",
KindRangeLoop: "RangeLoop",
KindSelectCaseBody: "SelectCaseBody",
KindSelectDone: "SelectDone",
KindSelectAfterCase: "SelectAfterCase",
KindSwitchCaseBody: "SwitchCaseBody",
KindSwitchDone: "SwitchDone",
KindSwitchNextCase: "SwitchNextCase",
}[kind]
}
// New returns a new control-flow graph for the specified function body,
@ -82,7 +140,7 @@ func New(body *ast.BlockStmt, mayReturn func(*ast.CallExpr) bool) *CFG {
mayReturn: mayReturn,
cfg: new(CFG),
}
b.current = b.newBlock("entry")
b.current = b.newBlock(KindBody, body)
b.stmt(body)
// Compute liveness (reachability from entry point), breadth-first.
@ -110,7 +168,15 @@ func New(body *ast.BlockStmt, mayReturn func(*ast.CallExpr) bool) *CFG {
}
func (b *Block) String() string {
return fmt.Sprintf("block %d (%s)", b.Index, b.comment)
return fmt.Sprintf("block %d (%s)", b.Index, b.comment(nil))
}
func (b *Block) comment(fset *token.FileSet) string {
s := b.Kind.String()
if fset != nil && b.Stmt != nil {
s = fmt.Sprintf("%s@L%d", s, fset.Position(b.Stmt.Pos()).Line)
}
return s
}
// Return returns the return statement at the end of this block if present, nil
@ -129,7 +195,7 @@ func (b *Block) Return() (ret *ast.ReturnStmt) {
func (g *CFG) Format(fset *token.FileSet) string {
var buf bytes.Buffer
for _, b := range g.Blocks {
fmt.Fprintf(&buf, ".%d: # %s\n", b.Index, b.comment)
fmt.Fprintf(&buf, ".%d: # %s\n", b.Index, b.comment(fset))
for _, n := range b.Nodes {
fmt.Fprintf(&buf, "\t%s\n", formatNode(fset, n))
}
@ -145,6 +211,34 @@ func (g *CFG) Format(fset *token.FileSet) string {
return buf.String()
}
// Dot returns the control-flow graph in the [Dot graph description language].
// Use a command such as 'dot -Tsvg' to render it in a form viewable in a browser.
// This method is provided as a debugging aid; the details of the
// output are unspecified and may change.
//
// [Dot graph description language]: https://en.wikipedia.org/wiki/DOT_(graph_description_language)
func (g *CFG) Dot(fset *token.FileSet) string {
var buf bytes.Buffer
buf.WriteString("digraph CFG {\n")
buf.WriteString(" node [shape=box];\n")
for _, b := range g.Blocks {
// node label
var text bytes.Buffer
text.WriteString(b.comment(fset))
for _, n := range b.Nodes {
fmt.Fprintf(&text, "\n%s", formatNode(fset, n))
}
// node and edges
fmt.Fprintf(&buf, " n%d [label=%q];\n", b.Index, &text)
for _, succ := range b.Succs {
fmt.Fprintf(&buf, " n%d -> n%d;\n", b.Index, succ.Index)
}
}
buf.WriteString("}\n")
return buf.String()
}
func formatNode(fset *token.FileSet, n ast.Node) string {
var buf bytes.Buffer
format.Node(&buf, fset, n)

View File

@ -29,9 +29,12 @@ import (
"strconv"
"strings"
"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/typesinternal"
)
// TODO(adonovan): think about generic aliases.
// A Path is an opaque name that identifies a types.Object
// relative to its package. Conceptually, the name consists of a
// sequence of destructuring operations applied to the package scope
@ -223,7 +226,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) {
// Reject obviously non-viable cases.
switch obj := obj.(type) {
case *types.TypeName:
if _, ok := obj.Type().(*types.TypeParam); !ok {
if _, ok := aliases.Unalias(obj.Type()).(*types.TypeParam); !ok {
// With the exception of type parameters, only package-level type names
// have a path.
return "", fmt.Errorf("no path for %v", obj)
@ -310,7 +313,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) {
}
// Inspect declared methods of defined types.
if T, ok := o.Type().(*types.Named); ok {
if T, ok := aliases.Unalias(o.Type()).(*types.Named); ok {
path = append(path, opType)
// The method index here is always with respect
// to the underlying go/types data structures,
@ -391,17 +394,12 @@ func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
// of objectpath will only be giving us origin methods, anyway, as referring
// to instantiated methods is usually not useful.
if typeparams.OriginMethod(meth) != meth {
if meth.Origin() != meth {
return "", false
}
recvT := meth.Type().(*types.Signature).Recv().Type()
if ptr, ok := recvT.(*types.Pointer); ok {
recvT = ptr.Elem()
}
named, ok := recvT.(*types.Named)
if !ok {
_, named := typesinternal.ReceiverNamed(meth.Type().(*types.Signature).Recv())
if named == nil {
return "", false
}
@ -444,6 +442,8 @@ func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
// nil, it will be allocated as necessary.
func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte {
switch T := T.(type) {
case *aliases.Alias:
return find(obj, aliases.Unalias(T), path, seen)
case *types.Basic, *types.Named:
// Named types belonging to pkg were handled already,
// so T must belong to another package. No path.
@ -616,6 +616,7 @@ func Object(pkg *types.Package, p Path) (types.Object, error) {
// Inv: t != nil, obj == nil
t = aliases.Unalias(t)
switch code {
case opElem:
hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map

View File

@ -6,7 +6,11 @@ package typeutil
// This file defines utilities for user interfaces that display types.
import "go/types"
import (
"go/types"
"golang.org/x/tools/internal/aliases"
)
// IntuitiveMethodSet returns the intuitive method set of a type T,
// which is the set of methods you can call on an addressable value of
@ -24,7 +28,7 @@ import "go/types"
// The order of the result is as for types.MethodSet(T).
func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection {
isPointerToConcrete := func(T types.Type) bool {
ptr, ok := T.(*types.Pointer)
ptr, ok := aliases.Unalias(T).(*types.Pointer)
return ok && !types.IsInterface(ptr.Elem())
}

View File

@ -13,6 +13,8 @@ import (
"go/token"
"go/types"
"strconv"
"golang.org/x/tools/internal/aliases"
)
func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos {
@ -28,7 +30,10 @@ func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos
}
func ZeroValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
under := typ
// TODO(adonovan): think about generics, and also generic aliases.
under := aliases.Unalias(typ)
// Don't call Underlying unconditionally: although it removed
// Named and Alias, it also removes TypeParam.
if n, ok := typ.(*types.Named); ok {
under = n.Underlying()
}

View File

@ -6,6 +6,8 @@ package facts
import (
"go/types"
"golang.org/x/tools/internal/aliases"
)
// importMap computes the import map for a package by traversing the
@ -45,6 +47,8 @@ func importMap(imports []*types.Package) map[string]*types.Package {
addType = func(T types.Type) {
switch T := T.(type) {
case *aliases.Alias:
addType(aliases.Unalias(T))
case *types.Basic:
// nop
case *types.Named:

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -2,20 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package typeparams contains common utilities for writing tools that interact
// with generic Go code, as introduced with Go 1.18.
//
// Many of the types and functions in this package are proxies for the new APIs
// introduced in the standard library with Go 1.18. For example, the
// typeparams.Union type is an alias for go/types.Union, and the ForTypeSpec
// function returns the value of the go/ast.TypeSpec.TypeParams field. At Go
// versions older than 1.18 these helpers are implemented as stubs, allowing
// users of this package to write code that handles generic constructs inline,
// even if the Go version being used to compile does not support generics.
//
// Additionally, this package contains common utilities for working with the
// new generic constructs, to supplement the standard library APIs. Notably,
// the StructuralTerms API computes a minimal representation of the structural
// Package typeparams contains common utilities for writing tools that
// interact with generic Go code, as introduced with Go 1.18. It
// supplements the standard library APIs. Notably, the StructuralTerms
// API computes a minimal representation of the structural
// restrictions on a type parameter.
//
// An external version of these APIs is available in the
@ -23,10 +13,11 @@
package typeparams
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"golang.org/x/tools/internal/aliases"
)
// UnpackIndexExpr extracts data from AST nodes that represent index
@ -72,68 +63,12 @@ func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack toke
}
}
// IsTypeParam reports whether t is a type parameter.
// IsTypeParam reports whether t is a type parameter (or an alias of one).
func IsTypeParam(t types.Type) bool {
_, ok := t.(*types.TypeParam)
_, ok := aliases.Unalias(t).(*types.TypeParam)
return ok
}
// OriginMethod returns the origin method associated with the method fn.
// For methods on a non-generic receiver base type, this is just
// fn. However, for methods with a generic receiver, OriginMethod returns the
// corresponding method in the method set of the origin type.
//
// As a special case, if fn is not a method (has no receiver), OriginMethod
// returns fn.
func OriginMethod(fn *types.Func) *types.Func {
recv := fn.Type().(*types.Signature).Recv()
if recv == nil {
return fn
}
base := recv.Type()
p, isPtr := base.(*types.Pointer)
if isPtr {
base = p.Elem()
}
named, isNamed := base.(*types.Named)
if !isNamed {
// Receiver is a *types.Interface.
return fn
}
if named.TypeParams().Len() == 0 {
// Receiver base has no type parameters, so we can avoid the lookup below.
return fn
}
orig := named.Origin()
gfn, _, _ := types.LookupFieldOrMethod(orig, true, fn.Pkg(), fn.Name())
// This is a fix for a gopls crash (#60628) due to a go/types bug (#60634). In:
// package p
// type T *int
// func (*T) f() {}
// LookupFieldOrMethod(T, true, p, f)=nil, but NewMethodSet(*T)={(*T).f}.
// Here we make them consistent by force.
// (The go/types bug is general, but this workaround is reached only
// for generic T thanks to the early return above.)
if gfn == nil {
mset := types.NewMethodSet(types.NewPointer(orig))
for i := 0; i < mset.Len(); i++ {
m := mset.At(i)
if m.Obj().Id() == fn.Id() {
gfn = m.Obj()
break
}
}
}
// In golang/go#61196, we observe another crash, this time inexplicable.
if gfn == nil {
panic(fmt.Sprintf("missing origin method for %s.%s; named == origin: %t, named.NumMethods(): %d, origin.NumMethods(): %d", named, fn, named == orig, named.NumMethods(), orig.NumMethods()))
}
return gfn.(*types.Func)
}
// GenericAssignableTo is a generalization of types.AssignableTo that
// implements the following rule for uninstantiated generic types:
//
@ -158,6 +93,9 @@ func OriginMethod(fn *types.Func) *types.Func {
// In this case, GenericAssignableTo reports that instantiations of Container
// are assignable to the corresponding instantiation of Interface.
func GenericAssignableTo(ctxt *types.Context, V, T types.Type) bool {
V = aliases.Unalias(V)
T = aliases.Unalias(T)
// If V and T are not both named, or do not have matching non-empty type
// parameter lists, fall back on types.AssignableTo.

View File

@ -5,7 +5,10 @@
package typeparams
import (
"fmt"
"go/types"
"golang.org/x/tools/internal/aliases"
)
// CoreType returns the core type of T or nil if T does not have a core type.
@ -109,7 +112,7 @@ func CoreType(T types.Type) types.Type {
// _NormalTerms makes no guarantees about the order of terms, except that it
// is deterministic.
func _NormalTerms(typ types.Type) ([]*types.Term, error) {
switch typ := typ.(type) {
switch typ := aliases.Unalias(typ).(type) {
case *types.TypeParam:
return StructuralTerms(typ)
case *types.Union:
@ -120,3 +123,30 @@ func _NormalTerms(typ types.Type) ([]*types.Term, error) {
return []*types.Term{types.NewTerm(false, typ)}, nil
}
}
// Deref returns the type of the variable pointed to by t,
// if t's core type is a pointer; otherwise it returns t.
//
// Do not assume that Deref(T)==T implies T is not a pointer:
// consider "type T *T", for example.
//
// TODO(adonovan): ideally this would live in typesinternal, but that
// creates an import cycle. Move there when we melt this package down.
func Deref(t types.Type) types.Type {
if ptr, ok := CoreType(t).(*types.Pointer); ok {
return ptr.Elem()
}
return t
}
// MustDeref returns the type of the variable pointed to by t.
// It panics if t's core type is not a pointer.
//
// TODO(adonovan): ideally this would live in typesinternal, but that
// creates an import cycle. Move there when we melt this package down.
func MustDeref(t types.Type) types.Type {
if ptr, ok := CoreType(t).(*types.Pointer); ok {
return ptr.Elem()
}
panic(fmt.Sprintf("%v is not a pointer", t))
}

View File

@ -1,34 +1,34 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ifaceassert
package typeparams
import (
"go/types"
"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/aliases"
)
// isParameterized reports whether typ contains any of the type parameters of tparams.
// Free is a memoization of the set of free type parameters within a
// type. It makes a sequence of calls to [Free.Has] for overlapping
// types more efficient. The zero value is ready for use.
//
// NOTE: Adapted from go/types/infer.go. If 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 {
// NOTE: Adapted from go/types/infer.go. If it is later exported, factor.
type Free struct {
seen map[types.Type]bool
}
func (w *tpWalker) isParameterized(typ types.Type) (res bool) {
// Has reports whether the specified type has a free type parameter.
func (w *Free) Has(typ types.Type) (res bool) {
// detect cycles
if x, ok := w.seen[typ]; ok {
return x
}
if w.seen == nil {
w.seen = make(map[types.Type]bool)
}
w.seen[typ] = false
defer func() {
w.seen[typ] = res
@ -38,26 +38,29 @@ func (w *tpWalker) isParameterized(typ types.Type) (res bool) {
case nil, *types.Basic: // TODO(gri) should nil be handled here?
break
case *aliases.Alias:
return w.Has(aliases.Unalias(t))
case *types.Array:
return w.isParameterized(t.Elem())
return w.Has(t.Elem())
case *types.Slice:
return w.isParameterized(t.Elem())
return w.Has(t.Elem())
case *types.Struct:
for i, n := 0, t.NumFields(); i < n; i++ {
if w.isParameterized(t.Field(i).Type()) {
if w.Has(t.Field(i).Type()) {
return true
}
}
case *types.Pointer:
return w.isParameterized(t.Elem())
return w.Has(t.Elem())
case *types.Tuple:
n := t.Len()
for i := 0; i < n; i++ {
if w.isParameterized(t.At(i).Type()) {
if w.Has(t.At(i).Type()) {
return true
}
}
@ -70,37 +73,42 @@ func (w *tpWalker) isParameterized(typ types.Type) (res bool) {
// Similarly, the receiver of a method may declare (rather than
// use) type parameters, we don't care about those either.
// Thus, we only need to look at the input and result parameters.
return w.isParameterized(t.Params()) || w.isParameterized(t.Results())
return w.Has(t.Params()) || w.Has(t.Results())
case *types.Interface:
for i, n := 0, t.NumMethods(); i < n; i++ {
if w.isParameterized(t.Method(i).Type()) {
if w.Has(t.Method(i).Type()) {
return true
}
}
terms, err := typeparams.InterfaceTermSet(t)
terms, err := InterfaceTermSet(t)
if err != nil {
panic(err)
}
for _, term := range terms {
if w.isParameterized(term.Type()) {
if w.Has(term.Type()) {
return true
}
}
case *types.Map:
return w.isParameterized(t.Key()) || w.isParameterized(t.Elem())
return w.Has(t.Key()) || w.Has(t.Elem())
case *types.Chan:
return w.isParameterized(t.Elem())
return w.Has(t.Elem())
case *types.Named:
list := t.TypeArgs()
for i, n := 0, list.Len(); i < n; i++ {
if w.isParameterized(list.At(i)) {
args := t.TypeArgs()
// TODO(taking): this does not match go/types/infer.go. Check with rfindley.
if params := t.TypeParams(); params.Len() > args.Len() {
return true
}
for i, n := 0, args.Len(); i < n; i++ {
if w.Has(args.At(i)) {
return true
}
}
return w.Has(t.Underlying()) // recurse for types local to parameterized functions
case *types.TypeParam:
return true

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,179 @@
// Code generated by "stringer -type=ErrorCode"; DO NOT EDIT.
package typesinternal
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[InvalidSyntaxTree - -1]
_ = x[Test-1]
_ = x[BlankPkgName-2]
_ = x[MismatchedPkgName-3]
_ = x[InvalidPkgUse-4]
_ = x[BadImportPath-5]
_ = x[BrokenImport-6]
_ = x[ImportCRenamed-7]
_ = x[UnusedImport-8]
_ = x[InvalidInitCycle-9]
_ = x[DuplicateDecl-10]
_ = x[InvalidDeclCycle-11]
_ = x[InvalidTypeCycle-12]
_ = x[InvalidConstInit-13]
_ = x[InvalidConstVal-14]
_ = x[InvalidConstType-15]
_ = x[UntypedNilUse-16]
_ = x[WrongAssignCount-17]
_ = x[UnassignableOperand-18]
_ = x[NoNewVar-19]
_ = x[MultiValAssignOp-20]
_ = x[InvalidIfaceAssign-21]
_ = x[InvalidChanAssign-22]
_ = x[IncompatibleAssign-23]
_ = x[UnaddressableFieldAssign-24]
_ = x[NotAType-25]
_ = x[InvalidArrayLen-26]
_ = x[BlankIfaceMethod-27]
_ = x[IncomparableMapKey-28]
_ = x[InvalidIfaceEmbed-29]
_ = x[InvalidPtrEmbed-30]
_ = x[BadRecv-31]
_ = x[InvalidRecv-32]
_ = x[DuplicateFieldAndMethod-33]
_ = x[DuplicateMethod-34]
_ = x[InvalidBlank-35]
_ = x[InvalidIota-36]
_ = x[MissingInitBody-37]
_ = x[InvalidInitSig-38]
_ = x[InvalidInitDecl-39]
_ = x[InvalidMainDecl-40]
_ = x[TooManyValues-41]
_ = x[NotAnExpr-42]
_ = x[TruncatedFloat-43]
_ = x[NumericOverflow-44]
_ = x[UndefinedOp-45]
_ = x[MismatchedTypes-46]
_ = x[DivByZero-47]
_ = x[NonNumericIncDec-48]
_ = x[UnaddressableOperand-49]
_ = x[InvalidIndirection-50]
_ = x[NonIndexableOperand-51]
_ = x[InvalidIndex-52]
_ = x[SwappedSliceIndices-53]
_ = x[NonSliceableOperand-54]
_ = x[InvalidSliceExpr-55]
_ = x[InvalidShiftCount-56]
_ = x[InvalidShiftOperand-57]
_ = x[InvalidReceive-58]
_ = x[InvalidSend-59]
_ = x[DuplicateLitKey-60]
_ = x[MissingLitKey-61]
_ = x[InvalidLitIndex-62]
_ = x[OversizeArrayLit-63]
_ = x[MixedStructLit-64]
_ = x[InvalidStructLit-65]
_ = x[MissingLitField-66]
_ = x[DuplicateLitField-67]
_ = x[UnexportedLitField-68]
_ = x[InvalidLitField-69]
_ = x[UntypedLit-70]
_ = x[InvalidLit-71]
_ = x[AmbiguousSelector-72]
_ = x[UndeclaredImportedName-73]
_ = x[UnexportedName-74]
_ = x[UndeclaredName-75]
_ = x[MissingFieldOrMethod-76]
_ = x[BadDotDotDotSyntax-77]
_ = x[NonVariadicDotDotDot-78]
_ = x[MisplacedDotDotDot-79]
_ = x[InvalidDotDotDotOperand-80]
_ = x[InvalidDotDotDot-81]
_ = x[UncalledBuiltin-82]
_ = x[InvalidAppend-83]
_ = x[InvalidCap-84]
_ = x[InvalidClose-85]
_ = x[InvalidCopy-86]
_ = x[InvalidComplex-87]
_ = x[InvalidDelete-88]
_ = x[InvalidImag-89]
_ = x[InvalidLen-90]
_ = x[SwappedMakeArgs-91]
_ = x[InvalidMake-92]
_ = x[InvalidReal-93]
_ = x[InvalidAssert-94]
_ = x[ImpossibleAssert-95]
_ = x[InvalidConversion-96]
_ = x[InvalidUntypedConversion-97]
_ = x[BadOffsetofSyntax-98]
_ = x[InvalidOffsetof-99]
_ = x[UnusedExpr-100]
_ = x[UnusedVar-101]
_ = x[MissingReturn-102]
_ = x[WrongResultCount-103]
_ = x[OutOfScopeResult-104]
_ = x[InvalidCond-105]
_ = x[InvalidPostDecl-106]
_ = x[InvalidChanRange-107]
_ = x[InvalidIterVar-108]
_ = x[InvalidRangeExpr-109]
_ = x[MisplacedBreak-110]
_ = x[MisplacedContinue-111]
_ = x[MisplacedFallthrough-112]
_ = x[DuplicateCase-113]
_ = x[DuplicateDefault-114]
_ = x[BadTypeKeyword-115]
_ = x[InvalidTypeSwitch-116]
_ = x[InvalidExprSwitch-117]
_ = x[InvalidSelectCase-118]
_ = x[UndeclaredLabel-119]
_ = x[DuplicateLabel-120]
_ = x[MisplacedLabel-121]
_ = x[UnusedLabel-122]
_ = x[JumpOverDecl-123]
_ = x[JumpIntoBlock-124]
_ = x[InvalidMethodExpr-125]
_ = x[WrongArgCount-126]
_ = x[InvalidCall-127]
_ = x[UnusedResults-128]
_ = x[InvalidDefer-129]
_ = x[InvalidGo-130]
_ = x[BadDecl-131]
_ = x[RepeatedDecl-132]
_ = x[InvalidUnsafeAdd-133]
_ = x[InvalidUnsafeSlice-134]
_ = x[UnsupportedFeature-135]
_ = x[NotAGenericType-136]
_ = x[WrongTypeArgCount-137]
_ = x[CannotInferTypeArgs-138]
_ = x[InvalidTypeArg-139]
_ = x[InvalidInstanceCycle-140]
_ = x[InvalidUnion-141]
_ = x[MisplacedConstraintIface-142]
_ = x[InvalidMethodTypeParams-143]
_ = x[MisplacedTypeParam-144]
_ = x[InvalidUnsafeSliceData-145]
_ = x[InvalidUnsafeString-146]
}
const (
_ErrorCode_name_0 = "InvalidSyntaxTree"
_ErrorCode_name_1 = "TestBlankPkgNameMismatchedPkgNameInvalidPkgUseBadImportPathBrokenImportImportCRenamedUnusedImportInvalidInitCycleDuplicateDeclInvalidDeclCycleInvalidTypeCycleInvalidConstInitInvalidConstValInvalidConstTypeUntypedNilUseWrongAssignCountUnassignableOperandNoNewVarMultiValAssignOpInvalidIfaceAssignInvalidChanAssignIncompatibleAssignUnaddressableFieldAssignNotATypeInvalidArrayLenBlankIfaceMethodIncomparableMapKeyInvalidIfaceEmbedInvalidPtrEmbedBadRecvInvalidRecvDuplicateFieldAndMethodDuplicateMethodInvalidBlankInvalidIotaMissingInitBodyInvalidInitSigInvalidInitDeclInvalidMainDeclTooManyValuesNotAnExprTruncatedFloatNumericOverflowUndefinedOpMismatchedTypesDivByZeroNonNumericIncDecUnaddressableOperandInvalidIndirectionNonIndexableOperandInvalidIndexSwappedSliceIndicesNonSliceableOperandInvalidSliceExprInvalidShiftCountInvalidShiftOperandInvalidReceiveInvalidSendDuplicateLitKeyMissingLitKeyInvalidLitIndexOversizeArrayLitMixedStructLitInvalidStructLitMissingLitFieldDuplicateLitFieldUnexportedLitFieldInvalidLitFieldUntypedLitInvalidLitAmbiguousSelectorUndeclaredImportedNameUnexportedNameUndeclaredNameMissingFieldOrMethodBadDotDotDotSyntaxNonVariadicDotDotDotMisplacedDotDotDotInvalidDotDotDotOperandInvalidDotDotDotUncalledBuiltinInvalidAppendInvalidCapInvalidCloseInvalidCopyInvalidComplexInvalidDeleteInvalidImagInvalidLenSwappedMakeArgsInvalidMakeInvalidRealInvalidAssertImpossibleAssertInvalidConversionInvalidUntypedConversionBadOffsetofSyntaxInvalidOffsetofUnusedExprUnusedVarMissingReturnWrongResultCountOutOfScopeResultInvalidCondInvalidPostDeclInvalidChanRangeInvalidIterVarInvalidRangeExprMisplacedBreakMisplacedContinueMisplacedFallthroughDuplicateCaseDuplicateDefaultBadTypeKeywordInvalidTypeSwitchInvalidExprSwitchInvalidSelectCaseUndeclaredLabelDuplicateLabelMisplacedLabelUnusedLabelJumpOverDeclJumpIntoBlockInvalidMethodExprWrongArgCountInvalidCallUnusedResultsInvalidDeferInvalidGoBadDeclRepeatedDeclInvalidUnsafeAddInvalidUnsafeSliceUnsupportedFeatureNotAGenericTypeWrongTypeArgCountCannotInferTypeArgsInvalidTypeArgInvalidInstanceCycleInvalidUnionMisplacedConstraintIfaceInvalidMethodTypeParamsMisplacedTypeParamInvalidUnsafeSliceDataInvalidUnsafeString"
)
var (
_ErrorCode_index_1 = [...]uint16{0, 4, 16, 33, 46, 59, 71, 85, 97, 113, 126, 142, 158, 174, 189, 205, 218, 234, 253, 261, 277, 295, 312, 330, 354, 362, 377, 393, 411, 428, 443, 450, 461, 484, 499, 511, 522, 537, 551, 566, 581, 594, 603, 617, 632, 643, 658, 667, 683, 703, 721, 740, 752, 771, 790, 806, 823, 842, 856, 867, 882, 895, 910, 926, 940, 956, 971, 988, 1006, 1021, 1031, 1041, 1058, 1080, 1094, 1108, 1128, 1146, 1166, 1184, 1207, 1223, 1238, 1251, 1261, 1273, 1284, 1298, 1311, 1322, 1332, 1347, 1358, 1369, 1382, 1398, 1415, 1439, 1456, 1471, 1481, 1490, 1503, 1519, 1535, 1546, 1561, 1577, 1591, 1607, 1621, 1638, 1658, 1671, 1687, 1701, 1718, 1735, 1752, 1767, 1781, 1795, 1806, 1818, 1831, 1848, 1861, 1872, 1885, 1897, 1906, 1913, 1925, 1941, 1959, 1977, 1992, 2009, 2028, 2042, 2062, 2074, 2098, 2121, 2139, 2161, 2180}
)
func (i ErrorCode) String() string {
switch {
case i == -1:
return _ErrorCode_name_0
case 1 <= i && i <= 146:
i -= 1
return _ErrorCode_name_1[_ErrorCode_index_1[i]:_ErrorCode_index_1[i+1]]
default:
return "ErrorCode(" + strconv.FormatInt(int64(i), 10) + ")"
}
}

View File

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

View File

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

View File

@ -0,0 +1,50 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package typesinternal provides access to internal go/types APIs that are not
// yet exported.
package typesinternal
import (
"go/token"
"go/types"
"reflect"
"unsafe"
)
func SetUsesCgo(conf *types.Config) bool {
v := reflect.ValueOf(conf).Elem()
f := v.FieldByName("go115UsesCgo")
if !f.IsValid() {
f = v.FieldByName("UsesCgo")
if !f.IsValid() {
return false
}
}
addr := unsafe.Pointer(f.UnsafeAddr())
*(*bool)(addr) = true
return true
}
// ReadGo116ErrorData extracts additional information from types.Error values
// generated by Go version 1.16 and later: the error code, start position, and
// end position. If all positions are valid, start <= err.Pos <= end.
//
// If the data could not be read, the final result parameter will be false.
func ReadGo116ErrorData(err types.Error) (code ErrorCode, start, end token.Pos, ok bool) {
var data [3]int
// By coincidence all of these fields are ints, which simplifies things.
v := reflect.ValueOf(err)
for i, name := range []string{"go116code", "go116start", "go116end"} {
f := v.FieldByName(name)
if !f.IsValid() {
return 0, 0, 0, false
}
data[i] = int(f.Int())
}
return ErrorCode(data[0]), token.Pos(data[1]), token.Pos(data[2]), true
}

View File

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

View File

@ -0,0 +1,14 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package versions
// toolchain is maximum version (<1.22) that the go toolchain used
// to build the current tool is known to support.
//
// When a tool is built with >=1.22, the value of toolchain is unused.
//
// x/tools does not support building with go <1.18. So we take this
// as the minimum possible maximum.
var toolchain string = Go1_18

View File

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

View File

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

View File

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

View File

@ -12,9 +12,19 @@ import (
"go/types"
)
// FileVersions always reports the a file's Go version as the
// zero version at this Go version.
func FileVersions(info *types.Info, file *ast.File) string { return "" }
// FileVersion returns a language version (<=1.21) derived from runtime.Version()
// or an unknown future version.
func FileVersion(info *types.Info, file *ast.File) string {
// In x/tools built with Go <= 1.21, we do not have Info.FileVersions
// available. We use a go version derived from the toolchain used to
// compile the tool by default.
// This will be <= go1.21. We take this as the maximum version that
// this tool can support.
//
// There are no features currently in x/tools that need to tell fine grained
// differences for versions <1.22.
return toolchain
}
// InitFileVersions is a noop at this Go version.
// InitFileVersions is a noop when compiled with this Go version.
func InitFileVersions(*types.Info) {}

View File

@ -12,10 +12,27 @@ import (
"go/types"
)
// FileVersions maps a file to the file's semantic Go version.
// The reported version is the zero version if a version cannot be determined.
func FileVersions(info *types.Info, file *ast.File) string {
return info.FileVersions[file]
// FileVersions returns a file's Go version.
// The reported version is an unknown Future version if a
// version cannot be determined.
func FileVersion(info *types.Info, file *ast.File) string {
// In tools built with Go >= 1.22, the Go version of a file
// follow a cascades of sources:
// 1) types.Info.FileVersion, which follows the cascade:
// 1.a) file version (ast.File.GoVersion),
// 1.b) the package version (types.Config.GoVersion), or
// 2) is some unknown Future version.
//
// File versions require a valid package version to be provided to types
// in Config.GoVersion. Config.GoVersion is either from the package's module
// or the toolchain (go run). This value should be provided by go/packages
// or unitchecker.Config.GoVersion.
if v := info.FileVersions[file]; IsValid(v) {
return v
}
// Note: we could instead return runtime.Version() [if valid].
// This would act as a max version on what a tool can support.
return Future
}
// InitFileVersions initializes info to record Go versions for Go files.

View File

@ -4,6 +4,10 @@
package versions
import (
"strings"
)
// Note: If we use build tags to use go/versions when go >=1.22,
// we run into go.dev/issue/53737. Under some operations users would see an
// import of "go/versions" even if they would not compile the file.
@ -45,6 +49,7 @@ func IsValid(x string) bool { return isValid(stripGo(x)) }
// stripGo converts from a "go1.21" version to a "1.21" version.
// If v does not start with "go", stripGo returns the empty string (a known invalid version).
func stripGo(v string) string {
v, _, _ = strings.Cut(v, "-") // strip -bigcorp suffix.
if len(v) < 2 || v[:2] != "go" {
return ""
}

View File

@ -71,8 +71,8 @@ golang.org/x/text/internal/tag
golang.org/x/text/language
golang.org/x/text/transform
golang.org/x/text/unicode/norm
# golang.org/x/tools v0.18.0
## explicit; go 1.18
# golang.org/x/tools v0.19.1-0.20240329171618-904c6baa6e14
## explicit; go 1.19
golang.org/x/tools/cmd/bisect
golang.org/x/tools/cover
golang.org/x/tools/go/analysis
@ -122,7 +122,9 @@ golang.org/x/tools/internal/aliases
golang.org/x/tools/internal/analysisinternal
golang.org/x/tools/internal/bisect
golang.org/x/tools/internal/facts
golang.org/x/tools/internal/stdlib
golang.org/x/tools/internal/typeparams
golang.org/x/tools/internal/typesinternal
golang.org/x/tools/internal/versions
# rsc.io/markdown v0.0.0-20240117044121-669d2fdf1650
## explicit; go 1.20

3
src/cmd/vet/testdata/rangeloop/go.mod vendored Normal file
View File

@ -0,0 +1,3 @@
module rangeloop
go 1.21

View File

@ -78,7 +78,6 @@ func TestVet(t *testing.T) {
"method",
"nilfunc",
"print",
"rangeloop",
"shift",
"slog",
"structtag",
@ -120,6 +119,39 @@ func TestVet(t *testing.T) {
errchk(cmd, files, t)
})
}
// The loopclosure analyzer (aka "rangeloop" before CL 140578)
// is a no-op for files whose version >= go1.22, so we use a
// go.mod file in the rangeloop directory to "downgrade".
//
// TOOD(adonovan): delete when go1.21 goes away.
t.Run("loopclosure", func(t *testing.T) {
cmd := testenv.Command(t, testenv.GoToolPath(t), "vet", "-vettool="+vetPath(t), ".")
cmd.Env = append(os.Environ(), "GOWORK=off")
cmd.Dir = "testdata/rangeloop"
cmd.Stderr = new(strings.Builder) // all vet output goes to stderr
cmd.Run()
stderr := cmd.Stderr.(fmt.Stringer).String()
filename := filepath.FromSlash("testdata/rangeloop/rangeloop.go")
// Unlike the tests above, which runs vet in cmd/vet/, this one
// runs it in subdirectory, so the "full names" in the output
// are in fact short "./rangeloop.go".
// But we can't just pass "./rangeloop.go" as the "full name"
// argument to errorCheck as it does double duty as both a
// string that appears in the output, and as file name
// openable relative to the test directory, containing text
// expectations.
//
// So, we munge the file.
stderr = strings.ReplaceAll(stderr, filepath.FromSlash("./rangeloop.go"), filename)
if err := errorCheck(stderr, false, filename, filepath.Base(filename)); err != nil {
t.Errorf("error check failed: %s", err)
t.Log("vet stderr:\n", cmd.Stderr)
}
})
}
func cgoEnabled(t *testing.T) bool {