1
0
mirror of https://github.com/golang/go synced 2024-11-26 04:07:59 -07:00

[dev.typeparams] cmd/compile/internal/types2: use syntax printer to print expressions

The syntax package has a full-fledged node printer. Use that printer
to create the expression strings needed in error messages, and remove
the local (essentially) duplicate code for creating expression strings.

Change-Id: I03673e5e79b3c1470f8073ebbe840a90fd9053ec
Reviewed-on: https://go-review.googlesource.com/c/go/+/282553
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Robert Griesemer 2021-01-07 12:58:31 -08:00
parent 934f9dc0ef
commit 0aede1205b
8 changed files with 15 additions and 307 deletions

View File

@ -379,7 +379,7 @@ func (init *Initializer) String() string {
buf.WriteString(lhs.Name())
}
buf.WriteString(" = ")
WriteExpr(&buf, init.Rhs)
syntax.Fprint(&buf, init.Rhs, syntax.ShortForm)
return buf.String()
}

View File

@ -151,7 +151,7 @@ func TestValuesInfo(t *testing.T) {
// look for expression
var expr syntax.Expr
for e := range info.Types {
if ExprString(e) == test.expr {
if syntax.ShortString(e) == test.expr {
expr = e
break
}
@ -306,7 +306,7 @@ func TestTypesInfo(t *testing.T) {
// look for expression type
var typ Type
for e, tv := range info.Types {
if ExprString(e) == test.expr {
if syntax.ShortString(e) == test.expr {
typ = tv.Type
break
}
@ -454,7 +454,7 @@ func TestInferredInfo(t *testing.T) {
default:
panic(fmt.Sprintf("unexpected call expression type %T", call))
}
if ExprString(fun) == test.fun {
if syntax.ShortString(fun) == test.fun {
targs = inf.Targs
sig = inf.Sig
break
@ -733,8 +733,8 @@ func TestPredicatesInfo(t *testing.T) {
// look for expression predicates
got := "<missing>"
for e, tv := range info.Types {
//println(name, ExprString(e))
if ExprString(e) == test.expr {
//println(name, syntax.ShortString(e))
if syntax.ShortString(e) == test.expr {
got = predString(tv)
break
}

View File

@ -197,7 +197,7 @@ func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type {
var op operand
check.expr(&op, sel.X)
if op.mode == mapindex {
check.errorf(&z, "cannot assign to struct field %s in map", ExprString(z.expr))
check.errorf(&z, "cannot assign to struct field %s in map", syntax.ShortString(z.expr))
return nil
}
}

View File

@ -176,7 +176,7 @@ func testBuiltinSignature(t *testing.T, name, src0, want string) {
// the recorded type for the built-in must match the wanted signature
typ := types[fun].Type
if typ == nil {
t.Errorf("%s: no type recorded for %s", src0, ExprString(fun))
t.Errorf("%s: no type recorded for %s", src0, syntax.ShortString(fun))
return
}
if got := typ.String(); got != want {

View File

@ -53,7 +53,7 @@ func (check *Checker) sprintf(format string, args ...interface{}) string {
case syntax.Pos:
arg = a.String()
case syntax.Expr:
arg = ExprString(a)
arg = syntax.ShortString(a)
case Object:
arg = ObjectString(a, check.qualifier)
case Type:

View File

@ -1,293 +0,0 @@
// UNREVIEWED
// Copyright 2013 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.
// This file implements printing of expressions.
package types2
import (
"bytes"
"cmd/compile/internal/syntax"
)
// ExprString returns the (possibly shortened) string representation for x.
// Shortened representations are suitable for user interfaces but may not
// necessarily follow Go syntax.
func ExprString(x syntax.Expr) string {
var buf bytes.Buffer
WriteExpr(&buf, x)
return buf.String()
}
// WriteExpr writes the (possibly shortened) string representation for x to buf.
// Shortened representations are suitable for user interfaces but may not
// necessarily follow Go syntax.
func WriteExpr(buf *bytes.Buffer, x syntax.Expr) {
// The AST preserves source-level parentheses so there is
// no need to introduce them here to correct for different
// operator precedences. (This assumes that the AST was
// generated by a Go parser.)
// TODO(gri): This assumption is not correct - we need to recreate
// parentheses in expressions.
switch x := x.(type) {
default:
buf.WriteString("(ast: bad expr)") // nil, syntax.BadExpr, syntax.KeyValueExpr
case *syntax.Name:
buf.WriteString(x.Value)
case *syntax.DotsType:
buf.WriteString("...")
if x.Elem != nil {
WriteExpr(buf, x.Elem)
}
case *syntax.BasicLit:
buf.WriteString(x.Value)
case *syntax.FuncLit:
WriteExpr(buf, x.Type)
if x.Body != nil && len(x.Body.List) > 0 {
buf.WriteString(" {…}") // shortened
} else {
buf.WriteString(" {}")
}
case *syntax.CompositeLit:
WriteExpr(buf, x.Type)
if len(x.ElemList) > 0 {
buf.WriteString("{…}") // shortened
} else {
buf.WriteString("{}")
}
case *syntax.ParenExpr:
buf.WriteByte('(')
WriteExpr(buf, x.X)
buf.WriteByte(')')
case *syntax.SelectorExpr:
WriteExpr(buf, x.X)
buf.WriteByte('.')
buf.WriteString(x.Sel.Value)
case *syntax.IndexExpr:
WriteExpr(buf, x.X)
buf.WriteByte('[')
WriteExpr(buf, x.Index) // x.Index may be a *ListExpr
buf.WriteByte(']')
case *syntax.SliceExpr:
WriteExpr(buf, x.X)
buf.WriteByte('[')
if x.Index[0] != nil {
WriteExpr(buf, x.Index[0])
}
buf.WriteByte(':')
if x.Index[1] != nil {
WriteExpr(buf, x.Index[1])
}
if x.Full {
buf.WriteByte(':')
if x.Index[2] != nil {
WriteExpr(buf, x.Index[2])
}
}
buf.WriteByte(']')
case *syntax.AssertExpr:
WriteExpr(buf, x.X)
buf.WriteString(".(")
WriteExpr(buf, x.Type)
buf.WriteByte(')')
case *syntax.CallExpr:
WriteExpr(buf, x.Fun)
buf.WriteByte('(')
writeExprList(buf, x.ArgList)
if x.HasDots {
buf.WriteString("...")
}
buf.WriteByte(')')
case *syntax.ListExpr:
writeExprList(buf, x.ElemList)
case *syntax.Operation:
// TODO(gri) This would be simpler if x.X == nil meant unary expression.
if x.Y == nil {
// unary expression
buf.WriteString(x.Op.String())
WriteExpr(buf, x.X)
} else {
// binary expression
WriteExpr(buf, x.X)
buf.WriteByte(' ')
buf.WriteString(x.Op.String())
buf.WriteByte(' ')
WriteExpr(buf, x.Y)
}
// case *ast.StarExpr:
// buf.WriteByte('*')
// WriteExpr(buf, x.X)
// case *ast.UnaryExpr:
// buf.WriteString(x.Op.String())
// WriteExpr(buf, x.X)
// case *ast.BinaryExpr:
// WriteExpr(buf, x.X)
// buf.WriteByte(' ')
// buf.WriteString(x.Op.String())
// buf.WriteByte(' ')
// WriteExpr(buf, x.Y)
case *syntax.ArrayType:
if x.Len == nil {
buf.WriteString("[...]")
} else {
buf.WriteByte('[')
WriteExpr(buf, x.Len)
buf.WriteByte(']')
}
WriteExpr(buf, x.Elem)
case *syntax.SliceType:
buf.WriteString("[]")
WriteExpr(buf, x.Elem)
case *syntax.StructType:
buf.WriteString("struct{")
writeFieldList(buf, x.FieldList, "; ", false)
buf.WriteByte('}')
case *syntax.FuncType:
buf.WriteString("func")
writeSigExpr(buf, x)
case *syntax.InterfaceType:
// separate type list types from method list
// TODO(gri) we can get rid of this extra code if writeExprList does the separation
var types []syntax.Expr
var methods []*syntax.Field
for _, f := range x.MethodList {
if f.Name != nil && f.Name.Value == "type" {
// type list type
types = append(types, f.Type)
} else {
// method or embedded interface
methods = append(methods, f)
}
}
buf.WriteString("interface{")
writeFieldList(buf, methods, "; ", true)
if len(types) > 0 {
if len(methods) > 0 {
buf.WriteString("; ")
}
buf.WriteString("type ")
writeExprList(buf, types)
}
buf.WriteByte('}')
case *syntax.MapType:
buf.WriteString("map[")
WriteExpr(buf, x.Key)
buf.WriteByte(']')
WriteExpr(buf, x.Value)
case *syntax.ChanType:
var s string
switch x.Dir {
case syntax.SendOnly:
s = "chan<- "
case syntax.RecvOnly:
s = "<-chan "
default:
s = "chan "
}
buf.WriteString(s)
if e, _ := x.Elem.(*syntax.ChanType); x.Dir != syntax.SendOnly && e != nil && e.Dir == syntax.RecvOnly {
// don't print chan (<-chan T) as chan <-chan T (but chan<- <-chan T is ok)
buf.WriteByte('(')
WriteExpr(buf, x.Elem)
buf.WriteByte(')')
} else {
WriteExpr(buf, x.Elem)
}
}
}
func writeSigExpr(buf *bytes.Buffer, sig *syntax.FuncType) {
buf.WriteByte('(')
writeFieldList(buf, sig.ParamList, ", ", false)
buf.WriteByte(')')
res := sig.ResultList
n := len(res)
if n == 0 {
// no result
return
}
buf.WriteByte(' ')
if n == 1 && res[0].Name == nil {
// single unnamed result
WriteExpr(buf, res[0].Type)
return
}
// multiple or named result(s)
buf.WriteByte('(')
writeFieldList(buf, res, ", ", false)
buf.WriteByte(')')
}
func writeFieldList(buf *bytes.Buffer, list []*syntax.Field, sep string, iface bool) {
for i := 0; i < len(list); {
f := list[i]
if i > 0 {
buf.WriteString(sep)
}
// if we don't have a name, we have an embedded type
if f.Name == nil {
WriteExpr(buf, f.Type)
i++
continue
}
// types of interface methods consist of signatures only
if sig, _ := f.Type.(*syntax.FuncType); sig != nil && iface {
buf.WriteString(f.Name.Value)
writeSigExpr(buf, sig)
i++
continue
}
// write the type only once for a sequence of fields with the same type
t := f.Type
buf.WriteString(f.Name.Value)
for i++; i < len(list) && list[i].Type == t; i++ {
buf.WriteString(", ")
buf.WriteString(list[i].Name.Value)
}
buf.WriteByte(' ')
WriteExpr(buf, t)
}
}
func writeExprList(buf *bytes.Buffer, list []syntax.Expr) {
for i, x := range list {
if i > 0 {
buf.WriteString(", ")
}
WriteExpr(buf, x)
}
}

View File

@ -9,9 +9,9 @@ import (
"testing"
"cmd/compile/internal/syntax"
. "cmd/compile/internal/types2"
)
// TODO(gri) move these tests into syntax package
var testExprs = []testEntry{
// basic type literals
dup("x"),
@ -24,8 +24,9 @@ var testExprs = []testEntry{
dup("`bar`"),
// func and composite literals
{"func(){}", "func() {}"},
{"func(x int) complex128 {}", "func(x int) complex128 {}"},
dup("func() {}"),
dup("[]int{}"),
{"func(x int) complex128 { return 0 }", "func(x int) complex128 {…}"},
{"[]int{1, 2, 3}", "[]int{…}"},
// non-type expressions
@ -90,7 +91,7 @@ func TestExprString(t *testing.T) {
continue
}
x := f.DeclList[0].(*syntax.VarDecl).Values
if got := ExprString(x); got != test.str {
if got := syntax.ShortString(x); got != test.str {
t.Errorf("%s: got %s, want %s", test.src, got, test.str)
}
}

View File

@ -110,7 +110,7 @@ func operandString(x *operand, qf Qualifier) string {
var expr string
if x.expr != nil {
expr = ExprString(x.expr)
expr = syntax.ShortString(x.expr)
} else {
switch x.mode {
case builtin: