1
0
mirror of https://github.com/golang/go synced 2024-09-29 07:24:32 -06:00

go/types: generate builtins.go from types2 source

Minor changes to types2.builtin.go to simplify automatic translation.

Added new conversion functions to generate_test.go to handle the
translation of builtins.go.

While at it, added additional helper functions to generate_test.go
and simplified some of the existing conversion functions.

This CL reduces the amount of code that needs to be maintained
manually by about 1000 LOC.

Change-Id: I1bd5c8eda0c0194a0b47e69882d2b987d91eef50
Reviewed-on: https://go-review.googlesource.com/c/go/+/562835
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
This commit is contained in:
Robert Griesemer 2024-02-08 14:29:26 -08:00 committed by Gopher Robot
parent 808c8fb815
commit 81609c454b
5 changed files with 159 additions and 74 deletions

View File

@ -23,8 +23,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
// append is the only built-in that permits the use of ... for the last argument
bin := predeclaredFuncs[id]
if hasDots(call) && id != _Append {
//check.errorf(call.Ellipsis, invalidOp + "invalid use of ... with built-in %s", bin.name)
check.errorf(call,
check.errorf(dddErrPos(call),
InvalidDotDotDot,
invalidOp+"invalid use of ... with built-in %s", bin.name)
check.use(argList...)
@ -76,7 +75,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
msg = "too many"
}
if msg != "" {
check.errorf(call, WrongArgCount, invalidOp+"%s arguments for %v (expected %d, found %d)", msg, call, bin.nargs, nargs)
check.errorf(argErrPos(call), WrongArgCount, invalidOp+"%s arguments for %v (expected %d, found %d)", msg, call, bin.nargs, nargs)
return
}
}

View File

@ -24,5 +24,16 @@ func cmpPos(p, q syntax.Pos) int { return p.Cmp(q) }
// hasDots reports whether the last argument in the call is followed by ...
func hasDots(call *syntax.CallExpr) bool { return call.HasDots }
// dddErrPos returns the node (poser) for reporting an invalid ... use in a call.
func dddErrPos(call *syntax.CallExpr) *syntax.CallExpr {
// TODO(gri) should use "..." instead of call position
return call
}
// argErrPos returns the node (poser) for reportign an invalid argument count.
func argErrPos(call *syntax.CallExpr) *syntax.CallExpr {
return call
}
// ExprString returns a string representation of x.
func ExprString(x syntax.Node) string { return syntax.String(x) }

View File

