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  	"strings"
    13  	"testing"
    14  )
    15  
    16  var validFiles = []string{
    17  	"parser.go",
    18  	"parser_test.go",
    19  	"error_test.go",
    20  	"short_test.go",
    21  }
    22  
    23  func 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  
    32  func 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  
    42  func dirFilter(f fs.FileInfo) bool { return nameFilter(f.Name()) }
    43  
    44  func 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  
    52  func 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  
    60  func 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  
    84  func 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  
    92  func 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  
   161  func 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  
   183  func 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  
   204  func TestObjects(t *testing.T) {
   205  	const src = `
   206  package p
   207  import fmt "fmt"
   208  const pi = 3.14
   209  type T struct{}
   210  var x int
   211  func 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  
   251  func TestUnresolved(t *testing.T) {
   252  	f, err := ParseFile(token.NewFileSet(), "", `
   253  package p
   254  //
   255  func f1a(int)
   256  func f2a(byte, int, float)
   257  func f3a(a, b int, c float)
   258  func f4a(...complex)
   259  func f5a(a s1a, b ...complex)
   260  //
   261  func f1b(*int)
   262  func f2b([]byte, (int), *float)
   263  func f3b(a, b *int, c []float)
   264  func f4b(...*complex)
   265  func f5b(a s1a, b ...[]complex)
   266  //
   267  type s1a struct { int }
   268  type s2a struct { byte; int; s1a }
   269  type s3a struct { a, b int; c float }
   270  //
   271  type s1b struct { *int }
   272  type s2b struct { byte; int; *float }
   273  type 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  
   312  func TestCommentGroups(t *testing.T) {
   313  	f, err := ParseFile(token.NewFileSet(), "", `
   314  package p /* 1a */ /* 1b */      /* 1c */ // 1d
   315  /* 2a
   316  */
   317  // 2b
   318  const pi = 3.1415
   319  /* 3a */ // 3b
   320  /* 3c */ const e = 2.7182
   321  
   322  // Example from go.dev/issue/3139
   323  func 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  
   360  func 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.
   383  func 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  
   393  func 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  
   406  func TestLeadAndLineComments(t *testing.T) {
   407  	f, err := ParseFile(token.NewFileSet(), "", `
   408  package p
   409  type 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.
   445  func 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  
   491  func TestFileStartEndPos(t *testing.T) {
   492  	const src = `// Copyright
   493  
   494  //+build tag
   495  
   496  // Package p doc comment.
   497  package p
   498  
   499  var 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.
   522  func 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  
   558  func TestLastLineComment(t *testing.T) {
   559  	const src = `package main
   560  type 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  
   573  var 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, "", "".
   639  func 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  
   647  func 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  
   693  func 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
   740  func 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.
   770  func 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  
   785  func 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  
   805  func 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  
   826  func TestParseTypeParamsAsParenExpr(t *testing.T) {
   827  	const src = "package p; type X[A (B),] struct{}"
   828  
   829  	fset := token.NewFileSet()
   830  	f, err := ParseFile(fset, "test.go", src, ParseComments|SkipObjectResolution)
   831  	if err != nil {
   832  		t.Fatal(err)
   833  	}
   834  
   835  	typeParam := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.TypeSpec).TypeParams.List[0].Type
   836  	_, ok := typeParam.(*ast.ParenExpr)
   837  	if !ok {
   838  		t.Fatalf("typeParam is a %T; want: *ast.ParenExpr", typeParam)
   839  	}
   840  }
   841  
   842  // TestEmptyFileHasValidStartEnd is a regression test for #70162.
   843  func TestEmptyFileHasValidStartEnd(t *testing.T) {
   844  	for _, test := range []struct {
   845  		src  string
   846  		want string // "Pos() FileStart FileEnd"
   847  	}{
   848  		{src: "", want: "0 1 1"},
   849  		{src: "package ", want: "0 1 9"},
   850  		{src: "package p", want: "1 1 10"},
   851  		{src: "type T int", want: "0 1 11"},
   852  	} {
   853  		fset := token.NewFileSet()
   854  		f, _ := ParseFile(fset, "a.go", test.src, 0)
   855  		got := fmt.Sprintf("%d %d %d", f.Pos(), f.FileStart, f.FileEnd)
   856  		if got != test.want {
   857  			t.Fatalf("src = %q: got %s, want %s", test.src, got, test.want)
   858  		}
   859  	}
   860  }
   861  

View as plain text