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