...

Source file src/go/parser/parser_test.go

Documentation: go/parser

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

View as plain text