Source file src/go/parser/parser_test.go

     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  
     5  package parser
     6  
     7  import (
     8  	"fmt"
     9  	"go/ast"
    10  	"go/token"
    11  	"io/fs"
    12  	"reflect"
    13  	"strings"
    14  	"testing"
    15  )
    16  
    17  var validFiles = []string{
    18  	"parser.go",
    19  	"parser_test.go",
    20  	"error_test.go",
    21  	"short_test.go",
    22  }
    23  
    24  func 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  
    33  func 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  
    43  func dirFilter(f fs.FileInfo) bool { return nameFilter(f.Name()) }
    44  
    45  func 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  
    53  func 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  
    61  func 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  
    85  func 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  
    93  func 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  
   162  func 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  
   184  func 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  
   205  func TestObjects(t *testing.T) {
   206  	const src = `
   207  package p
   208  import fmt "fmt"
   209  const pi = 3.14
   210  type T struct{}
   211  var x int
   212  func 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  
   252  func TestUnresolved(t *testing.T) {
   253  	f, err := ParseFile(token.NewFileSet(), "", `
   254  package p
   255  //
   256  func f1a(int)
   257  func f2a(byte, int, float)
   258  func f3a(a, b int, c float)
   259  func f4a(...complex)
   260  func f5a(a s1a, b ...complex)
   261  //
   262  func f1b(*int)
   263  func f2b([]byte, (int), *float)
   264  func f3b(a, b *int, c []float)
   265  func f4b(...*complex)
   266  func f5b(a s1a, b ...[]complex)
   267  //
   268  type s1a struct { int }
   269  type s2a struct { byte; int; s1a }
   270  type s3a struct { a, b int; c float }
   271  //
   272  type s1b struct { *int }
   273  type s2b struct { byte; int; *float }
   274  type 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 strings.Builder
   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  
   313  func TestCommentGroups(t *testing.T) {
   314  	f, err := ParseFile(token.NewFileSet(), "", `
   315  package p /* 1a */ /* 1b */      /* 1c */ // 1d
   316  /* 2a
   317  */
   318  // 2b
   319  const pi = 3.1415
   320  /* 3a */ // 3b
   321  /* 3c */ const e = 2.7182
   322  
   323  // Example from go.dev/issue/3139
   324  func ExampleCount() {
   325  	fmt.Println(strings.Count("cheese", "e"))
   326  	fmt.Println(strings.Count("five", "")) // before & after each rune
   327  	// Output:
   328  	// 3
   329  	// 5
   330  }
   331  `, ParseComments)
   332  	if err != nil {
   333  		t.Fatal(err)
   334  	}
   335  	expected := [][]string{
   336  		{"/* 1a */", "/* 1b */", "/* 1c */", "// 1d"},
   337  		{"/* 2a\n*/", "// 2b"},
   338  		{"/* 3a */", "// 3b", "/* 3c */"},
   339  		{"// Example from go.dev/issue/3139"},
   340  		{"// before & after each rune"},
   341  		{"// Output:", "// 3", "// 5"},
   342  	}
   343  	if len(f.Comments) != len(expected) {
   344  		t.Fatalf("got %d comment groups; expected %d", len(f.Comments), len(expected))
   345  	}
   346  	for i, exp := range expected {
   347  		got := f.Comments[i].List
   348  		if len(got) != len(exp) {
   349  			t.Errorf("got %d comments in group %d; expected %d", len(got), i, len(exp))
   350  			continue
   351  		}
   352  		for j, exp := range exp {
   353  			got := got[j].Text
   354  			if got != exp {
   355  				t.Errorf("got %q in group %d; expected %q", got, i, exp)
   356  			}
   357  		}
   358  	}
   359  }
   360  
   361  func getField(file *ast.File, fieldname string) *ast.Field {
   362  	parts := strings.Split(fieldname, ".")
   363  	for _, d := range file.Decls {
   364  		if d, ok := d.(*ast.GenDecl); ok && d.Tok == token.TYPE {
   365  			for _, s := range d.Specs {
   366  				if s, ok := s.(*ast.TypeSpec); ok && s.Name.Name == parts[0] {
   367  					if s, ok := s.Type.(*ast.StructType); ok {
   368  						for _, f := range s.Fields.List {
   369  							for _, name := range f.Names {
   370  								if name.Name == parts[1] {
   371  									return f
   372  								}
   373  							}
   374  						}
   375  					}
   376  				}
   377  			}
   378  		}
   379  	}
   380  	return nil
   381  }
   382  
   383  // Don't use ast.CommentGroup.Text() - we want to see exact comment text.
   384  func commentText(c *ast.CommentGroup) string {
   385  	var buf strings.Builder
   386  	if c != nil {
   387  		for _, c := range c.List {
   388  			buf.WriteString(c.Text)
   389  		}
   390  	}
   391  	return buf.String()
   392  }
   393  
   394  func checkFieldComments(t *testing.T, file *ast.File, fieldname, lead, line string) {
   395  	f := getField(file, fieldname)
   396  	if f == nil {
   397  		t.Fatalf("field not found: %s", fieldname)
   398  	}
   399  	if got := commentText(f.Doc); got != lead {
   400  		t.Errorf("got lead comment %q; expected %q", got, lead)
   401  	}
   402  	if got := commentText(f.Comment); got != line {
   403  		t.Errorf("got line comment %q; expected %q", got, line)
   404  	}
   405  }
   406  
   407  func TestLeadAndLineComments(t *testing.T) {
   408  	f, err := ParseFile(token.NewFileSet(), "", `
   409  package p
   410  type T struct {
   411  	/* F1 lead comment */
   412  	//
   413  	F1 int  /* F1 */ // line comment
   414  	// F2 lead
   415  	// comment
   416  	F2 int  // F2 line comment
   417  	// f3 lead comment
   418  	f3 int  // f3 line comment
   419  
   420  	f4 int   /* not a line comment */ ;
   421          f5 int ; // f5 line comment
   422  	f6 int ; /* f6 line comment */
   423  	f7 int ; /*f7a*/ /*f7b*/ //f7c
   424  }
   425  `, ParseComments)
   426  	if err != nil {
   427  		t.Fatal(err)
   428  	}
   429  	checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
   430  	checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
   431  	checkFieldComments(t, f, "T.f3", "// f3 lead comment", "// f3 line comment")
   432  	checkFieldComments(t, f, "T.f4", "", "")
   433  	checkFieldComments(t, f, "T.f5", "", "// f5 line comment")
   434  	checkFieldComments(t, f, "T.f6", "", "/* f6 line comment */")
   435  	checkFieldComments(t, f, "T.f7", "", "/*f7a*//*f7b*///f7c")
   436  
   437  	ast.FileExports(f)
   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  	if getField(f, "T.f3") != nil {
   441  		t.Error("not expected to find T.f3")
   442  	}
   443  }
   444  
   445  // TestIssue9979 verifies that empty statements are contained within their enclosing blocks.
   446  func TestIssue9979(t *testing.T) {
   447  	for _, src := range []string{
   448  		"package p; func f() {;}",
   449  		"package p; func f() {L:}",
   450  		"package p; func f() {L:;}",
   451  		"package p; func f() {L:\n}",
   452  		"package p; func f() {L:\n;}",
   453  		"package p; func f() { ; }",
   454  		"package p; func f() { L: }",
   455  		"package p; func f() { L: ; }",
   456  		"package p; func f() { L: \n}",
   457  		"package p; func f() { L: \n; }",
   458  	} {
   459  		fset := token.NewFileSet()
   460  		f, err := ParseFile(fset, "", src, 0)
   461  		if err != nil {
   462  			t.Fatal(err)
   463  		}
   464  
   465  		var pos, end token.Pos
   466  		ast.Inspect(f, func(x ast.Node) bool {
   467  			switch s := x.(type) {
   468  			case *ast.BlockStmt:
   469  				pos, end = s.Pos()+1, s.End()-1 // exclude "{", "}"
   470  			case *ast.LabeledStmt:
   471  				pos, end = s.Pos()+2, s.End() // exclude "L:"
   472  			case *ast.EmptyStmt:
   473  				// check containment
   474  				if s.Pos() < pos || s.End() > end {
   475  					t.Errorf("%s: %T[%d, %d] not inside [%d, %d]", src, s, s.Pos(), s.End(), pos, end)
   476  				}
   477  				// check semicolon
   478  				offs := fset.Position(s.Pos()).Offset
   479  				if ch := src[offs]; ch != ';' != s.Implicit {
   480  					want := "want ';'"
   481  					if s.Implicit {
   482  						want = "but ';' is implicit"
   483  					}
   484  					t.Errorf("%s: found %q at offset %d; %s", src, ch, offs, want)
   485  				}
   486  			}
   487  			return true
   488  		})
   489  	}
   490  }
   491  
   492  func TestFileStartEndPos(t *testing.T) {
   493  	const src = `// Copyright
   494  
   495  //+build tag
   496  
   497  // Package p doc comment.
   498  package p
   499  
   500  var lastDecl int
   501  
   502  /* end of file */
   503  `
   504  	fset := token.NewFileSet()
   505  	f, err := ParseFile(fset, "file.go", src, 0)
   506  	if err != nil {
   507  		t.Fatal(err)
   508  	}
   509  
   510  	// File{Start,End} spans the entire file, not just the declarations.
   511  	if got, want := fset.Position(f.FileStart).String(), "file.go:1:1"; got != want {
   512  		t.Errorf("for File.FileStart, got %s, want %s", got, want)
   513  	}
   514  	// The end position is the newline at the end of the /* end of file */ line.
   515  	if got, want := fset.Position(f.FileEnd).String(), "file.go:10:19"; got != want {
   516  		t.Errorf("for File.FileEnd, got %s, want %s", got, want)
   517  	}
   518  }
   519  
   520  // TestIncompleteSelection ensures that an incomplete selector
   521  // expression is parsed as a (blank) *ast.SelectorExpr, not a
   522  // *ast.BadExpr.
   523  func TestIncompleteSelection(t *testing.T) {
   524  	for _, src := range []string{
   525  		"package p; var _ = fmt.",             // at EOF
   526  		"package p; var _ = fmt.\ntype X int", // not at EOF
   527  	} {
   528  		fset := token.NewFileSet()
   529  		f, err := ParseFile(fset, "", src, 0)
   530  		if err == nil {
   531  			t.Errorf("ParseFile(%s) succeeded unexpectedly", src)
   532  			continue
   533  		}
   534  
   535  		const wantErr = "expected selector or type assertion"
   536  		if !strings.Contains(err.Error(), wantErr) {
   537  			t.Errorf("ParseFile returned wrong error %q, want %q", err, wantErr)
   538  		}
   539  
   540  		var sel *ast.SelectorExpr
   541  		ast.Inspect(f, func(n ast.Node) bool {
   542  			if n, ok := n.(*ast.SelectorExpr); ok {
   543  				sel = n
   544  			}
   545  			return true
   546  		})
   547  		if sel == nil {
   548  			t.Error("found no *ast.SelectorExpr")
   549  			continue
   550  		}
   551  		const wantSel = "&{fmt _}"
   552  		if fmt.Sprint(sel) != wantSel {
   553  			t.Errorf("found selector %s, want %s", sel, wantSel)
   554  			continue
   555  		}
   556  	}
   557  }
   558  
   559  func TestLastLineComment(t *testing.T) {
   560  	const src = `package main
   561  type x int // comment
   562  `
   563  	fset := token.NewFileSet()
   564  	f, err := ParseFile(fset, "", src, ParseComments)
   565  	if err != nil {
   566  		t.Fatal(err)
   567  	}
   568  	comment := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.TypeSpec).Comment.List[0].Text
   569  	if comment != "// comment" {
   570  		t.Errorf("got %q, want %q", comment, "// comment")
   571  	}
   572  }
   573  
   574  var parseDepthTests = []struct {
   575  	name   string
   576  	format string
   577  	// parseMultiplier is used when a single statement may result in more than one
   578  	// change in the depth level, for instance "1+(..." produces a BinaryExpr
   579  	// followed by a UnaryExpr, which increments the depth twice. The test
   580  	// case comment explains which nodes are triggering the multiple depth
   581  	// changes.
   582  	parseMultiplier int
   583  	// scope is true if we should also test the statement for the resolver scope
   584  	// depth limit.
   585  	scope bool
   586  	// scopeMultiplier does the same as parseMultiplier, but for the scope
   587  	// depths.
   588  	scopeMultiplier int
   589  }{
   590  	// The format expands the part inside « » many times.
   591  	// A second set of brackets nested inside the first stops the repetition,
   592  	// so that for example «(«1»)» expands to (((...((((1))))...))).
   593  	{name: "array", format: "package main; var x «[1]»int"},
   594  	{name: "slice", format: "package main; var x «[]»int"},
   595  	{name: "struct", format: "package main; var x «struct { X «int» }»", scope: true},
   596  	{name: "pointer", format: "package main; var x «*»int"},
   597  	{name: "func", format: "package main; var x «func()»int", scope: true},
   598  	{name: "chan", format: "package main; var x «chan »int"},
   599  	{name: "chan2", format: "package main; var x «<-chan »int"},
   600  	{name: "interface", format: "package main; var x «interface { M() «int» }»", scope: true, scopeMultiplier: 2}, // Scopes: InterfaceType, FuncType
   601  	{name: "map", format: "package main; var x «map[int]»int"},
   602  	{name: "slicelit", format: "package main; var x = []any{«[]any{«»}»}", parseMultiplier: 3},      // Parser nodes: UnaryExpr, CompositeLit
   603  	{name: "arraylit", format: "package main; var x = «[1]any{«nil»}»", parseMultiplier: 3},         // Parser nodes: UnaryExpr, CompositeLit
   604  	{name: "structlit", format: "package main; var x = «struct{x any}{«nil»}»", parseMultiplier: 3}, // Parser nodes: UnaryExpr, CompositeLit
   605  	{name: "maplit", format: "package main; var x = «map[int]any{1:«nil»}»", parseMultiplier: 3},    // Parser nodes: CompositeLit, KeyValueExpr
   606  	{name: "element", format: "package main; var x = struct{x any}{x: «{«»}»}"},
   607  	{name: "dot", format: "package main; var x = «x.»x"},
   608  	{name: "index", format: "package main; var x = x«[1]»"},
   609  	{name: "slice", format: "package main; var x = x«[1:2]»"},
   610  	{name: "slice3", format: "package main; var x = x«[1:2:3]»"},
   611  	{name: "dottype", format: "package main; var x = x«.(any)»"},
   612  	{name: "callseq", format: "package main; var x = x«()»"},
   613  	{name: "methseq", format: "package main; var x = x«.m()»", parseMultiplier: 2}, // Parser nodes: SelectorExpr, CallExpr
   614  	{name: "binary", format: "package main; var x = «1+»1"},
   615  	{name: "binaryparen", format: "package main; var x = «1+(«1»)»", parseMultiplier: 2}, // Parser nodes: BinaryExpr, ParenExpr
   616  	{name: "unary", format: "package main; var x = «^»1"},
   617  	{name: "addr", format: "package main; var x = «& »x"},
   618  	{name: "star", format: "package main; var x = «*»x"},
   619  	{name: "recv", format: "package main; var x = «<-»x"},
   620  	{name: "call", format: "package main; var x = «f(«1»)»", parseMultiplier: 2},    // Parser nodes: Ident, CallExpr
   621  	{name: "conv", format: "package main; var x = «(*T)(«1»)»", parseMultiplier: 2}, // Parser nodes: ParenExpr, CallExpr
   622  	{name: "label", format: "package main; func main() { «Label:» }"},
   623  	{name: "if", format: "package main; func main() { «if true { «» }»}", parseMultiplier: 2, scope: true, scopeMultiplier: 2}, // Parser nodes: IfStmt, BlockStmt. Scopes: IfStmt, BlockStmt
   624  	{name: "ifelse", format: "package main; func main() { «if true {} else » {} }", scope: true},
   625  	{name: "switch", format: "package main; func main() { «switch { default: «» }»}", scope: true, scopeMultiplier: 2},               // Scopes: TypeSwitchStmt, CaseClause
   626  	{name: "typeswitch", format: "package main; func main() { «switch x.(type) { default: «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: TypeSwitchStmt, CaseClause
   627  	{name: "for0", format: "package main; func main() { «for { «» }» }", scope: true, scopeMultiplier: 2},                            // Scopes: ForStmt, BlockStmt
   628  	{name: "for1", format: "package main; func main() { «for x { «» }» }", scope: true, scopeMultiplier: 2},                          // Scopes: ForStmt, BlockStmt
   629  	{name: "for3", format: "package main; func main() { «for f(); g(); h() { «» }» }", scope: true, scopeMultiplier: 2},              // Scopes: ForStmt, BlockStmt
   630  	{name: "forrange0", format: "package main; func main() { «for range x { «» }» }", scope: true, scopeMultiplier: 2},               // Scopes: RangeStmt, BlockStmt
   631  	{name: "forrange1", format: "package main; func main() { «for x = range z { «» }» }", scope: true, scopeMultiplier: 2},           // Scopes: RangeStmt, BlockStmt
   632  	{name: "forrange2", format: "package main; func main() { «for x, y = range z { «» }» }", scope: true, scopeMultiplier: 2},        // Scopes: RangeStmt, BlockStmt
   633  	{name: "go", format: "package main; func main() { «go func() { «» }()» }", parseMultiplier: 2, scope: true},                      // Parser nodes: GoStmt, FuncLit
   634  	{name: "defer", format: "package main; func main() { «defer func() { «» }()» }", parseMultiplier: 2, scope: true},                // Parser nodes: DeferStmt, FuncLit
   635  	{name: "select", format: "package main; func main() { «select { default: «» }» }", scope: true},
   636  	{name: "block", format: "package main; func main() { «{«»}» }", scope: true},
   637  }
   638  
   639  // split splits pre«mid»post into pre, mid, post.
   640  // If the string does not have that form, split returns x, "", "".
   641  func split(x string) (pre, mid, post string) {
   642  	start, end := strings.Index(x, "«"), strings.LastIndex(x, "»")
   643  	if start < 0 || end < 0 {
   644  		return x, "", ""
   645  	}
   646  	return x[:start], x[start+len("«") : end], x[end+len("»"):]
   647  }
   648  
   649  func TestParseDepthLimit(t *testing.T) {
   650  	if testing.Short() {
   651  		t.Skip("test requires significant memory")
   652  	}
   653  	for _, tt := range parseDepthTests {
   654  		for _, size := range []string{"small", "big"} {
   655  			t.Run(tt.name+"/"+size, func(t *testing.T) {
   656  				n := maxNestLev + 1
   657  				if tt.parseMultiplier > 0 {
   658  					n /= tt.parseMultiplier
   659  				}
   660  				if size == "small" {
   661  					// Decrease the number of statements by 10, in order to check
   662  					// that we do not fail when under the limit. 10 is used to
   663  					// provide some wiggle room for cases where the surrounding
   664  					// scaffolding syntax adds some noise to the depth that changes
   665  					// on a per testcase basis.
   666  					n -= 10
   667  				}
   668  
   669  				pre, mid, post := split(tt.format)
   670  				if strings.Contains(mid, "«") {
   671  					left, base, right := split(mid)
   672  					mid = strings.Repeat(left, n) + base + strings.Repeat(right, n)
   673  				} else {
   674  					mid = strings.Repeat(mid, n)
   675  				}
   676  				input := pre + mid + post
   677  
   678  				fset := token.NewFileSet()
   679  				_, err := ParseFile(fset, "", input, ParseComments|SkipObjectResolution)
   680  				if size == "small" {
   681  					if err != nil {
   682  						t.Errorf("ParseFile(...): %v (want success)", err)
   683  					}
   684  				} else {
   685  					expected := "exceeded max nesting depth"
   686  					if err == nil || !strings.HasSuffix(err.Error(), expected) {
   687  						t.Errorf("ParseFile(...) = _, %v, want %q", err, expected)
   688  					}
   689  				}
   690  			})
   691  		}
   692  	}
   693  }
   694  
   695  func TestScopeDepthLimit(t *testing.T) {
   696  	for _, tt := range parseDepthTests {
   697  		if !tt.scope {
   698  			continue
   699  		}
   700  		for _, size := range []string{"small", "big"} {
   701  			t.Run(tt.name+"/"+size, func(t *testing.T) {
   702  				n := maxScopeDepth + 1
   703  				if tt.scopeMultiplier > 0 {
   704  					n /= tt.scopeMultiplier
   705  				}
   706  				if size == "small" {
   707  					// Decrease the number of statements by 10, in order to check
   708  					// that we do not fail when under the limit. 10 is used to
   709  					// provide some wiggle room for cases where the surrounding
   710  					// scaffolding syntax adds some noise to the depth that changes
   711  					// on a per testcase basis.
   712  					n -= 10
   713  				}
   714  
   715  				pre, mid, post := split(tt.format)
   716  				if strings.Contains(mid, "«") {
   717  					left, base, right := split(mid)
   718  					mid = strings.Repeat(left, n) + base + strings.Repeat(right, n)
   719  				} else {
   720  					mid = strings.Repeat(mid, n)
   721  				}
   722  				input := pre + mid + post
   723  
   724  				fset := token.NewFileSet()
   725  				_, err := ParseFile(fset, "", input, DeclarationErrors)
   726  				if size == "small" {
   727  					if err != nil {
   728  						t.Errorf("ParseFile(...): %v (want success)", err)
   729  					}
   730  				} else {
   731  					expected := "exceeded max scope depth during object resolution"
   732  					if err == nil || !strings.HasSuffix(err.Error(), expected) {
   733  						t.Errorf("ParseFile(...) = _, %v, want %q", err, expected)
   734  					}
   735  				}
   736  			})
   737  		}
   738  	}
   739  }
   740  
   741  // proposal go.dev/issue/50429
   742  func TestRangePos(t *testing.T) {
   743  	testcases := []string{
   744  		"package p; func _() { for range x {} }",
   745  		"package p; func _() { for i = range x {} }",
   746  		"package p; func _() { for i := range x {} }",
   747  		"package p; func _() { for k, v = range x {} }",
   748  		"package p; func _() { for k, v := range x {} }",
   749  	}
   750  
   751  	for _, src := range testcases {
   752  		fset := token.NewFileSet()
   753  		f, err := ParseFile(fset, src, src, 0)
   754  		if err != nil {
   755  			t.Fatal(err)
   756  		}
   757  
   758  		ast.Inspect(f, func(x ast.Node) bool {
   759  			switch s := x.(type) {
   760  			case *ast.RangeStmt:
   761  				pos := fset.Position(s.Range)
   762  				if pos.Offset != strings.Index(src, "range") {
   763  					t.Errorf("%s: got offset %v, want %v", src, pos.Offset, strings.Index(src, "range"))
   764  				}
   765  			}
   766  			return true
   767  		})
   768  	}
   769  }
   770  
   771  // TestIssue59180 tests that line number overflow doesn't cause an infinite loop.
   772  func TestIssue59180(t *testing.T) {
   773  	testcases := []string{
   774  		"package p\n//line :9223372036854775806\n\n//",
   775  		"package p\n//line :1:9223372036854775806\n\n//",
   776  		"package p\n//line file:9223372036854775806\n\n//",
   777  	}
   778  
   779  	for _, src := range testcases {
   780  		_, err := ParseFile(token.NewFileSet(), "", src, ParseComments)
   781  		if err == nil {
   782  			t.Errorf("ParseFile(%s) succeeded unexpectedly", src)
   783  		}
   784  	}
   785  }
   786  
   787  func TestGoVersion(t *testing.T) {
   788  	fset := token.NewFileSet()
   789  	pkgs, err := ParseDir(fset, "./testdata/goversion", nil, 0)
   790  	if err != nil {
   791  		t.Fatal(err)
   792  	}
   793  
   794  	for _, p := range pkgs {
   795  		want := strings.ReplaceAll(p.Name, "_", ".")
   796  		if want == "none" {
   797  			want = ""
   798  		}
   799  		for _, f := range p.Files {
   800  			if f.GoVersion != want {
   801  				t.Errorf("%s: GoVersion = %q, want %q", fset.Position(f.Pos()), f.GoVersion, want)
   802  			}
   803  		}
   804  	}
   805  }
   806  
   807  func TestIssue57490(t *testing.T) {
   808  	src := `package p; func f() { var x struct` // program not correctly terminated
   809  	fset := token.NewFileSet()
   810  	file, err := ParseFile(fset, "", src, 0)
   811  	if err == nil {
   812  		t.Fatalf("syntax error expected, but no error reported")
   813  	}
   814  
   815  	// Because of the syntax error, the end position of the function declaration
   816  	// is past the end of the file's position range.
   817  	funcEnd := file.Decls[0].End()
   818  
   819  	// Offset(funcEnd) must not panic (to test panic, set debug=true in token package)
   820  	// (panic: offset 35 out of bounds [0, 34] (position 36 out of bounds [1, 35]))
   821  	tokFile := fset.File(file.Pos())
   822  	offset := tokFile.Offset(funcEnd)
   823  	if offset != tokFile.Size() {
   824  		t.Fatalf("offset = %d, want %d", offset, tokFile.Size())
   825  	}
   826  }
   827  
   828  func TestParseTypeParamsAsParenExpr(t *testing.T) {
   829  	const src = "package p; type X[A (B),] struct{}"
   830  
   831  	fset := token.NewFileSet()
   832  	f, err := ParseFile(fset, "test.go", src, ParseComments|SkipObjectResolution)
   833  	if err != nil {
   834  		t.Fatal(err)
   835  	}
   836  
   837  	typeParam := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.TypeSpec).TypeParams.List[0].Type
   838  	_, ok := typeParam.(*ast.ParenExpr)
   839  	if !ok {
   840  		t.Fatalf("typeParam is a %T; want: *ast.ParenExpr", typeParam)
   841  	}
   842  }
   843  
   844  // TestEmptyFileHasValidStartEnd is a regression test for #70162.
   845  func TestEmptyFileHasValidStartEnd(t *testing.T) {
   846  	for _, test := range []struct {
   847  		src  string
   848  		want string // "Pos() FileStart FileEnd"
   849  	}{
   850  		{src: "", want: "0 1 1"},
   851  		{src: "package ", want: "0 1 9"},
   852  		{src: "package p", want: "1 1 10"},
   853  		{src: "type T int", want: "0 1 11"},
   854  	} {
   855  		fset := token.NewFileSet()
   856  		f, _ := ParseFile(fset, "a.go", test.src, 0)
   857  		got := fmt.Sprintf("%d %d %d", f.Pos(), f.FileStart, f.FileEnd)
   858  		if got != test.want {
   859  			t.Fatalf("src = %q: got %s, want %s", test.src, got, test.want)
   860  		}
   861  	}
   862  }
   863  
   864  func TestCommentGroupWithLineDirective(t *testing.T) {
   865  	const src = `package main
   866  func test() {
   867  //line a:15:1
   868  	//
   869  }
   870  `
   871  	fset := token.NewFileSet()
   872  	f, err := ParseFile(fset, "test.go", src, ParseComments|SkipObjectResolution)
   873  	if err != nil {
   874  		t.Fatal(err)
   875  	}
   876  
   877  	wantCommentGroups := []*ast.CommentGroup{
   878  		{
   879  			List: []*ast.Comment{
   880  				{
   881  					Slash: token.Pos(28),
   882  					Text:  "//line a:15:1",
   883  				},
   884  				{
   885  					Slash: token.Pos(43),
   886  					Text:  "//",
   887  				},
   888  			},
   889  		},
   890  	}
   891  
   892  	if !reflect.DeepEqual(f.Comments, wantCommentGroups) {
   893  		var got, want strings.Builder
   894  		ast.Fprint(&got, fset, f.Comments, nil)
   895  		ast.Fprint(&want, fset, wantCommentGroups, nil)
   896  		t.Fatalf("unexpected f.Comments got:\n%v\nwant:\n%v", got.String(), want.String())
   897  	}
   898  }
   899  

View as plain text