1
0
mirror of https://github.com/golang/go synced 2024-11-11 17:51:49 -07:00

vendor/golang.org/x/tools: update to v0.24.1-0.20240904143311-70f56264139c

Among other things, this should fix a regression in printf
whereby materialized aliases caused "any" and "interface{}"
in printf signatures not to be recognized as identical.

It also updates ureader.go used by vendored x/tools during
some tests, including cmd/internal/moddeps.TestAllDependencies.
This test uses golang.org/x/tools/cmd/bundle which uses x/reader.

Fixes #68796

Change-Id: I9f0711e66a5c4daaffe695c515aea3b8fb3d01e0
Reviewed-on: https://go-review.googlesource.com/c/go/+/610736
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
Reviewed-by: Tim King <taking@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Alan Donovan 2024-09-04 12:24:55 -04:00 committed by Gopher Robot
parent 004686b6e5
commit de0aafa3c7
18 changed files with 299 additions and 101 deletions

View File

@ -11,7 +11,7 @@ require (
golang.org/x/sys v0.23.0 golang.org/x/sys v0.23.0
golang.org/x/telemetry v0.0.0-20240828202201-a797f331ea97 golang.org/x/telemetry v0.0.0-20240828202201-a797f331ea97
golang.org/x/term v0.22.1-0.20240716160707-d4346f0be292 golang.org/x/term v0.22.1-0.20240716160707-d4346f0be292
golang.org/x/tools v0.23.1-0.20240722161640-ec1a81bfec7c golang.org/x/tools v0.24.1-0.20240904143311-70f56264139c
) )
require ( require (

View File

@ -22,7 +22,7 @@ golang.org/x/term v0.22.1-0.20240716160707-d4346f0be292 h1:BOrQi08eIX3cDgGcMgFON
golang.org/x/term v0.22.1-0.20240716160707-d4346f0be292/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/term v0.22.1-0.20240716160707-d4346f0be292/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/text v0.16.1-0.20240716160804-ae0cf96bbcd9 h1:MlCLrwVF1WvXT14xTzwuKN3u4LpUve8sG/gJUCuBpe8= golang.org/x/text v0.16.1-0.20240716160804-ae0cf96bbcd9 h1:MlCLrwVF1WvXT14xTzwuKN3u4LpUve8sG/gJUCuBpe8=
golang.org/x/text v0.16.1-0.20240716160804-ae0cf96bbcd9/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.16.1-0.20240716160804-ae0cf96bbcd9/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/tools v0.23.1-0.20240722161640-ec1a81bfec7c h1:jGHQpjWnvPa5qmjhxUYeut+TlRYWRGaMcOnAZ4S5IOo= golang.org/x/tools v0.24.1-0.20240904143311-70f56264139c h1:JImdv91aqIPqamNg5sOTUjNQD++5KkvchZi2BcYlNoE=
golang.org/x/tools v0.23.1-0.20240722161640-ec1a81bfec7c/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/tools v0.24.1-0.20240904143311-70f56264139c/go.mod h1:IV2Kidsnn7A8K7hHxn/wcUfHXkViw0LLHdu8LnpT8LU=
rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef h1:mqLYrXCXYEZOop9/Dbo6RPX11539nwiCNBb1icVPmw8= rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef h1:mqLYrXCXYEZOop9/Dbo6RPX11539nwiCNBb1icVPmw8=
rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef/go.mod h1:8xcPgWmwlZONN1D9bjxtHEjrUtSEa3fakVF8iaewYKQ= rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef/go.mod h1:8xcPgWmwlZONN1D9bjxtHEjrUtSEa3fakVF8iaewYKQ=

View File

@ -100,6 +100,8 @@ type Pass struct {
TypesSizes types.Sizes // function for computing sizes of types TypesSizes types.Sizes // function for computing sizes of types
TypeErrors []types.Error // type errors (only if Analyzer.RunDespiteErrors) TypeErrors []types.Error // type errors (only if Analyzer.RunDespiteErrors)
Module *Module // the package's enclosing module (possibly nil in some drivers)
// Report reports a Diagnostic, a finding about a specific location // Report reports a Diagnostic, a finding about a specific location
// in the analyzed source code such as a potential mistake. // in the analyzed source code such as a potential mistake.
// It may be called by the Run function. // It may be called by the Run function.
@ -238,3 +240,10 @@ func (pass *Pass) String() string {
type Fact interface { type Fact interface {
AFact() // dummy method to avoid type errors AFact() // dummy method to avoid type errors
} }
// A Module describes the module to which a package belongs.
type Module struct {
Path string // module path
Version string // module version ("" if unknown, such as for workspace modules)
GoVersion string // go version used in module (e.g. "go1.22.0")
}

View File

@ -15,6 +15,7 @@ import (
"golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/internal/versions"
) )
const Doc = "check //go:build and // +build directives" const Doc = "check //go:build and // +build directives"
@ -264,6 +265,8 @@ func (check *checker) goBuildLine(pos token.Pos, line string) {
return return
} }
check.tags(pos, x)
if check.goBuild == nil { if check.goBuild == nil {
check.goBuild = x check.goBuild = x
} }
@ -323,6 +326,8 @@ func (check *checker) plusBuildLine(pos token.Pos, line string) {
check.crossCheck = false check.crossCheck = false
return return
} }
check.tags(pos, y)
if check.plusBuild == nil { if check.plusBuild == nil {
check.plusBuild = y check.plusBuild = y
} else { } else {
@ -363,3 +368,51 @@ func (check *checker) finish() {
return return
} }
} }
// tags reports issues in go versions in tags within the expression e.
func (check *checker) tags(pos token.Pos, e constraint.Expr) {
// Check that constraint.GoVersion is meaningful (>= go1.21).
if versions.ConstraintGoVersion == nil {
return
}
// Use Eval to visit each tag.
_ = e.Eval(func(tag string) bool {
if malformedGoTag(tag) {
check.pass.Reportf(pos, "invalid go version %q in build constraint", tag)
}
return false // result is immaterial as Eval does not short-circuit
})
}
// malformedGoTag returns true if a tag is likely to be a malformed
// go version constraint.
func malformedGoTag(tag string) bool {
// Not a go version?
if !strings.HasPrefix(tag, "go1") {
// Check for close misspellings of the "go1." prefix.
for _, pre := range []string{"go.", "g1.", "go"} {
suffix := strings.TrimPrefix(tag, pre)
if suffix != tag {
if valid, ok := validTag("go1." + suffix); ok && valid {
return true
}
}
}
return false
}
// The tag starts with "go1" so it is almost certainly a GoVersion.
// Report it if it is not a valid build constraint.
valid, ok := validTag(tag)
return ok && !valid
}
// validTag returns (valid, ok) where valid reports when a tag is valid,
// and ok reports determining if the tag is valid succeeded.
func validTag(tag string) (valid bool, ok bool) {
if versions.ConstraintGoVersion != nil {
return versions.ConstraintGoVersion(&constraint.TagExpr{Tag: tag}) != "", true
}
return false, false
}

