From 81609c454be36b92155f795e726dc090659e1c50 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 8 Feb 2024 14:29:26 -0800 Subject: [PATCH] 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 Reviewed-by: Robert Findley Reviewed-by: Robert Griesemer Auto-Submit: Robert Griesemer --- src/cmd/compile/internal/types2/builtins.go | 5 +- src/cmd/compile/internal/types2/util.go | 11 ++ src/go/types/builtins.go | 6 +- src/go/types/generate_test.go | 201 +++++++++++++------- src/go/types/util.go | 10 + 5 files changed, 159 insertions(+), 74 deletions(-) diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go index 5db3ae2fa4..79ed6ad640 100644 --- a/src/cmd/compile/internal/types2/builtins.go +++ b/src/cmd/compile/internal/types2/builtins.go @@ -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 } } diff --git a/src/cmd/compile/internal/types2/util.go b/src/cmd/compile/internal/types2/util.go index 35ab71be2b..9ffef1314d 100644 --- a/src/cmd/compile/internal/types2/util.go +++ b/src/cmd/compile/internal/types2/util.go @@ -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) } diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index b49af55469..040b24f5cc 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -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 } } diff --git a/src/go/types/generate_test.go b/src/go/types/generate_test.go index 2208f56b7d..d1b98ad8e4 100644 --- a/src/go/types/generate_test.go +++ b/src/go/types/generate_test.go @@ -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) diff --git a/src/go/types/util.go b/src/go/types/util.go index 910a0f16d9..ea0dd7b071 100644 --- a/src/go/types/util.go +++ b/src/go/types/util.go @@ -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) +} -- 2.48.1