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{«»}»", parseMultiplier: 2},             // Parser nodes: UnaryExpr, CompositeLit
   602  	{name: "arraylit", format: "package main; var x = «[1]any{«nil»}»", parseMultiplier: 2},         // Parser nodes: UnaryExpr, CompositeLit
   603  	{name: "structlit", format: "package main; var x = «struct{x any}{«nil»}»", parseMultiplier: 2}, // Parser nodes: UnaryExpr, CompositeLit
   604  	{name: "maplit", format: "package main; var x = «map[int]any{1:«nil»}»", parseMultiplier: 2},    // Parser nodes: CompositeLit, KeyValueExpr
   605  	{name: "dot", format: "package main; var x = «x.»x"},
   606  	{name: "index", format: "package main; var x = x«[1]»"},
   607  	{name: "slice", format: "package main; var x = x«[1:2]»"},
   608  	{name: "slice3", format: "package main; var x = x«[1:2:3]»"},
   609  	{name: "dottype", format: "package main; var x = x«.(any)»"},
   610  	{name: "callseq", format: "package main; var x = x«()»"},
   611  	{name: "methseq", format: "package main; var x = x«.m()»", parseMultiplier: 2}, // Parser nodes: SelectorExpr, CallExpr
   612  	{name: "binary", format: "package main; var x = «1+»1"},
   613  	{name: "binaryparen", format: "package main; var x = «1+(«1»)»", parseMultiplier: 2}, // Parser nodes: BinaryExpr, ParenExpr
   614  	{name: "unary", format: "package main; var x = «^»1"},
   615  	{name: "addr", format: "package main; var x = «& »x"},
   616  	{name: "star", format: "package main; var x = «*»x"},
   617  	{name: "recv", format: "package main; var x = «<-»x"},
   618  	{name: "call", format: "package main; var x = «f(«1»)»", parseMultiplier: 2},    // Parser nodes: Ident, CallExpr
   619  	{name: "conv", format: "package main; var x = «(*T)(«1»)»", parseMultiplier: 2}, // Parser nodes: ParenExpr, CallExpr
   620  	{name: "label", format: "package main; func main() { «Label:» }"},
   621  	{name: "if", format: "package main; func main() { «if true { «» }»}", parseMultiplier: 2, scope: true, scopeMultiplier: 2}, // Parser nodes: IfStmt, BlockStmt. Scopes: IfStmt, BlockStmt
   622  	{name: "ifelse", format: "package main; func main() { «if true {} else » {} }", scope: true},
   623  	{name: "switch", format: "package main; func main() { «switch { default: «» }»}", scope: true, scopeMultiplier: 2},               // Scopes: TypeSwitchStmt, CaseClause
   624  	{name: "typeswitch", format: "package main; func main() { «switch x.(type) { default: «» }» }", scope: true, scopeMultiplier: 2}, // Scopes: TypeSwitchStmt, CaseClause
   625  	{name: "for0", format: "package main; func main() { «for { «» }» }", scope: true, scopeMultiplier: 2},                            // Scopes: ForStmt, BlockStmt
   626  	{name: "for1", format: "package main; func main() { «for x { «» }» }", scope: true, scopeMultiplier: 2},                          // Scopes: ForStmt, BlockStmt
   627  	{name: "for3", format: "package main; func main() { «for f(); g(); h() { «» }» }", scope: true, scopeMultiplier: 2},              // Scopes: ForStmt, BlockStmt
   628  	{name: "forrange0", format: "package main; func main() { «for range x { «» }» }", scope: true, scopeMultiplier: 2},               // Scopes: RangeStmt, BlockStmt
   629  	{name: "forrange1", format: "package main; func main() { «for x = range z { «» }» }", scope: true, scopeMultiplier: 2},           // Scopes: RangeStmt, BlockStmt
   630  	{name: "forrange2", format: "package main; func main() { «for x, y = range z { «» }» }", scope: true, scopeMultiplier: 2},        // Scopes: RangeStmt, BlockStmt
   631  	{name: "go", format: "package main; func main() { «go func() { «» }()» }", parseMultiplier: 2, scope: true},                      // Parser nodes: GoStmt, FuncLit
   632  	{name: "defer", format: "package main; func main() { «defer func() { «» }()» }", parseMultiplier: 2, scope: true},                // Parser nodes: DeferStmt, FuncLit
   633  	{name: "select", format: "package main; func main() { «select { default: «» }» }", scope: true},
   634  }
   635  
   636  // split splits pre«mid»post into pre, mid, post.
   637  // If the string does not have that form, split returns x, "", "".
   638  func split(x string) (pre, mid, post string) {
   639  	start, end := strings.Index(x, "«"), strings.LastIndex(x, "»")
   640  	if start < 0 || end < 0 {
   641  		return x, "", ""
   642  	}
   643  	return x[:start], x[start+len("«") : end], x[end+len("»"):]
   644  }
   645  
   646  func TestParseDepthLimit(t *testing.T) {
   647  	if testing.Short() {
   648  		t.Skip("test requires significant memory")
   649  	}
   650  	for _, tt := range parseDepthTests {
   651  		for _, size := range []string{"small", "big"} {
   652  			t.Run(tt.name+"/"+size, func(t *testing.T) {
   653  				n := maxNestLev + 1
   654  				if tt.parseMultiplier > 0 {
   655  					n /= tt.parseMultiplier
   656  				}
   657  				if size == "small" {
   658  					// Decrease the number of statements by 10, in order to check
   659  					// that we do not fail when under the limit. 10 is used to
   660  					// provide some wiggle room for cases where the surrounding
   661  					// scaffolding syntax adds some noise to the depth that changes
   662  					// on a per testcase basis.
   663  					n -= 10
   664  				}
   665  
   666  				pre, mid, post := split(tt.format)
   667  				if strings.Contains(mid, "«") {
   668  					left, base, right := split(mid)
   669  					mid = strings.Repeat(left, n) + base + strings.Repeat(right, n)
   670  				} else {
   671  					mid = strings.Repeat(mid, n)
   672  				}
   673  				input := pre + mid + post
   674  
   675  				fset := token.NewFileSet()
   676  				_, err := ParseFile(fset, "", input, ParseComments|SkipObjectResolution)
   677  				if size == "small" {
   678  					if err != nil {
   679  						t.Errorf("ParseFile(...): %v (want success)", err)
   680  					}
   681  				} else {
   682  					expected := "exceeded max nesting depth"
   683  					if err == nil || !strings.HasSuffix(err.Error(), expected) {
   684  						t.Errorf("ParseFile(...) = _, %v, want %q", err, expected)
   685  					}
   686  				}
   687  			})
   688  		}
   689  	}
   690  }
   691  
   692  func TestScopeDepthLimit(t *testing.T) {
   693  	for _, tt := range parseDepthTests {
   694  		if !tt.scope {
   695  			continue
   696  		}
   697  		for _, size := range []string{"small", "big"} {
   698  			t.Run(tt.name+"/"+size, func(t *testing.T) {
   699  				n := maxScopeDepth + 1
   700  				if tt.scopeMultiplier > 0 {
   701  					n /= tt.scopeMultiplier
   702  				}
   703  				if size == "small" {
   704  					// Decrease the number of statements by 10, in order to check
   705  					// that we do not fail when under the limit. 10 is used to
   706  					// provide some wiggle room for cases where the surrounding
   707  					// scaffolding syntax adds some noise to the depth that changes
   708  					// on a per testcase basis.
   709  					n -= 10
   710  				}
   711  
   712  				pre, mid, post := split(tt.format)
   713  				if strings.Contains(mid, "«") {
   714  					left, base, right := split(mid)
   715  					mid = strings.Repeat(left, n) + base + strings.Repeat(right, n)
   716  				} else {
   717  					mid = strings.Repeat(mid, n)
   718  				}
   719  				input := pre + mid + post
   720  
   721  				fset := token.NewFileSet()
   722  				_, err := ParseFile(fset, "", input, DeclarationErrors)
   723  				if size == "small" {
   724  					if err != nil {
   725  						t.Errorf("ParseFile(...): %v (want success)", err)
   726  					}
   727  				} else {
   728  					expected := "exceeded max scope depth during object resolution"
   729  					if err == nil || !strings.HasSuffix(err.Error(), expected) {
   730  						t.Errorf("ParseFile(...) = _, %v, want %q", err, expected)
   731  					}
   732  				}
   733  			})
   734  		}
   735  	}
   736  }
   737  
   738  // proposal go.dev/issue/50429
   739  func TestRangePos(t *testing.T) {
   740  	testcases := []string{
   741  		"package p; func _() { for range x {} }",
   742  		"package p; func _() { for i = range x {} }",
   743  		"package p; func _() { for i := range x {} }",
   744  		"package p; func _() { for k, v = range x {} }",
   745  		"package p; func _() { for k, v := range x {} }",
   746  	}
   747  
   748  	for _, src := range testcases {
   749  		fset := token.NewFileSet()
   750  		f, err := ParseFile(fset, src, src, 0)
   751  		if err != nil {
   752  			t.Fatal(err)
   753  		}
   754  
   755  		ast.Inspect(f, func(x ast.Node) bool {
   756  			switch s := x.(type) {
   757  			case *ast.RangeStmt:
   758  				pos := fset.Position(s.Range)
   759  				if pos.Offset != strings.Index(src, "range") {
   760  					t.Errorf("%s: got offset %v, want %v", src, pos.Offset, strings.Index(src, "range"))
   761  				}
   762  			}
   763  			return true
   764  		})
   765  	}
   766  }
   767  
   768  // TestIssue59180 tests that line number overflow doesn't cause an infinite loop.
   769  func TestIssue59180(t *testing.T) {
   770  	testcases := []string{
   771  		"package p\n//line :9223372036854775806\n\n//",
   772  		"package p\n//line :1:9223372036854775806\n\n//",
   773  		"package p\n//line file:9223372036854775806\n\n//",
   774  	}
   775  
   776  	for _, src := range testcases {
   777  		_, err := ParseFile(token.NewFileSet(), "", src, ParseComments)
   778  		if err == nil {
   779  			t.Errorf("ParseFile(%s) succeeded unexpectedly", src)
   780  		}
   781  	}
   782  }
   783  
   784  func TestGoVersion(t *testing.T) {
   785  	fset := token.NewFileSet()
   786  	pkgs, err := ParseDir(fset, "./testdata/goversion", nil, 0)
   787  	if err != nil {
   788  		t.Fatal(err)
   789  	}
   790  
   791  	for _, p := range pkgs {
   792  		want := strings.ReplaceAll(p.Name, "_", ".")
   793  		if want == "none" {
   794  			want = ""
   795  		}
   796  		for _, f := range p.Files {
   797  			if f.GoVersion != want {
   798  				t.Errorf("%s: GoVersion = %q, want %q", fset.Position(f.Pos()), f.GoVersion, want)
   799  			}
   800  		}
   801  	}
   802  }
   803  

View as plain text