View File

@ -20,6 +20,7 @@ import (
"golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/versions"
) )
const Doc = `check for locks erroneously passed by value const Doc = `check for locks erroneously passed by value
@ -40,18 +41,25 @@ var Analyzer = &analysis.Analyzer{
func run(pass *analysis.Pass) (interface{}, error) { func run(pass *analysis.Pass) (interface{}, error) {
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
var goversion string // effective file version ("" => unknown)
nodeFilter := []ast.Node{ nodeFilter := []ast.Node{
(*ast.AssignStmt)(nil), (*ast.AssignStmt)(nil),
(*ast.CallExpr)(nil), (*ast.CallExpr)(nil),
(*ast.CompositeLit)(nil), (*ast.CompositeLit)(nil),
(*ast.File)(nil),
(*ast.FuncDecl)(nil), (*ast.FuncDecl)(nil),
(*ast.FuncLit)(nil), (*ast.FuncLit)(nil),
(*ast.GenDecl)(nil), (*ast.GenDecl)(nil),
(*ast.RangeStmt)(nil), (*ast.RangeStmt)(nil),
(*ast.ReturnStmt)(nil), (*ast.ReturnStmt)(nil),
} }
inspect.Preorder(nodeFilter, func(node ast.Node) { inspect.WithStack(nodeFilter, func(node ast.Node, push bool, stack []ast.Node) bool {
if !push {
return false
}
switch node := node.(type) { switch node := node.(type) {
case *ast.File:
goversion = versions.FileVersion(pass.TypesInfo, node)
case *ast.RangeStmt: case *ast.RangeStmt:
checkCopyLocksRange(pass, node) checkCopyLocksRange(pass, node)
case *ast.FuncDecl: case *ast.FuncDecl:
@ -61,7 +69,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
case *ast.CallExpr: case *ast.CallExpr:
checkCopyLocksCallExpr(pass, node) checkCopyLocksCallExpr(pass, node)
case *ast.AssignStmt: case *ast.AssignStmt:
checkCopyLocksAssign(pass, node) checkCopyLocksAssign(pass, node, goversion, parent(stack))
case *ast.GenDecl: case *ast.GenDecl:
checkCopyLocksGenDecl(pass, node) checkCopyLocksGenDecl(pass, node)
case *ast.CompositeLit: case *ast.CompositeLit:
@ -69,16 +77,36 @@ func run(pass *analysis.Pass) (interface{}, error) {
case *ast.ReturnStmt: case *ast.ReturnStmt:
checkCopyLocksReturnStmt(pass, node) checkCopyLocksReturnStmt(pass, node)
} }
return true
}) })
return nil, nil return nil, nil
} }
// checkCopyLocksAssign checks whether an assignment // checkCopyLocksAssign checks whether an assignment
// copies a lock. // copies a lock.
func checkCopyLocksAssign(pass *analysis.Pass, as *ast.AssignStmt) { func checkCopyLocksAssign(pass *analysis.Pass, assign *ast.AssignStmt, goversion string, parent ast.Node) {
for i, x := range as.Rhs { lhs := assign.Lhs
for i, x := range assign.Rhs {
if path := lockPathRhs(pass, x); path != nil { if path := lockPathRhs(pass, x); path != nil {
pass.ReportRangef(x, "assignment copies lock value to %v: %v", analysisutil.Format(pass.Fset, as.Lhs[i]), path) pass.ReportRangef(x, "assignment copies lock value to %v: %v", analysisutil.Format(pass.Fset, assign.Lhs[i]), path)
lhs = nil // An lhs has been reported. We prefer the assignment warning and do not report twice.
}
}
// After GoVersion 1.22, loop variables are implicitly copied on each iteration.
// So a for statement may inadvertently copy a lock when any of the
// iteration variables contain locks.
if assign.Tok == token.DEFINE && versions.AtLeast(goversion, versions.Go1_22) {
if parent, _ := parent.(*ast.ForStmt); parent != nil && parent.Init == assign {
for _, l := range lhs {
if id, ok := l.(*ast.Ident); ok && id.Name != "_" {
if obj := pass.TypesInfo.Defs[id]; obj != nil && obj.Type() != nil {
if path := lockPath(pass.Pkg, obj.Type(), nil); path != nil {
pass.ReportRangef(l, "for loop iteration copies lock value to %v: %v", analysisutil.Format(pass.Fset, l), path)
}
}
}
}
} }
} }
} }
@ -340,6 +368,14 @@ func lockPath(tpkg *types.Package, typ types.Type, seen map[types.Type]bool) typ
return nil return nil
} }
// parent returns the second from the last node on stack if it exists.
func parent(stack []ast.Node) ast.Node {
if len(stack) >= 2 {
return stack[len(stack)-2]
}
return nil
}
var lockerType *types.Interface var lockerType *types.Interface
// Construct a sync.Locker interface type. // Construct a sync.Locker interface type.

