// 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 types import ( "bytes" "fmt" "go/ast" "go/internal/typeparams" ) // 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 ast.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 ast.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.) switch x := x.(type) { default: buf.WriteString(fmt.Sprintf("(ast: %T)", x)) // nil, ast.BadExpr, ast.KeyValueExpr case *ast.Ident: buf.WriteString(x.Name) case *ast.Ellipsis: buf.WriteString("...") if x.Elt != nil { WriteExpr(buf, x.Elt) } case *ast.BasicLit: buf.WriteString(x.Value) case *ast.FuncLit: buf.WriteByte('(') WriteExpr(buf, x.Type) buf.WriteString(" literal)") // shortened case *ast.CompositeLit: buf.WriteByte('(') WriteExpr(buf, x.Type) buf.WriteString(" literal)") // shortened case *ast.ParenExpr: buf.WriteByte('(') WriteExpr(buf, x.X) buf.WriteByte(')') case *ast.SelectorExpr: WriteExpr(buf, x.X) buf.WriteByte('.') buf.WriteString(x.Sel.Name) case *ast.IndexExpr: WriteExpr(buf, x.X) buf.WriteByte('[') exprs := typeparams.UnpackExpr(x.Index) for i, e := range exprs { if i > 0 { buf.WriteString(", ") } WriteExpr(buf, e) } buf.WriteByte(']') case *ast.SliceExpr: WriteExpr(buf, x.X) buf.WriteByte('[') if x.Low != nil { WriteExpr(buf, x.Low) } buf.WriteByte(':') if x.High != nil { WriteExpr(buf, x.High) } if x.Slice3 { buf.WriteByte(':') if x.Max != nil { WriteExpr(buf, x.Max) } } buf.WriteByte(']') case *ast.TypeAssertExpr: WriteExpr(buf, x.X) buf.WriteString(".(") WriteExpr(buf, x.Type) buf.WriteByte(')') case *ast.CallExpr: WriteExpr(buf, x.Fun) buf.WriteByte('(') writeExprList(buf, x.Args) if x.Ellipsis.IsValid() { buf.WriteString("...") } buf.WriteByte(')') 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 *ast.ArrayType: buf.WriteByte('[') if x.Len != nil { WriteExpr(buf, x.Len) } buf.WriteByte(']') WriteExpr(buf, x.Elt) case *ast.StructType: buf.WriteString("struct{") writeFieldList(buf, x.Fields.List, "; ", false) buf.WriteByte('}') case *ast.FuncType: buf.WriteString("func") writeSigExpr(buf, x) case *ast.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 []ast.Expr var methods []*ast.Field for _, f := range x.Methods.List { if len(f.Names) > 1 && f.Names[0].Name == "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 *ast.MapType: buf.WriteString("map[") WriteExpr(buf, x.Key) buf.WriteByte(']') WriteExpr(buf, x.Value) case *ast.ChanType: var s string switch x.Dir { case ast.SEND: s = "chan<- " case ast.RECV: s = "<-chan " default: s = "chan " } buf.WriteString(s) WriteExpr(buf, x.Value) } } func writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) { buf.WriteByte('(') writeFieldList(buf, sig.Params.List, ", ", false) buf.WriteByte(')') res := sig.Results n := res.NumFields() if n == 0 { // no result return } buf.WriteByte(' ') if n == 1 && len(res.List[0].Names) == 0 { // single unnamed result WriteExpr(buf, res.List[0].Type) return } // multiple or named result(s) buf.WriteByte('(') writeFieldList(buf, res.List, ", ", false) buf.WriteByte(')') } func writeFieldList(buf *bytes.Buffer, list []*ast.Field, sep string, iface bool) { for i, f := range list { if i > 0 { buf.WriteString(sep) } // field list names writeIdentList(buf, f.Names) // types of interface methods consist of signatures only if sig, _ := f.Type.(*ast.FuncType); sig != nil && iface { writeSigExpr(buf, sig) continue } // named fields are separated with a blank from the field type if len(f.Names) > 0 { buf.WriteByte(' ') } WriteExpr(buf, f.Type) // ignore tag } } func writeIdentList(buf *bytes.Buffer, list []*ast.Ident) { for i, x := range list { if i > 0 { buf.WriteString(", ") } buf.WriteString(x.Name) } } func writeExprList(buf *bytes.Buffer, list []ast.Expr) { for i, x := range list { if i > 0 { buf.WriteString(", ") } WriteExpr(buf, x) } }