@ -1,3 +1,5 @@
// Code generated by "go test -run=Generate -write=all"; DO NOT EDIT.
// Copyright 2012 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.
@ -23,7 +25,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
// append is the only built-in that permits the use of ... for the last argument
bin := predeclaredFuncs[id]
if hasDots(call) && id != _Append {
check.errorf(atPos(call.Ellipsis),
check.errorf(dddErrPos(call),
InvalidDotDotDot,
invalidOp+"invalid use of ... with built-in %s", bin.name)
check.use(argList...)
@ -75,7 +77,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
msg = "too many"
}
if msg != "" {
check.errorf(inNode(call, call.Rparen), WrongArgCount, invalidOp+"%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs)
check.errorf(argErrPos(call), WrongArgCount, invalidOp+"%s arguments for %v (expected %d, found %d)", msg, call, bin.nargs, nargs)
return
}
}

View File

@ -99,81 +99,108 @@ var filemap = map[string]action{
"array.go": nil,
"api_predicates.go": nil,
"basic.go": nil,
"chan.go": nil,
"const.go": func(f *ast.File) { fixTokenPos(f) },
"context.go": nil,
"context_test.go": nil,
"errsupport.go": nil,
"gccgosizes.go": nil,
"gcsizes.go": func(f *ast.File) { renameIdents(f, "IsSyncAtomicAlign64->_IsSyncAtomicAlign64") },
"hilbert_test.go": func(f *ast.File) { renameImportPath(f, `"cmd/compile/internal/types2"`, `"go/types"`) },
"infer.go": func(f *ast.File) {
fixTokenPos(f)
fixInferSig(f)
"builtins.go": func(f *ast.File) {
renameImportPath(f, `"cmd/compile/internal/syntax"`, `"go/ast"`)
renameIdents(f, "syntax->ast")
renameSelectors(f, "ArgList->Args")
fixSelValue(f)
fixAtPosCall(f)
},
"chan.go": nil,
"const.go": func(f *ast.File) { fixTokenPos(f) },
"context.go": nil,
"context_test.go": nil,
"errsupport.go": nil,
"gccgosizes.go": nil,
"gcsizes.go": func(f *ast.File) { renameIdents(f, "IsSyncAtomicAlign64->_IsSyncAtomicAlign64") },
"hilbert_test.go": func(f *ast.File) { renameImportPath(f, `"cmd/compile/internal/types2"`, `"go/types"`) },
"infer.go": func(f *ast.File) { fixTokenPos(f); fixInferSig(f) },
// "initorder.go": fixErrErrorfCall, // disabled for now due to unresolved error_ use implications for gopls
"instantiate.go": func(f *ast.File) { fixTokenPos(f); fixCheckErrorfCall(f) },
"instantiate_test.go": func(f *ast.File) { renameImportPath(f, `"cmd/compile/internal/types2"`, `"go/types"`) },
"lookup.go": func(f *ast.File) { fixTokenPos(f) },
"main_test.go": nil,
"map.go": nil,
"named.go": func(f *ast.File) { fixTokenPos(f); fixTraceSel(f) },
"named.go": func(f *ast.File) { fixTokenPos(f); renameSelectors(f, "Trace->_Trace") },
"object.go": func(f *ast.File) { fixTokenPos(f); renameIdents(f, "NewTypeNameLazy->_NewTypeNameLazy") },
"object_test.go": func(f *ast.File) { renameImportPath(f, `"cmd/compile/internal/types2"`, `"go/types"`) },
"objset.go": nil,
"package.go": nil,
"pointer.go": nil,
"predicates.go": nil,
"scope.go": func(f *ast.File) {
fixTokenPos(f)
renameIdents(f, "Squash->squash", "InsertLazy->_InsertLazy")
},
"selection.go": nil,
"sizes.go": func(f *ast.File) { renameIdents(f, "IsSyncAtomicAlign64->_IsSyncAtomicAlign64") },
"slice.go": nil,
"subst.go": func(f *ast.File) { fixTokenPos(f); fixTraceSel(f) },
"termlist.go": nil,
"termlist_test.go": nil,
"tuple.go": nil,
"typelists.go": nil,
"typeparam.go": nil,
"typeterm_test.go": nil,
"typeterm.go": nil,
"under.go": nil,
"unify.go": fixSprintf,
"universe.go": fixGlobalTypVarDecl,
"util_test.go": fixTokenPos,
"validtype.go": nil,
"scope.go": func(f *ast.File) { fixTokenPos(f); renameIdents(f, "Squash->squash", "InsertLazy->_InsertLazy") },
"selection.go": nil,
"sizes.go": func(f *ast.File) { renameIdents(f, "IsSyncAtomicAlign64->_IsSyncAtomicAlign64") },
"slice.go": nil,
"subst.go": func(f *ast.File) { fixTokenPos(f); renameSelectors(f, "Trace->_Trace") },
"termlist.go": nil,
"termlist_test.go": nil,
"tuple.go": nil,
"typelists.go": nil,
"typeparam.go": nil,
"typeterm_test.go": nil,
"typeterm.go": nil,
"under.go": nil,
"unify.go": fixSprintf,
"universe.go": fixGlobalTypVarDecl,
"util_test.go": fixTokenPos,
"validtype.go": nil,
}
// TODO(gri) We should be able to make these rewriters more configurable/composable.
// For now this is a good starting point.
// renameIdent renames identifiers: each renames entry is of the form from->to.
// Note: This doesn't change the use of the identifiers in comments.
func renameIdents(f *ast.File, renames ...string) {
var list [][]string
// A renameMap maps old names to new names.
type renameMap map[string]string
// makeRenameMap returns a renameMap populates from renames entries of the form "from->to".
func makeRenameMap(renames ...string) renameMap {
m := make(renameMap)
for _, r := range renames {
s := strings.Split(r, "->")
if len(s) != 2 {
panic("invalid rename entry: " + r)
}
list = append(list, s)
m[s[0]] = s[1]
}
return m
}
// rename renames the given name if a corresponding rename exists in m.
func (m renameMap) rename(name *string) {
if new, ok := m[*name]; ok {
*name = new
}
}
// renameIdents renames identifiers: each renames entry is of the form "from->to".
// Note: This doesn't change the use of the identifiers in comments.
func renameIdents(f *ast.File, renames ...string) {
m := makeRenameMap(renames...)
ast.Inspect(f, func(n ast.Node) bool {
switch n := n.(type) {
case *ast.Ident:
for _, r := range list {
if n.Name == r[0] {
n.Name = r[1]
}
}
m.rename(&n.Name)
return false
}
return true
})
}
// renameSelectors is like renameIdents but only looks at selectors.
func renameSelectors(f *ast.File, renames ...string) {
m := makeRenameMap(renames...)
ast.Inspect(f, func(n ast.Node) bool {
switch n := n.(type) {
case *ast.SelectorExpr:
m.rename(&n.Sel.Name)
return false
}
return true
})
}
// renameImportPath renames an import path.
func renameImportPath(f *ast.File, from, to string) {
ast.Inspect(f, func(n ast.Node) bool {
@ -201,7 +228,7 @@ func fixTokenPos(f *ast.File) {
}
case *ast.SelectorExpr:
// rewrite syntax.Pos to token.Pos
if x, _ := n.X.(*ast.Ident); x != nil && x.Name == "syntax" && n.Sel.Name == "Pos" {
if x := asIdent(n.X, "syntax"); x != nil && n.Sel.Name == "Pos" {
x.Name = "token"
return false
}
@ -216,6 +243,22 @@ func fixTokenPos(f *ast.File) {
})
}
// fixSelValue updates the selector Sel.Value to Sel.Name.
func fixSelValue(f *ast.File) {
ast.Inspect(f, func(n ast.Node) bool {
switch n := n.(type) {
case *ast.SelectorExpr:
if n.Sel.Name == "Value" {
if selx, _ := n.X.(*ast.SelectorExpr); selx != nil && selx.Sel.Name == "Sel" {
n.Sel.Name = "Name"
return false
}
}
}
return true
})
}
// fixInferSig updates the Checker.infer signature to use a positioner instead of a token.Position
// as first argument, renames the argument from "pos" to "posn", and updates a few internal uses of
// "pos" to "posn" and "posn.Pos()" respectively.
@ -237,7 +280,7 @@ func fixInferSig(f *ast.File) {
switch selx.Sel.Name {
case "renameTParams":
// rewrite check.renameTParams(pos, ... ) to check.renameTParams(posn.Pos(), ... )
if ident, _ := n.Args[0].(*ast.Ident); ident != nil && ident.Name == "pos" {
if isIdent(n.Args[0], "pos") {
pos := n.Args[0].Pos()
fun := &ast.SelectorExpr{X: newIdent(pos, "posn"), Sel: newIdent(pos, "Pos")}
arg := &ast.CallExpr{Fun: fun, Lparen: pos, Args: nil, Ellipsis: token.NoPos, Rparen: pos}
@ -246,7 +289,7 @@ func fixInferSig(f *ast.File) {
}
case "errorf":
// rewrite check.errorf(pos, ...) to check.errorf(posn, ...)
if ident, _ := n.Args[0].(*ast.Ident); ident != nil && ident.Name == "pos" {
if isIdent(n.Args[0], "pos") {
pos := n.Args[0].Pos()
arg := newIdent(pos, "posn")
n.Args[0] = arg
@ -254,7 +297,7 @@ func fixInferSig(f *ast.File) {
}
case "allowVersion":
// rewrite check.allowVersion(..., pos, ...) to check.allowVersion(..., posn, ...)
if ident, _ := n.Args[1].(*ast.Ident); ident != nil && ident.Name == "pos" {
if isIdent(n.Args[1], "pos") {
pos := n.Args[1].Pos()
arg := newIdent(pos, "posn")
n.Args[1] = arg
@ -267,21 +310,44 @@ func fixInferSig(f *ast.File) {
})
}
// fixAtPosCall updates calls of the form atPos(x) to x.Pos() in argument lists of (check).dump calls.
// TODO(gri) can we avoid this and just use atPos consistently in go/types and types2?
func fixAtPosCall(f *ast.File) {
ast.Inspect(f, func(n ast.Node) bool {
switch n := n.(type) {
case *ast.CallExpr:
if selx, _ := n.Fun.(*ast.SelectorExpr); selx != nil && selx.Sel.Name == "dump" {
for i, arg := range n.Args {
if call, _ := arg.(*ast.CallExpr); call != nil {
// rewrite xxx.dump(..., atPos(x), ...) to xxx.dump(..., x.Pos(), ...)
if isIdent(call.Fun, "atPos") {
pos := call.Args[0].Pos()
fun := &ast.SelectorExpr{X: call.Args[0], Sel: newIdent(pos, "Pos")}
n.Args[i] = &ast.CallExpr{Fun: fun, Lparen: pos, Rparen: pos}
return false
}
}
}
}
}
return true
})
}
// fixErrErrorfCall updates calls of the form err.errorf(obj, ...) to err.errorf(obj.Pos(), ...).
func fixErrErrorfCall(f *ast.File) {
ast.Inspect(f, func(n ast.Node) bool {
switch n := n.(type) {
case *ast.CallExpr:
if selx, _ := n.Fun.(*ast.SelectorExpr); selx != nil {
if ident, _ := selx.X.(*ast.Ident); ident != nil && ident.Name == "err" {
if isIdent(selx.X, "err") {
switch selx.Sel.Name {
case "errorf":
// rewrite err.errorf(obj, ... ) to err.errorf(obj.Pos(), ... )
if ident, _ := n.Args[0].(*ast.Ident); ident != nil && ident.Name == "obj" {
pos := n.Args[0].Pos()
fun := &ast.SelectorExpr{X: ident, Sel: newIdent(pos, "Pos")}
arg := &ast.CallExpr{Fun: fun, Lparen: pos, Args: nil, Ellipsis: token.NoPos, Rparen: pos}
n.Args[0] = arg
n.Args[0] = &ast.CallExpr{Fun: fun, Lparen: pos, Rparen: pos}
return false
}
}
@ -298,15 +364,14 @@ func fixCheckErrorfCall(f *ast.File) {
switch n := n.(type) {
case *ast.CallExpr:
if selx, _ := n.Fun.(*ast.SelectorExpr); selx != nil {
if ident, _ := selx.X.(*ast.Ident); ident != nil && ident.Name == "check" {
if isIdent(selx.X, "check") {
switch selx.Sel.Name {
case "errorf":
// rewrite check.errorf(pos, ... ) to check.errorf(atPos(pos), ... )
if ident, _ := n.Args[0].(*ast.Ident); ident != nil && ident.Name == "pos" {
if ident := asIdent(n.Args[0], "pos"); ident != nil {
pos := n.Args[0].Pos()
fun := newIdent(pos, "atPos")
arg := &ast.CallExpr{Fun: fun, Lparen: pos, Args: []ast.Expr{ident}, Ellipsis: token.NoPos, Rparen: pos}
n.Args[0] = arg
n.Args[0] = &ast.CallExpr{Fun: fun, Lparen: pos, Args: []ast.Expr{ident}, Rparen: pos}
return false
}
}
@ -317,21 +382,6 @@ func fixCheckErrorfCall(f *ast.File) {
})
}
// fixTraceSel renames uses of x.Trace to x.trace, where x for any x with a Trace field.
func fixTraceSel(f *ast.File) {
ast.Inspect(f, func(n ast.Node) bool {
switch n := n.(type) {
case *ast.SelectorExpr:
// rewrite x.Trace to x._Trace (for Config.Trace)
if n.Sel.Name == "Trace" {
n.Sel.Name = "_Trace"
return false
}
}
return true
})
}
// fixGlobalTypVarDecl changes the global Typ variable from an array to a slice
// (in types2 we use an array for efficiency, in go/types it's a slice and we
// cannot change that).
@ -354,7 +404,7 @@ func fixSprintf(f *ast.File) {
ast.Inspect(f, func(n ast.Node) bool {
switch n := n.(type) {
case *ast.CallExpr:
if fun, _ := n.Fun.(*ast.Ident); fun != nil && fun.Name == "sprintf" && len(n.Args) >= 4 /* ... args */ {
if isIdent(n.Fun, "sprintf") && len(n.Args) >= 4 /* ... args */ {
n.Args = insert(n.Args, 1, newIdent(n.Args[1].Pos(), "nil"))
return false
}
@ -363,6 +413,19 @@ func fixSprintf(f *ast.File) {
})
}
// asIdent returns x as *ast.Ident if it is an identifier with the given name.
func asIdent(x ast.Node, name string) *ast.Ident {
if ident, _ := x.(*ast.Ident); ident != nil && ident.Name == name {
return ident
}
return nil
}
// isIdent reports whether x is an identifier with the given name.
func isIdent(x ast.Node, name string) bool {
return asIdent(x, name) != nil
}
// newIdent returns a new identifier with the given position and name.
func newIdent(pos token.Pos, name string) *ast.Ident {
id := ast.NewIdent(name)

View File

@ -26,3 +26,13 @@ func cmpPos(p, q token.Pos) int { return int(p - q) }
// hasDots reports whether the last argument in the call is followed by ...
func hasDots(call *ast.CallExpr) bool { return call.Ellipsis.IsValid() }
// dddErrPos returns the positioner for reporting an invalid ... use in a call.
func dddErrPos(call *ast.CallExpr) positioner {
return atPos(call.Ellipsis)
}
// argErrPos returns positioner for reportign an invalid argument count.
func argErrPos(call *ast.CallExpr) positioner {
return inNode(call, call.Rparen)
}