View File

@ -45,6 +45,18 @@
// //
// log.Print("%d", 123) // log.Print call has possible formatting directive %d // log.Print("%d", 123) // log.Print call has possible formatting directive %d
// //
// Conversely, it also reports calls to Printf-like functions with a
// non-constant format string and no other arguments:
//
// fmt.Printf(message) // non-constant format string in call to fmt.Printf
//
// Such calls may have been intended for the function's Print-like
// counterpart: if the value of message happens to contain "%",
// misformatting will occur. In this case, the checker additionally
// suggests a fix to turn the call into:
//
// fmt.Printf("%s", message)
//
// # Inferred printf wrappers // # Inferred printf wrappers
// //
// Functions that delegate their arguments to fmt.Printf are // Functions that delegate their arguments to fmt.Printf are

View File

@ -159,10 +159,11 @@ func maybePrintfWrapper(info *types.Info, decl ast.Decl) *printfWrapper {
params := sig.Params() params := sig.Params()
nparams := params.Len() // variadic => nonzero nparams := params.Len() // variadic => nonzero
// Check final parameter is "args ...interface{}".
args := params.At(nparams - 1) args := params.At(nparams - 1)
iface, ok := args.Type().(*types.Slice).Elem().(*types.Interface) iface, ok := aliases.Unalias(args.Type().(*types.Slice).Elem()).(*types.Interface)
if !ok || !iface.Empty() { if !ok || !iface.Empty() {
return nil // final (args) param is not ...interface{} return nil
} }
// Is second last param 'format string'? // Is second last param 'format string'?
@ -372,64 +373,29 @@ var isPrint = stringSet{
"(testing.TB).Skipf": true, "(testing.TB).Skipf": true,
} }
// formatString returns the format string argument and its index within // formatStringIndex returns the index of the format string (the last
// the given printf-like call expression. // non-variadic parameter) within the given printf-like call
// // expression, or -1 if unknown.
// The last parameter before variadic arguments is assumed to be func formatStringIndex(pass *analysis.Pass, call *ast.CallExpr) int {
// a format string.
//
// The first string literal or string constant is assumed to be a format string
// if the call's signature cannot be determined.
//
// If it cannot find any format string parameter, it returns ("", -1).
func formatString(pass *analysis.Pass, call *ast.CallExpr) (format string, idx int) {
typ := pass.TypesInfo.Types[call.Fun].Type typ := pass.TypesInfo.Types[call.Fun].Type
if typ != nil { if typ == nil {
if sig, ok := typ.(*types.Signature); ok { return -1 // missing type
if !sig.Variadic() {
// Skip checking non-variadic functions.
return "", -1
}
idx := sig.Params().Len() - 2
if idx < 0 {
// Skip checking variadic functions without
// fixed arguments.
return "", -1
}
s, ok := stringConstantArg(pass, call, idx)
if !ok {
// The last argument before variadic args isn't a string.
return "", -1
}
return s, idx
}
} }
sig, ok := typ.(*types.Signature)
// Cannot determine call's signature. Fall back to scanning for the first if !ok {
// string constant in the call. return -1 // ill-typed
for idx := range call.Args {
if s, ok := stringConstantArg(pass, call, idx); ok {
return s, idx
}
if pass.TypesInfo.Types[call.Args[idx]].Type == types.Typ[types.String] {
// Skip checking a call with a non-constant format
// string argument, since its contents are unavailable
// for validation.
return "", -1
}
} }
return "", -1 if !sig.Variadic() {
} // Skip checking non-variadic functions.
return -1
// stringConstantArg returns call's string constant argument at the index idx.
//
// ("", false) is returned if call's argument at the index idx isn't a string
// constant.
func stringConstantArg(pass *analysis.Pass, call *ast.CallExpr, idx int) (string, bool) {
if idx >= len(call.Args) {
return "", false
} }
return stringConstantExpr(pass, call.Args[idx]) idx := sig.Params().Len() - 2
if idx < 0 {
// Skip checking variadic functions without
// fixed arguments.
return -1
}
return idx
} }
// stringConstantExpr returns expression's string constant value. // stringConstantExpr returns expression's string constant value.
@ -536,10 +502,34 @@ type formatState struct {
// checkPrintf checks a call to a formatted print routine such as Printf. // checkPrintf checks a call to a formatted print routine such as Printf.
func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.Func) { func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.Func) {
format, idx := formatString(pass, call) idx := formatStringIndex(pass, call)
if idx < 0 { if idx < 0 || idx >= len(call.Args) {
if false { return
pass.Reportf(call.Lparen, "can't check non-constant format in call to %s", fn.FullName()) }
formatArg := call.Args[idx]
format, ok := stringConstantExpr(pass, formatArg)
if !ok {
// Format string argument is non-constant.
// It is a common mistake to call fmt.Printf(msg) with a
// non-constant format string and no arguments:
// if msg contains "%", misformatting occurs.
// Report the problem and suggest a fix: fmt.Printf("%s", msg).
if idx == len(call.Args)-1 {
pass.Report(analysis.Diagnostic{
Pos: formatArg.Pos(),
End: formatArg.End(),
Message: fmt.Sprintf("non-constant format string in call to %s",
fn.FullName()),
SuggestedFixes: []analysis.SuggestedFix{{
Message: `Insert "%s" format string`,
TextEdits: []analysis.TextEdit{{
Pos: formatArg.Pos(),
End: formatArg.Pos(),
NewText: []byte(`"%s", `),
}},
}},
})
} }
return return
} }

View File

@ -6,7 +6,6 @@ package tests
import ( import (
_ "embed" _ "embed"
"fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types" "go/types"
@ -184,13 +183,13 @@ func checkAddCalls(pass *analysis.Pass, fn *ast.FuncDecl, params *types.Tuple) {
i := mismatched[0] i := mismatched[0]
expr := call.Args[i] expr := call.Args[i]
t := pass.TypesInfo.Types[expr].Type t := pass.TypesInfo.Types[expr].Type
pass.ReportRangef(expr, fmt.Sprintf("mismatched type in call to (*testing.F).Add: %v, fuzz target expects %v", t, params.At(i+1).Type())) pass.ReportRangef(expr, "mismatched type in call to (*testing.F).Add: %v, fuzz target expects %v", t, params.At(i+1).Type())
} else if len(mismatched) > 1 { } else if len(mismatched) > 1 {
var gotArgs, wantArgs []types.Type var gotArgs, wantArgs []types.Type
for i := 0; i < len(call.Args); i++ { for i := 0; i < len(call.Args); i++ {
gotArgs, wantArgs = append(gotArgs, pass.TypesInfo.Types[call.Args[i]].Type), append(wantArgs, params.At(i+1).Type()) gotArgs, wantArgs = append(gotArgs, pass.TypesInfo.Types[call.Args[i]].Type), append(wantArgs, params.At(i+1).Type())
} }
pass.ReportRangef(call, fmt.Sprintf("mismatched types in call to (*testing.F).Add: %v, fuzz target expects %v", gotArgs, wantArgs)) pass.ReportRangef(call, "mismatched types in call to (*testing.F).Add: %v, fuzz target expects %v", gotArgs, wantArgs)
} }
} }
return true return true
@ -244,7 +243,7 @@ func validateFuzzArgs(pass *analysis.Pass, params *types.Tuple, expr ast.Expr) b
} }
} }
} }
pass.ReportRangef(exprRange, "fuzzing arguments can only have the following types: "+formatAcceptedFuzzType()) pass.ReportRangef(exprRange, "fuzzing arguments can only have the following types: %s", formatAcceptedFuzzType())
ok = false ok = false
} }
} }

