• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2017 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
5package syntax
6
7import (
8	"fmt"
9	"strings"
10	"testing"
11)
12
13// A test is a source code snippet of a particular node type.
14// In the snippet, a '@' indicates the position recorded by
15// the parser when creating the respective node.
16type test struct {
17	nodetyp string
18	snippet string
19}
20
21var decls = []test{
22	// The position of declarations is always the
23	// position of the first token of an individual
24	// declaration, independent of grouping.
25	{"ImportDecl", `import @"math"`},
26	{"ImportDecl", `import @mymath "math"`},
27	{"ImportDecl", `import @. "math"`},
28	{"ImportDecl", `import (@"math")`},
29	{"ImportDecl", `import (@mymath "math")`},
30	{"ImportDecl", `import (@. "math")`},
31
32	{"ConstDecl", `const @x`},
33	{"ConstDecl", `const @x = 0`},
34	{"ConstDecl", `const @x, y, z = 0, 1, 2`},
35	{"ConstDecl", `const (@x)`},
36	{"ConstDecl", `const (@x = 0)`},
37	{"ConstDecl", `const (@x, y, z = 0, 1, 2)`},
38
39	{"TypeDecl", `type @T int`},
40	{"TypeDecl", `type @T = int`},
41	{"TypeDecl", `type (@T int)`},
42	{"TypeDecl", `type (@T = int)`},
43
44	{"VarDecl", `var @x int`},
45	{"VarDecl", `var @x, y, z int`},
46	{"VarDecl", `var @x int = 0`},
47	{"VarDecl", `var @x, y, z int = 1, 2, 3`},
48	{"VarDecl", `var @x = 0`},
49	{"VarDecl", `var @x, y, z = 1, 2, 3`},
50	{"VarDecl", `var (@x int)`},
51	{"VarDecl", `var (@x, y, z int)`},
52	{"VarDecl", `var (@x int = 0)`},
53	{"VarDecl", `var (@x, y, z int = 1, 2, 3)`},
54	{"VarDecl", `var (@x = 0)`},
55	{"VarDecl", `var (@x, y, z = 1, 2, 3)`},
56
57	{"FuncDecl", `func @f() {}`},
58	{"FuncDecl", `func @(T) f() {}`},
59	{"FuncDecl", `func @(x T) f() {}`},
60}
61
62var exprs = []test{
63	// The position of an expression is the position
64	// of the left-most token that identifies the
65	// kind of expression.
66	{"Name", `@x`},
67
68	{"BasicLit", `@0`},
69	{"BasicLit", `@0x123`},
70	{"BasicLit", `@3.1415`},
71	{"BasicLit", `@.2718`},
72	{"BasicLit", `@1i`},
73	{"BasicLit", `@'a'`},
74	{"BasicLit", `@"abc"`},
75	{"BasicLit", "@`abc`"},
76
77	{"CompositeLit", `@{}`},
78	{"CompositeLit", `T@{}`},
79	{"CompositeLit", `struct{x, y int}@{}`},
80
81	{"KeyValueExpr", `"foo"@: true`},
82	{"KeyValueExpr", `"a"@: b`},
83
84	{"FuncLit", `@func (){}`},
85	{"ParenExpr", `@(x)`},
86	{"SelectorExpr", `a@.b`},
87	{"IndexExpr", `a@[i]`},
88
89	{"SliceExpr", `a@[:]`},
90	{"SliceExpr", `a@[i:]`},
91	{"SliceExpr", `a@[:j]`},
92	{"SliceExpr", `a@[i:j]`},
93	{"SliceExpr", `a@[i:j:k]`},
94
95	{"AssertExpr", `x@.(T)`},
96
97	{"Operation", `@*b`},
98	{"Operation", `@+b`},
99	{"Operation", `@-b`},
100	{"Operation", `@!b`},
101	{"Operation", `@^b`},
102	{"Operation", `@&b`},
103	{"Operation", `@<-b`},
104
105	{"Operation", `a @|| b`},
106	{"Operation", `a @&& b`},
107	{"Operation", `a @== b`},
108	{"Operation", `a @+ b`},
109	{"Operation", `a @* b`},
110
111	{"CallExpr", `f@()`},
112	{"CallExpr", `f@(x, y, z)`},
113	{"CallExpr", `obj.f@(1, 2, 3)`},
114	{"CallExpr", `func(x int) int { return x + 1 }@(y)`},
115
116	// ListExpr: tested via multi-value const/var declarations
117}
118
119var types = []test{
120	{"Operation", `@*T`},
121	{"Operation", `@*struct{}`},
122
123	{"ArrayType", `@[10]T`},
124	{"ArrayType", `@[...]T`},
125
126	{"SliceType", `@[]T`},
127	{"DotsType", `@...T`},
128	{"StructType", `@struct{}`},
129	{"InterfaceType", `@interface{}`},
130	{"FuncType", `func@()`},
131	{"MapType", `@map[T]T`},
132
133	{"ChanType", `@chan T`},
134	{"ChanType", `@chan<- T`},
135	{"ChanType", `@<-chan T`},
136}
137
138var fields = []test{
139	{"Field", `@T`},
140	{"Field", `@(T)`},
141	{"Field", `@x T`},
142	{"Field", `@x *(T)`},
143	{"Field", `@x, y, z T`},
144	{"Field", `@x, y, z (*T)`},
145}
146
147var stmts = []test{
148	{"EmptyStmt", `@`},
149
150	{"LabeledStmt", `L@:`},
151	{"LabeledStmt", `L@: ;`},
152	{"LabeledStmt", `L@: f()`},
153
154	{"BlockStmt", `@{}`},
155
156	// The position of an ExprStmt is the position of the expression.
157	{"ExprStmt", `@<-ch`},
158	{"ExprStmt", `f@()`},
159	{"ExprStmt", `append@(s, 1, 2, 3)`},
160
161	{"SendStmt", `ch @<- x`},
162
163	{"DeclStmt", `@const x = 0`},
164	{"DeclStmt", `@const (x = 0)`},
165	{"DeclStmt", `@type T int`},
166	{"DeclStmt", `@type T = int`},
167	{"DeclStmt", `@type (T1 = int; T2 = float32)`},
168	{"DeclStmt", `@var x = 0`},
169	{"DeclStmt", `@var x, y, z int`},
170	{"DeclStmt", `@var (a, b = 1, 2)`},
171
172	{"AssignStmt", `x @= y`},
173	{"AssignStmt", `a, b, x @= 1, 2, 3`},
174	{"AssignStmt", `x @+= y`},
175	{"AssignStmt", `x @:= y`},
176	{"AssignStmt", `x, ok @:= f()`},
177	{"AssignStmt", `x@++`},
178	{"AssignStmt", `a[i]@--`},
179
180	{"BranchStmt", `@break`},
181	{"BranchStmt", `@break L`},
182	{"BranchStmt", `@continue`},
183	{"BranchStmt", `@continue L`},
184	{"BranchStmt", `@fallthrough`},
185	{"BranchStmt", `@goto L`},
186
187	{"CallStmt", `@defer f()`},
188	{"CallStmt", `@go f()`},
189
190	{"ReturnStmt", `@return`},
191	{"ReturnStmt", `@return x`},
192	{"ReturnStmt", `@return a, b, a + b*f(1, 2, 3)`},
193
194	{"IfStmt", `@if cond {}`},
195	{"IfStmt", `@if cond { f() } else {}`},
196	{"IfStmt", `@if cond { f() } else { g(); h() }`},
197	{"ForStmt", `@for {}`},
198	{"ForStmt", `@for { f() }`},
199	{"SwitchStmt", `@switch {}`},
200	{"SwitchStmt", `@switch { default: }`},
201	{"SwitchStmt", `@switch { default: x++ }`},
202	{"SelectStmt", `@select {}`},
203	{"SelectStmt", `@select { default: }`},
204	{"SelectStmt", `@select { default: ch <- false }`},
205}
206
207var ranges = []test{
208	{"RangeClause", `@range s`},
209	{"RangeClause", `i = @range s`},
210	{"RangeClause", `i := @range s`},
211	{"RangeClause", `_, x = @range s`},
212	{"RangeClause", `i, x = @range s`},
213	{"RangeClause", `_, x := @range s.f`},
214	{"RangeClause", `i, x := @range f(i)`},
215}
216
217var guards = []test{
218	{"TypeSwitchGuard", `x@.(type)`},
219	{"TypeSwitchGuard", `x := x@.(type)`},
220}
221
222var cases = []test{
223	{"CaseClause", `@case x:`},
224	{"CaseClause", `@case x, y, z:`},
225	{"CaseClause", `@case x == 1, y == 2:`},
226	{"CaseClause", `@default:`},
227}
228
229var comms = []test{
230	{"CommClause", `@case <-ch:`},
231	{"CommClause", `@case x <- ch:`},
232	{"CommClause", `@case x = <-ch:`},
233	{"CommClause", `@case x := <-ch:`},
234	{"CommClause", `@case x, ok = <-ch: f(1, 2, 3)`},
235	{"CommClause", `@case x, ok := <-ch: x++`},
236	{"CommClause", `@default:`},
237	{"CommClause", `@default: ch <- true`},
238}
239
240func TestPos(t *testing.T) {
241	// TODO(gri) Once we have a general tree walker, we can use that to find
242	// the first occurrence of the respective node and we don't need to hand-
243	// extract the node for each specific kind of construct.
244
245	testPos(t, decls, "package p; ", "",
246		func(f *File) Node { return f.DeclList[0] },
247	)
248
249	// embed expressions in a composite literal so we can test key:value and naked composite literals
250	testPos(t, exprs, "package p; var _ = T{ ", " }",
251		func(f *File) Node { return f.DeclList[0].(*VarDecl).Values.(*CompositeLit).ElemList[0] },
252	)
253
254	// embed types in a function  signature so we can test ... types
255	testPos(t, types, "package p; func f(", ")",
256		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Type.ParamList[0].Type },
257	)
258
259	testPos(t, fields, "package p; func f(", ")",
260		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Type.ParamList[0] },
261	)
262
263	testPos(t, stmts, "package p; func _() { ", "; }",
264		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body.List[0] },
265	)
266
267	testPos(t, ranges, "package p; func _() { for ", " {} }",
268		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body.List[0].(*ForStmt).Init.(*RangeClause) },
269	)
270
271	testPos(t, guards, "package p; func _() { switch ", " {} }",
272		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body.List[0].(*SwitchStmt).Tag.(*TypeSwitchGuard) },
273	)
274
275	testPos(t, cases, "package p; func _() { switch { ", " } }",
276		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body.List[0].(*SwitchStmt).Body[0] },
277	)
278
279	testPos(t, comms, "package p; func _() { select { ", " } }",
280		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body.List[0].(*SelectStmt).Body[0] },
281	)
282}
283
284func testPos(t *testing.T, list []test, prefix, suffix string, extract func(*File) Node) {
285	for _, test := range list {
286		// complete source, compute @ position, and strip @ from source
287		src, index := stripAt(prefix + test.snippet + suffix)
288		if index < 0 {
289			t.Errorf("missing @: %s (%s)", src, test.nodetyp)
290			continue
291		}
292
293		// build syntax tree
294		file, err := Parse(nil, strings.NewReader(src), nil, nil, 0)
295		if err != nil {
296			t.Errorf("parse error: %s: %v (%s)", src, err, test.nodetyp)
297			continue
298		}
299
300		// extract desired node
301		node := extract(file)
302		if typ := typeOf(node); typ != test.nodetyp {
303			t.Errorf("type error: %s: type = %s, want %s", src, typ, test.nodetyp)
304			continue
305		}
306
307		// verify node position with expected position as indicated by @
308		if pos := int(node.Pos().Col()); pos != index+colbase {
309			t.Errorf("pos error: %s: pos = %d, want %d (%s)", src, pos, index+colbase, test.nodetyp)
310			continue
311		}
312	}
313}
314
315func stripAt(s string) (string, int) {
316	if i := strings.Index(s, "@"); i >= 0 {
317		return s[:i] + s[i+1:], i
318	}
319	return s, -1
320}
321
322func typeOf(n Node) string {
323	const prefix = "*syntax."
324	k := fmt.Sprintf("%T", n)
325	return strings.TrimPrefix(k, prefix)
326}
327