1// Copyright 2009 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 parser
6
7import (
8	"bytes"
9	"fmt"
10	"go/ast"
11	"go/token"
12	"os"
13	"strings"
14	"testing"
15)
16
17var validFiles = []string{
18	"parser.go",
19	"parser_test.go",
20	"error_test.go",
21	"short_test.go",
22}
23
24func TestParse(t *testing.T) {
25	for _, filename := range validFiles {
26		_, err := ParseFile(token.NewFileSet(), filename, nil, DeclarationErrors)
27		if err != nil {
28			t.Fatalf("ParseFile(%s): %v", filename, err)
29		}
30	}
31}
32
33func nameFilter(filename string) bool {
34	switch filename {
35	case "parser.go", "interface.go", "parser_test.go":
36		return true
37	case "parser.go.orig":
38		return true // permit but should be ignored by ParseDir
39	}
40	return false
41}
42
43func dirFilter(f os.FileInfo) bool { return nameFilter(f.Name()) }
44
45func TestParseDir(t *testing.T) {
46	path := "."
47	pkgs, err := ParseDir(token.NewFileSet(), path, dirFilter, 0)
48	if err != nil {
49		t.Fatalf("ParseDir(%s): %v", path, err)
50	}
51	if n := len(pkgs); n != 1 {
52		t.Errorf("got %d packages; want 1", n)
53	}
54	pkg := pkgs["parser"]
55	if pkg == nil {
56		t.Errorf(`package "parser" not found`)
57		return
58	}
59	if n := len(pkg.Files); n != 3 {
60		t.Errorf("got %d package files; want 3", n)
61	}
62	for filename := range pkg.Files {
63		if !nameFilter(filename) {
64			t.Errorf("unexpected package file: %s", filename)
65		}
66	}
67}
68
69func TestParseExpr(t *testing.T) {
70	// just kicking the tires:
71	// a valid arithmetic expression
72	src := "a + b"
73	x, err := ParseExpr(src)
74	if err != nil {
75		t.Errorf("ParseExpr(%q): %v", src, err)
76	}
77	// sanity check
78	if _, ok := x.(*ast.BinaryExpr); !ok {
79		t.Errorf("ParseExpr(%q): got %T, want *ast.BinaryExpr", src, x)
80	}
81
82	// a valid type expression
83	src = "struct{x *int}"
84	x, err = ParseExpr(src)
85	if err != nil {
86		t.Errorf("ParseExpr(%q): %v", src, err)
87	}
88	// sanity check
89	if _, ok := x.(*ast.StructType); !ok {
90		t.Errorf("ParseExpr(%q): got %T, want *ast.StructType", src, x)
91	}
92
93	// an invalid expression
94	src = "a + *"
95	if _, err := ParseExpr(src); err == nil {
96		t.Errorf("ParseExpr(%q): got no error", src)
97	}
98
99	// a valid expression followed by extra tokens is invalid
100	src = "a[i] := x"
101	if _, err := ParseExpr(src); err == nil {
102		t.Errorf("ParseExpr(%q): got no error", src)
103	}
104
105	// a semicolon is not permitted unless automatically inserted
106	src = "a + b\n"
107	if _, err := ParseExpr(src); err != nil {
108		t.Errorf("ParseExpr(%q): got error %s", src, err)
109	}
110	src = "a + b;"
111	if _, err := ParseExpr(src); err == nil {
112		t.Errorf("ParseExpr(%q): got no error", src)
113	}
114
115	// various other stuff following a valid expression
116	const validExpr = "a + b"
117	const anything = "dh3*#D)#_"
118	for _, c := range "!)]};," {
119		src := validExpr + string(c) + anything
120		if _, err := ParseExpr(src); err == nil {
121			t.Errorf("ParseExpr(%q): got no error", src)
122		}
123	}
124
125	// ParseExpr must not crash
126	for _, src := range valids {
127		ParseExpr(src)
128	}
129}
130
131func TestColonEqualsScope(t *testing.T) {
132	f, err := ParseFile(token.NewFileSet(), "", `package p; func f() { x, y, z := x, y, z }`, 0)
133	if err != nil {
134		t.Fatal(err)
135	}
136
137	// RHS refers to undefined globals; LHS does not.
138	as := f.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.AssignStmt)
139	for _, v := range as.Rhs {
140		id := v.(*ast.Ident)
141		if id.Obj != nil {
142			t.Errorf("rhs %s has Obj, should not", id.Name)
143		}
144	}
145	for _, v := range as.Lhs {
146		id := v.(*ast.Ident)
147		if id.Obj == nil {
148			t.Errorf("lhs %s does not have Obj, should", id.Name)
149		}
150	}
151}
152
153func TestVarScope(t *testing.T) {
154	f, err := ParseFile(token.NewFileSet(), "", `package p; func f() { var x, y, z = x, y, z }`, 0)
155	if err != nil {
156		t.Fatal(err)
157	}
158
159	// RHS refers to undefined globals; LHS does not.
160	as := f.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.DeclStmt).Decl.(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
161	for _, v := range as.Values {
162		id := v.(*ast.Ident)
163		if id.Obj != nil {
164			t.Errorf("rhs %s has Obj, should not", id.Name)
165		}
166	}
167	for _, id := range as.Names {
168		if id.Obj == nil {
169			t.Errorf("lhs %s does not have Obj, should", id.Name)
170		}
171	}
172}
173
174func TestObjects(t *testing.T) {
175	const src = `
176package p
177import fmt "fmt"
178const pi = 3.14
179type T struct{}
180var x int
181func f() { L: }
182`
183
184	f, err := ParseFile(token.NewFileSet(), "", src, 0)
185	if err != nil {
186		t.Fatal(err)
187	}
188
189	objects := map[string]ast.ObjKind{
190		"p":   ast.Bad, // not in a scope
191		"fmt": ast.Bad, // not resolved yet
192		"pi":  ast.Con,
193		"T":   ast.Typ,
194		"x":   ast.Var,
195		"int": ast.Bad, // not resolved yet
196		"f":   ast.Fun,
197		"L":   ast.Lbl,
198	}
199
200	ast.Inspect(f, func(n ast.Node) bool {
201		if ident, ok := n.(*ast.Ident); ok {
202			obj := ident.Obj
203			if obj == nil {
204				if objects[ident.Name] != ast.Bad {
205					t.Errorf("no object for %s", ident.Name)
206				}
207				return true
208			}
209			if obj.Name != ident.Name {
210				t.Errorf("names don't match: obj.Name = %s, ident.Name = %s", obj.Name, ident.Name)
211			}
212			kind := objects[ident.Name]
213			if obj.Kind != kind {
214				t.Errorf("%s: obj.Kind = %s; want %s", ident.Name, obj.Kind, kind)
215			}
216		}
217		return true
218	})
219}
220
221func TestUnresolved(t *testing.T) {
222	f, err := ParseFile(token.NewFileSet(), "", `
223package p
224//
225func f1a(int)
226func f2a(byte, int, float)
227func f3a(a, b int, c float)
228func f4a(...complex)
229func f5a(a s1a, b ...complex)
230//
231func f1b(*int)
232func f2b([]byte, (int), *float)
233func f3b(a, b *int, c []float)
234func f4b(...*complex)
235func f5b(a s1a, b ...[]complex)
236//
237type s1a struct { int }
238type s2a struct { byte; int; s1a }
239type s3a struct { a, b int; c float }
240//
241type s1b struct { *int }
242type s2b struct { byte; int; *float }
243type s3b struct { a, b *s3b; c []float }
244`, 0)
245	if err != nil {
246		t.Fatal(err)
247	}
248
249	want := "int " + // f1a
250		"byte int float " + // f2a
251		"int float " + // f3a
252		"complex " + // f4a
253		"complex " + // f5a
254		//
255		"int " + // f1b
256		"byte int float " + // f2b
257		"int float " + // f3b
258		"complex " + // f4b
259		"complex " + // f5b
260		//
261		"int " + // s1a
262		"byte int " + // s2a
263		"int float " + // s3a
264		//
265		"int " + // s1a
266		"byte int float " + // s2a
267		"float " // s3a
268
269	// collect unresolved identifiers
270	var buf bytes.Buffer
271	for _, u := range f.Unresolved {
272		buf.WriteString(u.Name)
273		buf.WriteByte(' ')
274	}
275	got := buf.String()
276
277	if got != want {
278		t.Errorf("\ngot:  %s\nwant: %s", got, want)
279	}
280}
281
282var imports = map[string]bool{
283	`"a"`:        true,
284	"`a`":        true,
285	`"a/b"`:      true,
286	`"a.b"`:      true,
287	`"m\x61th"`:  true,
288	`"greek/αβ"`: true,
289	`""`:         false,
290
291	// Each of these pairs tests both `` vs "" strings
292	// and also use of invalid characters spelled out as
293	// escape sequences and written directly.
294	// For example `"\x00"` tests import "\x00"
295	// while "`\x00`" tests import `<actual-NUL-byte>`.
296	`"\x00"`:     false,
297	"`\x00`":     false,
298	`"\x7f"`:     false,
299	"`\x7f`":     false,
300	`"a!"`:       false,
301	"`a!`":       false,
302	`"a b"`:      false,
303	"`a b`":      false,
304	`"a\\b"`:     false,
305	"`a\\b`":     false,
306	"\"`a`\"":    false,
307	"`\"a\"`":    false,
308	`"\x80\x80"`: false,
309	"`\x80\x80`": false,
310	`"\xFFFD"`:   false,
311	"`\xFFFD`":   false,
312}
313
314func TestImports(t *testing.T) {
315	for path, isValid := range imports {
316		src := fmt.Sprintf("package p; import %s", path)
317		_, err := ParseFile(token.NewFileSet(), "", src, 0)
318		switch {
319		case err != nil && isValid:
320			t.Errorf("ParseFile(%s): got %v; expected no error", src, err)
321		case err == nil && !isValid:
322			t.Errorf("ParseFile(%s): got no error; expected one", src)
323		}
324	}
325}
326
327func TestCommentGroups(t *testing.T) {
328	f, err := ParseFile(token.NewFileSet(), "", `
329package p /* 1a */ /* 1b */      /* 1c */ // 1d
330/* 2a
331*/
332// 2b
333const pi = 3.1415
334/* 3a */ // 3b
335/* 3c */ const e = 2.7182
336
337// Example from issue 3139
338func ExampleCount() {
339	fmt.Println(strings.Count("cheese", "e"))
340	fmt.Println(strings.Count("five", "")) // before & after each rune
341	// Output:
342	// 3
343	// 5
344}
345`, ParseComments)
346	if err != nil {
347		t.Fatal(err)
348	}
349	expected := [][]string{
350		{"/* 1a */", "/* 1b */", "/* 1c */", "// 1d"},
351		{"/* 2a\n*/", "// 2b"},
352		{"/* 3a */", "// 3b", "/* 3c */"},
353		{"// Example from issue 3139"},
354		{"// before & after each rune"},
355		{"// Output:", "// 3", "// 5"},
356	}
357	if len(f.Comments) != len(expected) {
358		t.Fatalf("got %d comment groups; expected %d", len(f.Comments), len(expected))
359	}
360	for i, exp := range expected {
361		got := f.Comments[i].List
362		if len(got) != len(exp) {
363			t.Errorf("got %d comments in group %d; expected %d", len(got), i, len(exp))
364			continue
365		}
366		for j, exp := range exp {
367			got := got[j].Text
368			if got != exp {
369				t.Errorf("got %q in group %d; expected %q", got, i, exp)
370			}
371		}
372	}
373}
374
375func getField(file *ast.File, fieldname string) *ast.Field {
376	parts := strings.Split(fieldname, ".")
377	for _, d := range file.Decls {
378		if d, ok := d.(*ast.GenDecl); ok && d.Tok == token.TYPE {
379			for _, s := range d.Specs {
380				if s, ok := s.(*ast.TypeSpec); ok && s.Name.Name == parts[0] {
381					if s, ok := s.Type.(*ast.StructType); ok {
382						for _, f := range s.Fields.List {
383							for _, name := range f.Names {
384								if name.Name == parts[1] {
385									return f
386								}
387							}
388						}
389					}
390				}
391			}
392		}
393	}
394	return nil
395}
396
397// Don't use ast.CommentGroup.Text() - we want to see exact comment text.
398func commentText(c *ast.CommentGroup) string {
399	var buf bytes.Buffer
400	if c != nil {
401		for _, c := range c.List {
402			buf.WriteString(c.Text)
403		}
404	}
405	return buf.String()
406}
407
408func checkFieldComments(t *testing.T, file *ast.File, fieldname, lead, line string) {
409	f := getField(file, fieldname)
410	if f == nil {
411		t.Fatalf("field not found: %s", fieldname)
412	}
413	if got := commentText(f.Doc); got != lead {
414		t.Errorf("got lead comment %q; expected %q", got, lead)
415	}
416	if got := commentText(f.Comment); got != line {
417		t.Errorf("got line comment %q; expected %q", got, line)
418	}
419}
420
421func TestLeadAndLineComments(t *testing.T) {
422	f, err := ParseFile(token.NewFileSet(), "", `
423package p
424type T struct {
425	/* F1 lead comment */
426	//
427	F1 int  /* F1 */ // line comment
428	// F2 lead
429	// comment
430	F2 int  // F2 line comment
431	// f3 lead comment
432	f3 int  // f3 line comment
433}
434`, ParseComments)
435	if err != nil {
436		t.Fatal(err)
437	}
438	checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
439	checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
440	checkFieldComments(t, f, "T.f3", "// f3 lead comment", "// f3 line comment")
441	ast.FileExports(f)
442	checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
443	checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
444	if getField(f, "T.f3") != nil {
445		t.Error("not expected to find T.f3")
446	}
447}
448
449// TestIssue9979 verifies that empty statements are contained within their enclosing blocks.
450func TestIssue9979(t *testing.T) {
451	for _, src := range []string{
452		"package p; func f() {;}",
453		"package p; func f() {L:}",
454		"package p; func f() {L:;}",
455		"package p; func f() {L:\n}",
456		"package p; func f() {L:\n;}",
457		"package p; func f() { ; }",
458		"package p; func f() { L: }",
459		"package p; func f() { L: ; }",
460		"package p; func f() { L: \n}",
461		"package p; func f() { L: \n; }",
462	} {
463		fset := token.NewFileSet()
464		f, err := ParseFile(fset, "", src, 0)
465		if err != nil {
466			t.Fatal(err)
467		}
468
469		var pos, end token.Pos
470		ast.Inspect(f, func(x ast.Node) bool {
471			switch s := x.(type) {
472			case *ast.BlockStmt:
473				pos, end = s.Pos()+1, s.End()-1 // exclude "{", "}"
474			case *ast.LabeledStmt:
475				pos, end = s.Pos()+2, s.End() // exclude "L:"
476			case *ast.EmptyStmt:
477				// check containment
478				if s.Pos() < pos || s.End() > end {
479					t.Errorf("%s: %T[%d, %d] not inside [%d, %d]", src, s, s.Pos(), s.End(), pos, end)
480				}
481				// check semicolon
482				offs := fset.Position(s.Pos()).Offset
483				if ch := src[offs]; ch != ';' != s.Implicit {
484					want := "want ';'"
485					if s.Implicit {
486						want = "but ';' is implicit"
487					}
488					t.Errorf("%s: found %q at offset %d; %s", src, ch, offs, want)
489				}
490			}
491			return true
492		})
493	}
494}
495
496// TestIncompleteSelection ensures that an incomplete selector
497// expression is parsed as a (blank) *ast.SelectorExpr, not a
498// *ast.BadExpr.
499func TestIncompleteSelection(t *testing.T) {
500	for _, src := range []string{
501		"package p; var _ = fmt.",             // at EOF
502		"package p; var _ = fmt.\ntype X int", // not at EOF
503	} {
504		fset := token.NewFileSet()
505		f, err := ParseFile(fset, "", src, 0)
506		if err == nil {
507			t.Errorf("ParseFile(%s) succeeded unexpectedly", src)
508			continue
509		}
510
511		const wantErr = "expected selector or type assertion"
512		if !strings.Contains(err.Error(), wantErr) {
513			t.Errorf("ParseFile returned wrong error %q, want %q", err, wantErr)
514		}
515
516		var sel *ast.SelectorExpr
517		ast.Inspect(f, func(n ast.Node) bool {
518			if n, ok := n.(*ast.SelectorExpr); ok {
519				sel = n
520			}
521			return true
522		})
523		if sel == nil {
524			t.Error("found no *ast.SelectorExpr")
525			continue
526		}
527		const wantSel = "&{fmt _}"
528		if fmt.Sprint(sel) != wantSel {
529			t.Errorf("found selector %s, want %s", sel, wantSel)
530			continue
531		}
532	}
533}
534
535func TestLastLineComment(t *testing.T) {
536	const src = `package main
537type x int // comment
538`
539	fset := token.NewFileSet()
540	f, err := ParseFile(fset, "", src, ParseComments)
541	if err != nil {
542		t.Fatal(err)
543	}
544	comment := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.TypeSpec).Comment.List[0].Text
545	if comment != "// comment" {
546		t.Errorf("got %q, want %q", comment, "// comment")
547	}
548}
549