View File

@ -66,6 +66,8 @@ type Config struct {
GoFiles []string GoFiles []string
NonGoFiles []string NonGoFiles []string
IgnoredFiles []string IgnoredFiles []string
ModulePath string // module path
ModuleVersion string // module version
ImportMap map[string]string // maps import path to package path ImportMap map[string]string // maps import path to package path
PackageFile map[string]string // maps package path to file of type information PackageFile map[string]string // maps package path to file of type information
Standard map[string]bool // package belongs to standard library Standard map[string]bool // package belongs to standard library
@ -359,6 +361,12 @@ func run(fset *token.FileSet, cfg *Config, analyzers []*analysis.Analyzer) ([]re
factFilter[reflect.TypeOf(f)] = true factFilter[reflect.TypeOf(f)] = true
} }
module := &analysis.Module{
Path: cfg.ModulePath,
Version: cfg.ModuleVersion,
GoVersion: cfg.GoVersion,
}
pass := &analysis.Pass{ pass := &analysis.Pass{
Analyzer: a, Analyzer: a,
Fset: fset, Fset: fset,
@ -377,6 +385,7 @@ func run(fset *token.FileSet, cfg *Config, analyzers []*analysis.Analyzer) ([]re
ImportPackageFact: facts.ImportPackageFact, ImportPackageFact: facts.ImportPackageFact,
ExportPackageFact: facts.ExportPackageFact, ExportPackageFact: facts.ExportPackageFact,
AllPackageFacts: func() []analysis.PackageFact { return facts.AllPackageFacts(factFilter) }, AllPackageFacts: func() []analysis.PackageFact { return facts.AllPackageFacts(factFilter) },
Module: module,
} }
pass.ReadFile = analysisinternal.MakeReadFile(pass) pass.ReadFile = analysisinternal.MakeReadFile(pass)

View File

@ -51,7 +51,7 @@ type Path string
// //
// PO package->object Package.Scope.Lookup // PO package->object Package.Scope.Lookup
// OT object->type Object.Type // OT object->type Object.Type
// TT type->type Type.{Elem,Key,{,{,Recv}Type}Params,Results,Underlying} [EKPRUTrC] // TT type->type Type.{Elem,Key,{,{,Recv}Type}Params,Results,Underlying,Rhs} [EKPRUTrCa]
// TO type->object Type.{At,Field,Method,Obj} [AFMO] // TO type->object Type.{At,Field,Method,Obj} [AFMO]
// //
// All valid paths start with a package and end at an object // All valid paths start with a package and end at an object
@ -63,7 +63,7 @@ type Path string
// - The only PO operator is Package.Scope.Lookup, which requires an identifier. // - The only PO operator is Package.Scope.Lookup, which requires an identifier.
// - The only OT operator is Object.Type, // - The only OT operator is Object.Type,
// which we encode as '.' because dot cannot appear in an identifier. // which we encode as '.' because dot cannot appear in an identifier.
// - The TT operators are encoded as [EKPRUTrC]; // - The TT operators are encoded as [EKPRUTrCa];
// two of these ({,Recv}TypeParams) require an integer operand, // two of these ({,Recv}TypeParams) require an integer operand,
// which is encoded as a string of decimal digits. // which is encoded as a string of decimal digits.
// - The TO operators are encoded as [AFMO]; // - The TO operators are encoded as [AFMO];
@ -106,6 +106,7 @@ const (
opTypeParam = 'T' // .TypeParams.At(i) (Named, Signature) opTypeParam = 'T' // .TypeParams.At(i) (Named, Signature)
opRecvTypeParam = 'r' // .RecvTypeParams.At(i) (Signature) opRecvTypeParam = 'r' // .RecvTypeParams.At(i) (Signature)
opConstraint = 'C' // .Constraint() (TypeParam) opConstraint = 'C' // .Constraint() (TypeParam)
opRhs = 'a' // .Rhs() (Alias)
// type->object operators // type->object operators
opAt = 'A' // .At(i) (Tuple) opAt = 'A' // .At(i) (Tuple)
@ -279,21 +280,26 @@ func (enc *Encoder) For(obj types.Object) (Path, error) {
path = append(path, opType) path = append(path, opType)
T := o.Type() T := o.Type()
if alias, ok := T.(*aliases.Alias); ok {
if r := findTypeParam(obj, aliases.TypeParams(alias), path, opTypeParam, nil); r != nil {
return Path(r), nil
}
if r := find(obj, aliases.Rhs(alias), append(path, opRhs), nil); r != nil {
return Path(r), nil
}
if tname.IsAlias() { } else if tname.IsAlias() {
// type alias // legacy alias
if r := find(obj, T, path, nil); r != nil { if r := find(obj, T, path, nil); r != nil {
return Path(r), nil return Path(r), nil
} }
} else {
if named, _ := T.(*types.Named); named != nil { } else if named, ok := T.(*types.Named); ok {
if r := findTypeParam(obj, named.TypeParams(), path, opTypeParam, nil); r != nil {
// generic named type
return Path(r), nil
}
}
// defined (named) type // defined (named) type
if r := find(obj, T.Underlying(), append(path, opUnderlying), nil); r != nil { if r := findTypeParam(obj, named.TypeParams(), path, opTypeParam, nil); r != nil {
return Path(r), nil
}
if r := find(obj, named.Underlying(), append(path, opUnderlying), nil); r != nil {
return Path(r), nil return Path(r), nil
} }
} }
@ -657,6 +663,16 @@ func Object(pkg *types.Package, p Path) (types.Object, error) {
} }
t = named.Underlying() t = named.Underlying()
case opRhs:
if alias, ok := t.(*aliases.Alias); ok {
t = aliases.Rhs(alias)
} else if false && aliases.Enabled() {
// The Enabled check is too expensive, so for now we
// simply assume that aliases are not enabled.
// TODO(adonovan): replace with "if true {" when go1.24 is assured.
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want alias)", code, t, t)
}
case opTypeParam: case opTypeParam:
hasTypeParams, ok := t.(hasTypeParams) // Named, Signature hasTypeParams, ok := t.(hasTypeParams) // Named, Signature
if !ok { if !ok {

View File

@ -22,11 +22,17 @@ import (
// GODEBUG=gotypesalias=... by invoking the type checker. The Enabled // GODEBUG=gotypesalias=... by invoking the type checker. The Enabled
// function is expensive and should be called once per task (e.g. // function is expensive and should be called once per task (e.g.
// package import), not once per call to NewAlias. // package import), not once per call to NewAlias.
func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type) *types.TypeName { //
// Precondition: enabled || len(tparams)==0.
// If materialized aliases are disabled, there must not be any type parameters.
func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type, tparams []*types.TypeParam) *types.TypeName {
if enabled { if enabled {
tname := types.NewTypeName(pos, pkg, name, nil) tname := types.NewTypeName(pos, pkg, name, nil)
newAlias(tname, rhs) newAlias(tname, rhs, tparams)
return tname return tname
} }
if len(tparams) > 0 {
panic("cannot create an alias with type parameters when gotypesalias is not enabled")
}
return types.NewTypeName(pos, pkg, name, rhs) return types.NewTypeName(pos, pkg, name, rhs)
} }

View File

@ -15,15 +15,21 @@ import (
// It will never be created by go/types. // It will never be created by go/types.
type Alias struct{} type Alias struct{}
func (*Alias) String() string { panic("unreachable") } func (*Alias) String() string { panic("unreachable") }
func (*Alias) Underlying() types.Type { panic("unreachable") } func (*Alias) Underlying() types.Type { panic("unreachable") }
func (*Alias) Obj() *types.TypeName { panic("unreachable") } func (*Alias) Obj() *types.TypeName { panic("unreachable") }
func Rhs(alias *Alias) types.Type { panic("unreachable") } func Rhs(alias *Alias) types.Type { panic("unreachable") }
func TypeParams(alias *Alias) *types.TypeParamList { panic("unreachable") }
func SetTypeParams(alias *Alias, tparams []*types.TypeParam) { panic("unreachable") }
func TypeArgs(alias *Alias) *types.TypeList { panic("unreachable") }
func Origin(alias *Alias) *Alias { panic("unreachable") }
// Unalias returns the type t for go <=1.21. // Unalias returns the type t for go <=1.21.
func Unalias(t types.Type) types.Type { return t } func Unalias(t types.Type) types.Type { return t }
func newAlias(name *types.TypeName, rhs types.Type) *Alias { panic("unreachable") } func newAlias(name *types.TypeName, rhs types.Type, tparams []*types.TypeParam) *Alias {
panic("unreachable")
}
// Enabled reports whether [NewAlias] should create [types.Alias] types. // Enabled reports whether [NewAlias] should create [types.Alias] types.
// //

View File

@ -28,16 +28,51 @@ func Rhs(alias *Alias) types.Type {
return Unalias(alias) return Unalias(alias)
} }
// TypeParams returns the type parameter list of the alias.
func TypeParams(alias *Alias) *types.TypeParamList {
if alias, ok := any(alias).(interface{ TypeParams() *types.TypeParamList }); ok {
return alias.TypeParams() // go1.23+
}
return nil
}
// SetTypeParams sets the type parameters of the alias type.
func SetTypeParams(alias *Alias, tparams []*types.TypeParam) {
if alias, ok := any(alias).(interface {
SetTypeParams(tparams []*types.TypeParam)
}); ok {
alias.SetTypeParams(tparams) // go1.23+
} else if len(tparams) > 0 {
panic("cannot set type parameters of an Alias type in go1.22")
}
}
// TypeArgs returns the type arguments used to instantiate the Alias type.
func TypeArgs(alias *Alias) *types.TypeList {
if alias, ok := any(alias).(interface{ TypeArgs() *types.TypeList }); ok {
return alias.TypeArgs() // go1.23+
}
return nil // empty (go1.22)
}
// Origin returns the generic Alias type of which alias is an instance.
// If alias is not an instance of a generic alias, Origin returns alias.
func Origin(alias *Alias) *Alias {
if alias, ok := any(alias).(interface{ Origin() *types.Alias }); ok {
return alias.Origin() // go1.23+
}
return alias // not an instance of a generic alias (go1.22)
}
// Unalias is a wrapper of types.Unalias. // Unalias is a wrapper of types.Unalias.
func Unalias(t types.Type) types.Type { return types.Unalias(t) } func Unalias(t types.Type) types.Type { return types.Unalias(t) }
// newAlias is an internal alias around types.NewAlias. // newAlias is an internal alias around types.NewAlias.
// Direct usage is discouraged as the moment. // Direct usage is discouraged as the moment.
// Try to use NewAlias instead. // Try to use NewAlias instead.
func newAlias(tname *types.TypeName, rhs types.Type) *Alias { func newAlias(tname *types.TypeName, rhs types.Type, tparams []*types.TypeParam) *Alias {
a := types.NewAlias(tname, rhs) a := types.NewAlias(tname, rhs)
// TODO(go.dev/issue/65455): Remove kludgy workaround to set a.actual as a side-effect. SetTypeParams(a, tparams)
Unalias(a)
return a return a
} }

View File

@ -951,7 +951,7 @@ var PackageSymbols = map[string][]Symbol{
{"ParseSessionState", Func, 21}, {"ParseSessionState", Func, 21},
{"QUICClient", Func, 21}, {"QUICClient", Func, 21},
{"QUICConfig", Type, 21}, {"QUICConfig", Type, 21},
{"QUICConfig.EnableStoreSessionEvent", Field, 23}, {"QUICConfig.EnableSessionEvents", Field, 23},
{"QUICConfig.TLSConfig", Field, 21}, {"QUICConfig.TLSConfig", Field, 21},
{"QUICConn", Type, 21}, {"QUICConn", Type, 21},
{"QUICEncryptionLevel", Type, 21}, {"QUICEncryptionLevel", Type, 21},

View File

@ -838,7 +838,7 @@ const (
// InvalidCap occurs when an argument to the cap built-in function is not of // InvalidCap occurs when an argument to the cap built-in function is not of
// supported type. // supported type.
// //
// See https://golang.org/ref/spec#Lengthand_capacity for information on // See https://golang.org/ref/spec#Length_and_capacity for information on
// which underlying types are supported as arguments to cap and len. // which underlying types are supported as arguments to cap and len.
// //
// Example: // Example:
@ -859,7 +859,7 @@ const (
// InvalidCopy occurs when the arguments are not of slice type or do not // InvalidCopy occurs when the arguments are not of slice type or do not
// have compatible type. // have compatible type.
// //
// See https://golang.org/ref/spec#Appendingand_copying_slices for more // See https://golang.org/ref/spec#Appending_and_copying_slices for more
// information on the type requirements for the copy built-in. // information on the type requirements for the copy built-in.
// //
// Example: // Example:
@ -897,7 +897,7 @@ const (
// InvalidLen occurs when an argument to the len built-in function is not of // InvalidLen occurs when an argument to the len built-in function is not of
// supported type. // supported type.
// //
// See https://golang.org/ref/spec#Lengthand_capacity for information on // See https://golang.org/ref/spec#Length_and_capacity for information on
// which underlying types are supported as arguments to cap and len. // which underlying types are supported as arguments to cap and len.
// //
// Example: // Example:
@ -914,7 +914,7 @@ const (
// InvalidMake occurs when make is called with an unsupported type argument. // InvalidMake occurs when make is called with an unsupported type argument.
// //
// See https://golang.org/ref/spec#Makingslices_maps_and_channels for // See https://golang.org/ref/spec#Making_slices_maps_and_channels for
// information on the types that may be created using make. // information on the types that may be created using make.
// //
// Example: // Example:

View File

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

View File

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

View File

@ -71,8 +71,8 @@ golang.org/x/text/internal/tag
golang.org/x/text/language golang.org/x/text/language
golang.org/x/text/transform golang.org/x/text/transform
golang.org/x/text/unicode/norm golang.org/x/text/unicode/norm
# golang.org/x/tools v0.23.1-0.20240722161640-ec1a81bfec7c # golang.org/x/tools v0.24.1-0.20240904143311-70f56264139c
## explicit; go 1.19 ## explicit; go 1.22.6
golang.org/x/tools/cmd/bisect golang.org/x/tools/cmd/bisect
golang.org/x/tools/cover golang.org/x/tools/cover
golang.org/x/tools/go/analysis golang.org/x/tools/go/analysis