• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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	"fmt"
9	"go/ast"
10	"go/token"
11	"io/fs"
12	"strings"
13	"testing"
14)
15
16var validFiles = []string{
17	"parser.go",
18	"parser_test.go",
19	"error_test.go",
20	"short_test.go",
21}
22
23func TestParse(t *testing.T) {
24	for _, filename := range validFiles {
25		_, err := ParseFile(token.NewFileSet(), filename, nil, DeclarationErrors)
26		if err != nil {
27			t.Fatalf("ParseFile(%s): %v", filename, err)
28		}
29	}
30}
31
32func nameFilter(filename string) bool {
33	switch filename {
34	case "parser.go", "interface.go", "parser_test.go":
35		return true
36	case "parser.go.orig":
37		return true // permit but should be ignored by ParseDir
38	}
39	return false
40}
41
42func dirFilter(f fs.FileInfo) bool { return nameFilter(f.Name()) }
43
44func TestParseFile(t *testing.T) {
45	src := "package p\nvar _=s[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]"
46	_, err := ParseFile(token.NewFileSet(), "", src, 0)
47	if err == nil {
48		t.Errorf("ParseFile(%s) succeeded unexpectedly", src)
49	}
50}
51
52func TestParseExprFrom(t *testing.T) {
53	src := "s[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]+\ns[::]"
54	_, err := ParseExprFrom(token.NewFileSet(), "", src, 0)
55	if err == nil {
56		t.Errorf("ParseExprFrom(%s) succeeded unexpectedly", src)
57	}
58}
59
60func TestParseDir(t *testing.T) {
61	path := "."
62	pkgs, err := ParseDir(token.NewFileSet(), path, dirFilter, 0)
63	if err != nil {
64		t.Fatalf("ParseDir(%s): %v", path, err)
65	}
66	if n := len(pkgs); n != 1 {
67		t.Errorf("got %d packages; want 1", n)
68	}
69	pkg := pkgs["parser"]
70	if pkg == nil {
71		t.Errorf(`package "parser" not found`)
72		return
73	}
74	if n := len(pkg.Files); n != 3 {
75		t.Errorf("got %d package files; want 3", n)
76	}
77	for filename := range pkg.Files {
78		if !nameFilter(filename) {
79			t.Errorf("unexpected package file: %s", filename)
80		}
81	}
82}
83
84func TestIssue42951(t *testing.T) {
85	path := "./testdata/issue42951"
86	_, err := ParseDir(token.NewFileSet(), path, nil, 0)
87	if err != nil {
88		t.Errorf("ParseDir(%s): %v", path, err)
89	}
90}
91
92func TestParseExpr(t *testing.T) {
93	// just kicking the tires:
94	// a valid arithmetic expression
95	src := "a + b"
96	x, err := ParseExpr(src)
97	if err != nil {
98		t.Errorf("ParseExpr(%q): %v", src, err)
99	}
100	// sanity check
101	if _, ok := x.(*ast.BinaryExpr); !ok {
102		t.Errorf("ParseExpr(%q): got %T, want *ast.BinaryExpr", src, x)
103	}
104
105	// a valid type expression
106	src = "struct{x *int}"
107	x, err = ParseExpr(src)
108	if err != nil {
109		t.Errorf("ParseExpr(%q): %v", src, err)
110	}
111	// sanity check
112	if _, ok := x.(*ast.StructType); !ok {
113		t.Errorf("ParseExpr(%q): got %T, want *ast.StructType", src, x)
114	}
115
116	// an invalid expression
117	src = "a + *"
118	x, err = ParseExpr(src)
119	if err == nil {
120		t.Errorf("ParseExpr(%q): got no error", src)
121	}
122	if x == nil {
123		t.Errorf("ParseExpr(%q): got no (partial) result", src)
124	}
125	if _, ok := x.(*ast.BinaryExpr); !ok {
126		t.Errorf("ParseExpr(%q): got %T, want *ast.BinaryExpr", src, x)
127	}
128
129	// a valid expression followed by extra tokens is invalid
130	src = "a[i] := x"
131	if _, err := ParseExpr(src); err == nil {
132		t.Errorf("ParseExpr(%q): got no error", src)
133	}
134
135	// a semicolon is not permitted unless automatically inserted
136	src = "a + b\n"
137	if _, err := ParseExpr(src); err != nil {
138		t.Errorf("ParseExpr(%q): got error %s", src, err)
139	}
140	src = "a + b;"
141	if _, err := ParseExpr(src); err == nil {
142		t.Errorf("ParseExpr(%q): got no error", src)
143	}
144
145	// various other stuff following a valid expression
146	const validExpr = "a + b"
147	const anything = "dh3*#D)#_"
148	for _, c := range "!)]};," {
149		src := validExpr + string(c) + anything
150		if _, err := ParseExpr(src); err == nil {
151			t.Errorf("ParseExpr(%q): got no error", src)
152		}
153	}
154
155	// ParseExpr must not crash
156	for _, src := range valids {
157		ParseExpr(src)
158	}
159}
160
161func TestColonEqualsScope(t *testing.T) {
162	f, err := ParseFile(token.NewFileSet(), "", `package p; func f() { x, y, z := x, y, z }`, 0)
163	if err != nil {
164		t.Fatal(err)
165	}
166
167	// RHS refers to undefined globals; LHS does not.
168	as := f.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.AssignStmt)
169	for _, v := range as.Rhs {
170		id := v.(*ast.Ident)
171		if id.Obj != nil {
172			t.Errorf("rhs %s has Obj, should not", id.Name)
173		}
174	}
175	for _, v := range as.Lhs {
176		id := v.(*ast.Ident)
177		if id.Obj == nil {
178			t.Errorf("lhs %s does not have Obj, should", id.Name)
179		}
180	}
181}
182
183func TestVarScope(t *testing.T) {
184	f, err := ParseFile(token.NewFileSet(), "", `package p; func f() { var x, y, z = x, y, z }`, 0)
185	if err != nil {
186		t.Fatal(err)
187	}
188
189	// RHS refers to undefined globals; LHS does not.
190	as := f.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.DeclStmt).Decl.(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
191	for _, v := range as.Values {
192		id := v.(*ast.Ident)
193		if id.Obj != nil {
194			t.Errorf("rhs %s has Obj, should not", id.Name)
195		}
196	}
197	for _, id := range as.Names {
198		if id.Obj == nil {
199			t.Errorf("lhs %s does not have Obj, should", id.Name)
200		}
201	}
202}
203
204func TestObjects(t *testing.T) {
205	const src = `
206package p
207import fmt "fmt"
208const pi = 3.14
209type T struct{}
210var x int
211func f() { L: }
212`
213
214	f, err := ParseFile(token.NewFileSet(), "", src, 0)
215	if err != nil {
216		t.Fatal(err)
217	}
218
219	objects := map[string]ast.ObjKind{
220		"p":   ast.Bad, // not in a scope
221		"fmt": ast.Bad, // not resolved yet
222		"pi":  ast.Con,
223		"T":   ast.Typ,
224		"x":   ast.Var,
225		"int": ast.Bad, // not resolved yet
226		"f":   ast.Fun,
227		"L":   ast.Lbl,
228	}
229
230	ast.Inspect(f, func(n ast.Node) bool {
231		if ident, ok := n.(*ast.Ident); ok {
232			obj := ident.Obj
233			if obj == nil {
234				if objects[ident.Name] != ast.Bad {
235					t.Errorf("no object for %s", ident.Name)
236				}
237				return true
238			}
239			if obj.Name != ident.Name {
240				t.Errorf("names don't match: obj.Name = %s, ident.Name = %s", obj.Name, ident.Name)
241			}
242			kind := objects[ident.Name]
243			if obj.Kind != kind {
244				t.Errorf("%s: obj.Kind = %s; want %s", ident.Name, obj.Kind, kind)
245			}
246		}
247		return true
248	})
249}
250
251func TestUnresolved(t *testing.T) {
252	f, err := ParseFile(token.NewFileSet(), "", `
253package p
254//
255func f1a(int)
256func f2a(byte, int, float)
257func f3a(a, b int, c float)
258func f4a(...complex)
259func f5a(a s1a, b ...complex)
260//
261func f1b(*int)
262func f2b([]byte, (int), *float)
263func f3b(a, b *int, c []float)
264func f4b(...*complex)
265func f5b(a s1a, b ...[]complex)
266//
267type s1a struct { int }
268type s2a struct { byte; int; s1a }
269type s3a struct { a, b int; c float }
270//
271type s1b struct { *int }
272type s2b struct { byte; int; *float }
273type s3b struct { a, b *s3b; c []float }
274`, 0)
275	if err != nil {
276		t.Fatal(err)
277	}
278
279	want := "int " + // f1a
280		"byte int float " + // f2a
281		"int float " + // f3a
282		"complex " + // f4a
283		"complex " + // f5a
284		//
285		"int " + // f1b
286		"byte int float " + // f2b
287		"int float " + // f3b
288		"complex " + // f4b
289		"complex " + // f5b
290		//
291		"int " + // s1a
292		"byte int " + // s2a
293		"int float " + // s3a
294		//
295		"int " + // s1a
296		"byte int float " + // s2a
297		"float " // s3a
298
299	// collect unresolved identifiers
300	var buf strings.Builder
301	for _, u := range f.Unresolved {
302		buf.WriteString(u.Name)
303		buf.WriteByte(' ')
304	}
305	got := buf.String()
306
307	if got != want {
308		t.Errorf("\ngot:  %s\nwant: %s", got, want)
309	}
310}
311
312func TestCommentGroups(t *testing.T) {
313	f, err := ParseFile(token.NewFileSet(), "", `
314package p /* 1a */ /* 1b */      /* 1c */ // 1d
315/* 2a
316*/
317// 2b
318const pi = 3.1415
319/* 3a */ // 3b
320/* 3c */ const e = 2.7182
321
322// Example from go.dev/issue/3139
323func ExampleCount() {
324	fmt.Println(strings.Count("cheese", "e"))
325	fmt.Println(strings.Count("five", "")) // before & after each rune
326	// Output:
327	// 3
328	// 5
329}
330`, ParseComments)
331	if err != nil {
332		t.Fatal(err)
333	}
334	expected := [][]string{
335		{"/* 1a */", "/* 1b */", "/* 1c */", "// 1d"},
336		{"/* 2a\n*/", "// 2b"},
337		{"/* 3a */", "// 3b", "/* 3c */"},
338		{"// Example from go.dev/issue/3139"},
339		{"// before & after each rune"},
340		{"// Output:", "// 3", "// 5"},
341	}
342	if len(f.Comments) != len(expected) {
343		t.Fatalf("got %d comment groups; expected %d", len(f.Comments), len(expected))
344	}
345	for i, exp := range expected {
346		got := f.Comments[i].List
347		if len(got) != len(exp) {
348			t.Errorf("got %d comments in group %d; expected %d", len(got), i, len(exp))
349			continue
350		}
351		for j, exp := range exp {
352			got := got[j].Text
353			if got != exp {
354				t.Errorf("got %q in group %d; expected %q", got, i, exp)
355			}
356		}
357	}
358}
359
360func getField(file *ast.File, fieldname string) *ast.Field {
361	parts := strings.Split(fieldname, ".")
362	for _, d := range file.Decls {
363		if d, ok := d.(*ast.GenDecl); ok && d.Tok == token.TYPE {
364			for _, s := range d.Specs {
365				if s, ok := s.(*ast.TypeSpec); ok && s.Name.Name == parts[0] {
366					if s, ok := s.Type.(*ast.StructType); ok {
367						for _, f := range s.Fields.List {
368							for _, name := range f.Names {
369								if name.Name == parts[1] {
370									return f
371								}
372							}
373						}
374					}
375				}
376			}
377		}
378	}
379	return nil
380}
381
382// Don't use ast.CommentGroup.Text() - we want to see exact comment text.
383func commentText(c *ast.CommentGroup) string {
384	var buf strings.Builder
385	if c != nil {
386		for _, c := range c.List {
387			buf.WriteString(c.Text)
388		}
389	}
390	return buf.String()
391}
392
393func checkFieldComments(t *testing.T, file *ast.File, fieldname, lead, line string) {
394	f := getField(file, fieldname)
395	if f == nil {
396		t.Fatalf("field not found: %s", fieldname)
397	}
398	if got := commentText(f.Doc); got != lead {
399		t.Errorf("got lead comment %q; expected %q", got, lead)
400	}
401	if got := commentText(f.Comment); got != line {
402		t.Errorf("got line comment %q; expected %q", got, line)
403	}
404}
405
406func TestLeadAndLineComments(t *testing.T) {
407	f, err := ParseFile(token.NewFileSet(), "", `
408package p
409type T struct {
410	/* F1 lead comment */
411	//
412	F1 int  /* F1 */ // line comment
413	// F2 lead
414	// comment
415	F2 int  // F2 line comment
416	// f3 lead comment
417	f3 int  // f3 line comment
418
419	f4 int   /* not a line comment */ ;
420        f5 int ; // f5 line comment
421	f6 int ; /* f6 line comment */
422	f7 int ; /*f7a*/ /*f7b*/ //f7c
423}
424`, ParseComments)
425	if err != nil {
426		t.Fatal(err)
427	}
428	checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
429	checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
430	checkFieldComments(t, f, "T.f3", "// f3 lead comment", "// f3 line comment")
431	checkFieldComments(t, f, "T.f4", "", "")
432	checkFieldComments(t, f, "T.f5", "", "// f5 line comment")
433	checkFieldComments(t, f, "T.f6", "", "/* f6 line comment */")
434	checkFieldComments(t, f, "T.f7", "", "/*f7a*//*f7b*///f7c")
435
436	ast.FileExports(f)
437	checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
438	checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
439	if getField(f, "T.f3") != nil {
440		t.Error("not expected to find T.f3")
441	}
442}
443
444// TestIssue9979 verifies that empty statements are contained within their enclosing blocks.
445func TestIssue9979(t *testing.T) {
446	for _, src := range []string{
447		"package p; func f() {;}",
448		"package p; func f() {L:}",
449		"package p; func f() {L:;}",
450		"package p; func f() {L:\n}",
451		"package p; func f() {L:\n;}",
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	} {
458		fset := token.NewFileSet()
459		f, err := ParseFile(fset, "", src, 0)
460		if err != nil {
461			t.Fatal(err)
462		}
463
464		var pos, end token.Pos
465		ast.Inspect(f, func(x ast.Node) bool {
466			switch s := x.(type) {
467			case *ast.BlockStmt:
468				pos, end = s.Pos()+1, s.End()-1 // exclude "{", "}"
469			case *ast.LabeledStmt:
470				pos, end = s.Pos()+2, s.End() // exclude "L:"
471			case *ast.EmptyStmt:
472				// check containment
473				if s.Pos() < pos || s.End() > end {
474					t.Errorf("%s: %T[%d, %d] not inside [%d, %d]", src, s, s.Pos(), s.End(), pos, end)
475				}
476				// check semicolon
477				offs := fset.Position(s.Pos()).Offset
478				if ch := src[offs]; ch != ';' != s.Implicit {
479					want := "want ';'"
480					if s.Implicit {
481						want = "but ';' is implicit"
482					}
483					t.Errorf("%s: found %q at offset %d; %s", src, ch, offs, want)
484				}
485			}
486			return true
487		})
488	}
489}
490
491func TestFileStartEndPos(t *testing.T) {
492	const src = `// Copyright
493
494//+build tag
495
496// Package p doc comment.
497package p
498
499var lastDecl int
500
501/* end of file */
502`
503	fset := token.NewFileSet()
504	f, err := ParseFile(fset, "file.go", src, 0)
505	if err != nil {
506		t.Fatal(err)
507	}
508
509	// File{Start,End} spans the entire file, not just the declarations.
510	if got, want := fset.Position(f.FileStart).String(), "file.go:1:1"; got != want {
511		t.Errorf("for File.FileStart, got %s, want %s", got, want)
512	}
513	// The end position is the newline at the end of the /* end of file */ line.
514	if got, want := fset.Position(f.FileEnd).String(), "file.go:10:19"; got != want {
515		t.Errorf("for File.FileEnd, got %s, want %s", got, want)
516	}
517}
518
519// TestIncompleteSelection ensures that an incomplete selector
520// expression is parsed as a (blank) *ast.SelectorExpr, not a
521// *ast.BadExpr.
522func TestIncompleteSelection(t *testing.T) {
523	for _, src := range []string{
524		"package p; var _ = fmt.",             // at EOF
525		"package p; var _ = fmt.\ntype X int", // not at EOF
526	} {
527		fset := token.NewFileSet()
528		f, err := ParseFile(fset, "", src, 0)
529		if err == nil {
530			t.Errorf("ParseFile(%s) succeeded unexpectedly", src)
531			continue
532		}
533
534		const wantErr = "expected selector or type assertion"
535		if !strings.Contains(err.Error(), wantErr) {
536			t.Errorf("ParseFile returned wrong error %q, want %q", err, wantErr)
537		}
538
539		var sel *ast.SelectorExpr
540		ast.Inspect(f, func(n ast.Node) bool {
541			if n, ok := n.(*ast.SelectorExpr); ok {
542				sel = n
543			}
544			return true
545		})
546		if sel == nil {
547			t.Error("found no *ast.SelectorExpr")
548			continue
549		}
550		const wantSel = "&{fmt _}"
551		if fmt.Sprint(sel) != wantSel {
552			t.Errorf("found selector %s, want %s", sel, wantSel)
553			continue
554		}
555	}
556}
557
558func TestLastLineComment(t *testing.T) {
559	const src = `package main
560type x int // comment
561`
562	fset := token.NewFileSet()
563	f, err := ParseFile(fset, "", src, ParseComments)
564	if err != nil {
565		t.Fatal(err)
566	}
567	comment := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.TypeSpec).Comment.List[0].Text
568	if comment != "// comment" {
569		t.Errorf("got %q, want %q", comment, "// comment")
570	}
571}
572
573var parseDepthTests = []struct {
574	name   string
575	format string
576	// parseMultiplier is used when a single statement may result in more than one
577	// change in the depth level, for instance "1+(..." produces a BinaryExpr
578	// followed by a UnaryExpr, which increments the depth twice. The test
579	// case comment explains which nodes are triggering the multiple depth
580	// changes.
581	parseMultiplier int
582	// scope is true if we should also test the statement for the resolver scope
583	// depth limit.
584	scope bool
585	// scopeMultiplier does the same as parseMultiplier, but for the scope
586	// depths.
587	scopeMultiplier int
588}{
589	// The format expands the part inside « » many times.
590	// A second set of brackets nested inside the first stops the repetition,
591	// so that for example «(«1»)» expands to (((...((((1))))...))).
592	{name: "array", format: "package main; var x «[1]»int"},
593	{name: "slice", format: "package main; var x «[]»int"},
594	{name: "struct", format: "package main; var x «struct { X «int» }»", scope: true},
595	{name: "pointer", format: "package main; var x «*»int"},
596	{name: "func", format: "package main; var x «func()»int", scope: true},
597	{name: "chan", format: "package main; var x «chan »int"},
598	{name: "chan2", format: "package main; var x «<-chan »int"},
599	{name: "interface", format: "package main; var x «interface { M() «int» }»", scope: true, scopeMultiplier: 2}, // Scopes: InterfaceType, FuncType
600	{name: "map", format: "package main; var x «map[int]»int"},
601	{name: "slicelit", format: "package main; var x = []any{«[]any{«»}»}", parseMultiplier: 3},      // Parser nodes: UnaryExpr, CompositeLit
602	{name: "arraylit", format: "package main; var x = «[1]any{«nil»}»", parseMultiplier: 3},         // Parser nodes: UnaryExpr, CompositeLit
603	{name: "structlit", format: "package main; var x = «struct{x any}{«nil»}»", parseMultiplier: 3}, // Parser nodes: UnaryExpr, CompositeLit
604	{name: "maplit", format: "package main; var x = «map[int]any{1:«nil»}»", parseMultiplier: 3},    // Parser nodes: CompositeLit, KeyValueExpr
605	{name: "element", format: "package main; var x = struct{x any}{x: «{«»}»}"},
606	{name: "dot", format: "package main; var x = «x.»x"},
607	{name: "index", format: "package main; var x = x«[1]»"},
608	{name: "slice", format: "package main; var x = x«[1:2]»"},
609	{name: "slice3", format: "package main; var x = x«[1:2:3]»"},
610	{name: "dottype", format: "package main; var x = x«.(any)»"},
611	{name: "callseq", format: "package main; var x = x«()»"},
612	{name: "methseq", format: "package main; var x = x«.m()»", parseMultiplier: 2}, // Parser nodes: SelectorExpr, CallExpr
613	{name: "binary", format: "package main; var x = «1+»1"},
614	{name: "binaryparen", format: "package main; var x = «1+(«1»)»", parseMultiplier: 2}, // Parser nodes: BinaryExpr, ParenExpr
615	{name: "unary", format: "package main; var x = «^»1"},
616	{name: "addr", format: "package main; var x = «& »x"},
617	{name: "star", format: "package main; var x = «*»x"},
618	{name: "recv", format: "package main; var x = «<-»x"},
619	{name: "call", format: "package main; var x = «f(«1»)»", parseMultiplier: 2},    // Parser nodes: Ident, CallExpr
620	{name: "conv", format: "package main; var x = «(*T)(«1»)»", parseMultiplier: 2}, // Parser nodes: ParenExpr, CallExpr
621	{name: "label", format: "package main; func main() { «Label:» }"},
622	{name: "if", format: "package main; func main() { «if true { «» }»}", parseMultiplier: 2, scope: true, scopeMultiplier: 2}, // Parser nodes: IfStmt, BlockStmt. Scopes: IfStmt, BlockStmt
623	{name: "ifelse", format: "package main; func main() { «if true {} else » {} }", scope: true},
624	{name: "switch", format: "package main; func main() { «switch { default: «» }»}", scope: true, scopeMultiplier: 2},               // Scopes: TypeSwitchStmt, CaseClause
625	{name: "typeswitch", format: "package main; func main() { «switch x.(type) { default: «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: TypeSwitchStmt, CaseClause
626	{name: "for0", format: "package main; func main() { «for { «» }» }", scope: true, scopeMultiplier: 2},                            // Scopes: ForStmt, BlockStmt
627	{name: "for1", format: "package main; func main() { «for x { «» }» }", scope: true, scopeMultiplier: 2},                          // Scopes: ForStmt, BlockStmt
628	{name: "for3", format: "package main; func main() { «for f(); g(); h() { «» }» }", scope: true, scopeMultiplier: 2},              // Scopes: ForStmt, BlockStmt
629	{name: "forrange0", format: "package main; func main() { «for range x { «» }» }", scope: true, scopeMultiplier: 2},               // Scopes: RangeStmt, BlockStmt
630	{name: "forrange1", format: "package main; func main() { «for x = range z { «» }» }", scope: true, scopeMultiplier: 2},           // Scopes: RangeStmt, BlockStmt
631	{name: "forrange2", format: "package main; func main() { «for x, y = range z { «» }» }", scope: true, scopeMultiplier: 2},        // Scopes: RangeStmt, BlockStmt
632	{name: "go", format: "package main; func main() { «go func() { «» }()» }", parseMultiplier: 2, scope: true},                      // Parser nodes: GoStmt, FuncLit
633	{name: "defer", format: "package main; func main() { «defer func() { «» }()» }", parseMultiplier: 2, scope: true},                // Parser nodes: DeferStmt, FuncLit
634	{name: "select", format: "package main; func main() { «select { default: «» }» }", scope: true},
635}
636
637// split splits pre«mid»post into pre, mid, post.
638// If the string does not have that form, split returns x, "", "".
639func split(x string) (pre, mid, post string) {
640	start, end := strings.Index(x, "«"), strings.LastIndex(x, "»")
641	if start < 0 || end < 0 {
642		return x, "", ""
643	}
644	return x[:start], x[start+len("«") : end], x[end+len("»"):]
645}
646
647func TestParseDepthLimit(t *testing.T) {
648	if testing.Short() {
649		t.Skip("test requires significant memory")
650	}
651	for _, tt := range parseDepthTests {
652		for _, size := range []string{"small", "big"} {
653			t.Run(tt.name+"/"+size, func(t *testing.T) {
654				n := maxNestLev + 1
655				if tt.parseMultiplier > 0 {
656					n /= tt.parseMultiplier
657				}
658				if size == "small" {
659					// Decrease the number of statements by 10, in order to check
660					// that we do not fail when under the limit. 10 is used to
661					// provide some wiggle room for cases where the surrounding
662					// scaffolding syntax adds some noise to the depth that changes
663					// on a per testcase basis.
664					n -= 10
665				}
666
667				pre, mid, post := split(tt.format)
668				if strings.Contains(mid, "«") {
669					left, base, right := split(mid)
670					mid = strings.Repeat(left, n) + base + strings.Repeat(right, n)
671				} else {
672					mid = strings.Repeat(mid, n)
673				}
674				input := pre + mid + post
675
676				fset := token.NewFileSet()
677				_, err := ParseFile(fset, "", input, ParseComments|SkipObjectResolution)
678				if size == "small" {
679					if err != nil {
680						t.Errorf("ParseFile(...): %v (want success)", err)
681					}
682				} else {
683					expected := "exceeded max nesting depth"
684					if err == nil || !strings.HasSuffix(err.Error(), expected) {
685						t.Errorf("ParseFile(...) = _, %v, want %q", err, expected)
686					}
687				}
688			})
689		}
690	}
691}
692
693func TestScopeDepthLimit(t *testing.T) {
694	for _, tt := range parseDepthTests {
695		if !tt.scope {
696			continue
697		}
698		for _, size := range []string{"small", "big"} {
699			t.Run(tt.name+"/"+size, func(t *testing.T) {
700				n := maxScopeDepth + 1
701				if tt.scopeMultiplier > 0 {
702					n /= tt.scopeMultiplier
703				}
704				if size == "small" {
705					// Decrease the number of statements by 10, in order to check
706					// that we do not fail when under the limit. 10 is used to
707					// provide some wiggle room for cases where the surrounding
708					// scaffolding syntax adds some noise to the depth that changes
709					// on a per testcase basis.
710					n -= 10
711				}
712
713				pre, mid, post := split(tt.format)
714				if strings.Contains(mid, "«") {
715					left, base, right := split(mid)
716					mid = strings.Repeat(left, n) + base + strings.Repeat(right, n)
717				} else {
718					mid = strings.Repeat(mid, n)
719				}
720				input := pre + mid + post
721
722				fset := token.NewFileSet()
723				_, err := ParseFile(fset, "", input, DeclarationErrors)
724				if size == "small" {
725					if err != nil {
726						t.Errorf("ParseFile(...): %v (want success)", err)
727					}
728				} else {
729					expected := "exceeded max scope depth during object resolution"
730					if err == nil || !strings.HasSuffix(err.Error(), expected) {
731						t.Errorf("ParseFile(...) = _, %v, want %q", err, expected)
732					}
733				}
734			})
735		}
736	}
737}
738
739// proposal go.dev/issue/50429
740func TestRangePos(t *testing.T) {
741	testcases := []string{
742		"package p; func _() { for range x {} }",
743		"package p; func _() { for i = range x {} }",
744		"package p; func _() { for i := range x {} }",
745		"package p; func _() { for k, v = range x {} }",
746		"package p; func _() { for k, v := range x {} }",
747	}
748
749	for _, src := range testcases {
750		fset := token.NewFileSet()
751		f, err := ParseFile(fset, src, src, 0)
752		if err != nil {
753			t.Fatal(err)
754		}
755
756		ast.Inspect(f, func(x ast.Node) bool {
757			switch s := x.(type) {
758			case *ast.RangeStmt:
759				pos := fset.Position(s.Range)
760				if pos.Offset != strings.Index(src, "range") {
761					t.Errorf("%s: got offset %v, want %v", src, pos.Offset, strings.Index(src, "range"))
762				}
763			}
764			return true
765		})
766	}
767}
768
769// TestIssue59180 tests that line number overflow doesn't cause an infinite loop.
770func TestIssue59180(t *testing.T) {
771	testcases := []string{
772		"package p\n//line :9223372036854775806\n\n//",
773		"package p\n//line :1:9223372036854775806\n\n//",
774		"package p\n//line file:9223372036854775806\n\n//",
775	}
776
777	for _, src := range testcases {
778		_, err := ParseFile(token.NewFileSet(), "", src, ParseComments)
779		if err == nil {
780			t.Errorf("ParseFile(%s) succeeded unexpectedly", src)
781		}
782	}
783}
784
785func TestGoVersion(t *testing.T) {
786	fset := token.NewFileSet()
787	pkgs, err := ParseDir(fset, "./testdata/goversion", nil, 0)
788	if err != nil {
789		t.Fatal(err)
790	}
791
792	for _, p := range pkgs {
793		want := strings.ReplaceAll(p.Name, "_", ".")
794		if want == "none" {
795			want = ""
796		}
797		for _, f := range p.Files {
798			if f.GoVersion != want {
799				t.Errorf("%s: GoVersion = %q, want %q", fset.Position(f.Pos()), f.GoVersion, want)
800			}
801		}
802	}
803}
804
805func TestIssue57490(t *testing.T) {
806	src := `package p; func f() { var x struct` // program not correctly terminated
807	fset := token.NewFileSet()
808	file, err := ParseFile(fset, "", src, 0)
809	if err == nil {
810		t.Fatalf("syntax error expected, but no error reported")
811	}
812
813	// Because of the syntax error, the end position of the function declaration
814	// is past the end of the file's position range.
815	funcEnd := file.Decls[0].End()
816
817	// Offset(funcEnd) must not panic (to test panic, set debug=true in token package)
818	// (panic: offset 35 out of bounds [0, 34] (position 36 out of bounds [1, 35]))
819	tokFile := fset.File(file.Pos())
820	offset := tokFile.Offset(funcEnd)
821	if offset != tokFile.Size() {
822		t.Fatalf("offset = %d, want %d", offset, tokFile.Size())
823	}
824}
825