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