1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// This file implements printing of expressions.
6
7package types
8
9import (
10	"bytes"
11	"fmt"
12	"go/ast"
13	"go/internal/typeparams"
14)
15
16// ExprString returns the (possibly shortened) string representation for x.
17// Shortened representations are suitable for user interfaces but may not
18// necessarily follow Go syntax.
19func ExprString(x ast.Expr) string {
20	var buf bytes.Buffer
21	WriteExpr(&buf, x)
22	return buf.String()
23}
24
25// WriteExpr writes the (possibly shortened) string representation for x to buf.
26// Shortened representations are suitable for user interfaces but may not
27// necessarily follow Go syntax.
28func WriteExpr(buf *bytes.Buffer, x ast.Expr) {
29	// The AST preserves source-level parentheses so there is
30	// no need to introduce them here to correct for different
31	// operator precedences. (This assumes that the AST was
32	// generated by a Go parser.)
33
34	switch x := x.(type) {
35	default:
36		fmt.Fprintf(buf, "(ast: %T)", x) // nil, ast.BadExpr, ast.KeyValueExpr
37
38	case *ast.Ident:
39		buf.WriteString(x.Name)
40
41	case *ast.Ellipsis:
42		buf.WriteString("...")
43		if x.Elt != nil {
44			WriteExpr(buf, x.Elt)
45		}
46
47	case *ast.BasicLit:
48		buf.WriteString(x.Value)
49
50	case *ast.FuncLit:
51		buf.WriteByte('(')
52		WriteExpr(buf, x.Type)
53		buf.WriteString(" literal)") // shortened
54
55	case *ast.CompositeLit:
56		WriteExpr(buf, x.Type)
57		buf.WriteByte('{')
58		if len(x.Elts) > 0 {
59			buf.WriteString("…")
60		}
61		buf.WriteByte('}')
62
63	case *ast.ParenExpr:
64		buf.WriteByte('(')
65		WriteExpr(buf, x.X)
66		buf.WriteByte(')')
67
68	case *ast.SelectorExpr:
69		WriteExpr(buf, x.X)
70		buf.WriteByte('.')
71		buf.WriteString(x.Sel.Name)
72
73	case *ast.IndexExpr, *ast.IndexListExpr:
74		ix := typeparams.UnpackIndexExpr(x)
75		WriteExpr(buf, ix.X)
76		buf.WriteByte('[')
77		writeExprList(buf, ix.Indices)
78		buf.WriteByte(']')
79
80	case *ast.SliceExpr:
81		WriteExpr(buf, x.X)
82		buf.WriteByte('[')
83		if x.Low != nil {
84			WriteExpr(buf, x.Low)
85		}
86		buf.WriteByte(':')
87		if x.High != nil {
88			WriteExpr(buf, x.High)
89		}
90		if x.Slice3 {
91			buf.WriteByte(':')
92			if x.Max != nil {
93				WriteExpr(buf, x.Max)
94			}
95		}
96		buf.WriteByte(']')
97
98	case *ast.TypeAssertExpr:
99		WriteExpr(buf, x.X)
100		buf.WriteString(".(")
101		WriteExpr(buf, x.Type)
102		buf.WriteByte(')')
103
104	case *ast.CallExpr:
105		WriteExpr(buf, x.Fun)
106		buf.WriteByte('(')
107		writeExprList(buf, x.Args)
108		if hasDots(x) {
109			buf.WriteString("...")
110		}
111		buf.WriteByte(')')
112
113	case *ast.StarExpr:
114		buf.WriteByte('*')
115		WriteExpr(buf, x.X)
116
117	case *ast.UnaryExpr:
118		buf.WriteString(x.Op.String())
119		WriteExpr(buf, x.X)
120
121	case *ast.BinaryExpr:
122		WriteExpr(buf, x.X)
123		buf.WriteByte(' ')
124		buf.WriteString(x.Op.String())
125		buf.WriteByte(' ')
126		WriteExpr(buf, x.Y)
127
128	case *ast.ArrayType:
129		buf.WriteByte('[')
130		if x.Len != nil {
131			WriteExpr(buf, x.Len)
132		}
133		buf.WriteByte(']')
134		WriteExpr(buf, x.Elt)
135
136	case *ast.StructType:
137		buf.WriteString("struct{")
138		writeFieldList(buf, x.Fields.List, "; ", false)
139		buf.WriteByte('}')
140
141	case *ast.FuncType:
142		buf.WriteString("func")
143		writeSigExpr(buf, x)
144
145	case *ast.InterfaceType:
146		buf.WriteString("interface{")
147		writeFieldList(buf, x.Methods.List, "; ", true)
148		buf.WriteByte('}')
149
150	case *ast.MapType:
151		buf.WriteString("map[")
152		WriteExpr(buf, x.Key)
153		buf.WriteByte(']')
154		WriteExpr(buf, x.Value)
155
156	case *ast.ChanType:
157		var s string
158		switch x.Dir {
159		case ast.SEND:
160			s = "chan<- "
161		case ast.RECV:
162			s = "<-chan "
163		default:
164			s = "chan "
165		}
166		buf.WriteString(s)
167		WriteExpr(buf, x.Value)
168	}
169}
170
171func writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) {
172	buf.WriteByte('(')
173	writeFieldList(buf, sig.Params.List, ", ", false)
174	buf.WriteByte(')')
175
176	res := sig.Results
177	n := res.NumFields()
178	if n == 0 {
179		// no result
180		return
181	}
182
183	buf.WriteByte(' ')
184	if n == 1 && len(res.List[0].Names) == 0 {
185		// single unnamed result
186		WriteExpr(buf, res.List[0].Type)
187		return
188	}
189
190	// multiple or named result(s)
191	buf.WriteByte('(')
192	writeFieldList(buf, res.List, ", ", false)
193	buf.WriteByte(')')
194}
195
196func writeFieldList(buf *bytes.Buffer, list []*ast.Field, sep string, iface bool) {
197	for i, f := range list {
198		if i > 0 {
199			buf.WriteString(sep)
200		}
201
202		// field list names
203		writeIdentList(buf, f.Names)
204
205		// types of interface methods consist of signatures only
206		if sig, _ := f.Type.(*ast.FuncType); sig != nil && iface {
207			writeSigExpr(buf, sig)
208			continue
209		}
210
211		// named fields are separated with a blank from the field type
212		if len(f.Names) > 0 {
213			buf.WriteByte(' ')
214		}
215
216		WriteExpr(buf, f.Type)
217
218		// ignore tag
219	}
220}
221
222func writeIdentList(buf *bytes.Buffer, list []*ast.Ident) {
223	for i, x := range list {
224		if i > 0 {
225			buf.WriteString(", ")
226		}
227		buf.WriteString(x.Name)
228	}
229}
230
231func writeExprList(buf *bytes.Buffer, list []ast.Expr) {
232	for i, x := range list {
233		if i > 0 {
234			buf.WriteString(", ")
235		}
236		WriteExpr(buf, x)
237	}
238}
239