github.com/yunabe/lgo@v0.0.0-20190709125917-42c42d410fdf/parser/parser_test.go (about) 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 "os" 13 "strings" 14 "testing" 15 ) 16 17 var validFiles = []string{ 18 "parser.go", 19 "parser_test.go", 20 "error_test.go", 21 "short_test.go", 22 } 23 24 func TestParse(t *testing.T) { 25 for _, filename := range validFiles { 26 _, err := ParseFile(token.NewFileSet(), filename, nil, DeclarationErrors) 27 if err != nil { 28 t.Fatalf("ParseFile(%s): %v", filename, err) 29 } 30 } 31 } 32 33 func nameFilter(filename string) bool { 34 switch filename { 35 case "parser.go", "interface.go", "parser_test.go": 36 return true 37 case "parser.go.orig": 38 return true // permit but should be ignored by ParseDir 39 } 40 return false 41 } 42 43 func dirFilter(f os.FileInfo) bool { return nameFilter(f.Name()) } 44 45 func TestParseDir(t *testing.T) { 46 path := "." 47 pkgs, err := ParseDir(token.NewFileSet(), path, dirFilter, 0) 48 if err != nil { 49 t.Fatalf("ParseDir(%s): %v", path, err) 50 } 51 if n := len(pkgs); n != 1 { 52 t.Errorf("got %d packages; want 1", n) 53 } 54 pkg := pkgs["parser"] 55 if pkg == nil { 56 t.Errorf(`package "parser" not found`) 57 return 58 } 59 if n := len(pkg.Files); n != 3 { 60 t.Errorf("got %d package files; want 3", n) 61 } 62 for filename := range pkg.Files { 63 if !nameFilter(filename) { 64 t.Errorf("unexpected package file: %s", filename) 65 } 66 } 67 } 68 69 func TestParseExpr(t *testing.T) { 70 // just kicking the tires: 71 // a valid arithmetic expression 72 src := "a + b" 73 x, err := ParseExpr(src) 74 if err != nil { 75 t.Errorf("ParseExpr(%q): %v", src, err) 76 } 77 // sanity check 78 if _, ok := x.(*ast.BinaryExpr); !ok { 79 t.Errorf("ParseExpr(%q): got %T, want *ast.BinaryExpr", src, x) 80 } 81 82 // a valid type expression 83 src = "struct{x *int}" 84 x, err = ParseExpr(src) 85 if err != nil { 86 t.Errorf("ParseExpr(%q): %v", src, err) 87 } 88 // sanity check 89 if _, ok := x.(*ast.StructType); !ok { 90 t.Errorf("ParseExpr(%q): got %T, want *ast.StructType", src, x) 91 } 92 93 // an invalid expression 94 src = "a + *" 95 if _, err := ParseExpr(src); err == nil { 96 t.Errorf("ParseExpr(%q): got no error", src) 97 } 98 99 // a valid expression followed by extra tokens is invalid 100 src = "a[i] := x" 101 if _, err := ParseExpr(src); err == nil { 102 t.Errorf("ParseExpr(%q): got no error", src) 103 } 104 105 // a semicolon is not permitted unless automatically inserted 106 src = "a + b\n" 107 if _, err := ParseExpr(src); err != nil { 108 t.Errorf("ParseExpr(%q): got error %s", src, err) 109 } 110 src = "a + b;" 111 if _, err := ParseExpr(src); err == nil { 112 t.Errorf("ParseExpr(%q): got no error", src) 113 } 114 115 // various other stuff following a valid expression 116 const validExpr = "a + b" 117 const anything = "dh3*#D)#_" 118 for _, c := range "!)]};," { 119 src := validExpr + string(c) + anything 120 if _, err := ParseExpr(src); err == nil { 121 t.Errorf("ParseExpr(%q): got no error", src) 122 } 123 } 124 125 // ParseExpr must not crash 126 for _, src := range valids { 127 ParseExpr(src) 128 } 129 } 130 131 func TestColonEqualsScope(t *testing.T) { 132 f, err := ParseFile(token.NewFileSet(), "", `package p; func f() { x, y, z := x, y, z }`, 0) 133 if err != nil { 134 t.Fatal(err) 135 } 136 137 // RHS refers to undefined globals; LHS does not. 138 as := f.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.AssignStmt) 139 for _, v := range as.Rhs { 140 id := v.(*ast.Ident) 141 if id.Obj != nil { 142 t.Errorf("rhs %s has Obj, should not", id.Name) 143 } 144 } 145 for _, v := range as.Lhs { 146 id := v.(*ast.Ident) 147 if id.Obj == nil { 148 t.Errorf("lhs %s does not have Obj, should", id.Name) 149 } 150 } 151 } 152 153 func TestVarScope(t *testing.T) { 154 f, err := ParseFile(token.NewFileSet(), "", `package p; func f() { var x, y, z = x, y, z }`, 0) 155 if err != nil { 156 t.Fatal(err) 157 } 158 159 // RHS refers to undefined globals; LHS does not. 160 as := f.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.DeclStmt).Decl.(*ast.GenDecl).Specs[0].(*ast.ValueSpec) 161 for _, v := range as.Values { 162 id := v.(*ast.Ident) 163 if id.Obj != nil { 164 t.Errorf("rhs %s has Obj, should not", id.Name) 165 } 166 } 167 for _, id := range as.Names { 168 if id.Obj == nil { 169 t.Errorf("lhs %s does not have Obj, should", id.Name) 170 } 171 } 172 } 173 174 func TestObjects(t *testing.T) { 175 const src = ` 176 package p 177 import fmt "fmt" 178 const pi = 3.14 179 type T struct{} 180 var x int 181 func f() { L: } 182 ` 183 184 f, err := ParseFile(token.NewFileSet(), "", src, 0) 185 if err != nil { 186 t.Fatal(err) 187 } 188 189 objects := map[string]ast.ObjKind{ 190 "p": ast.Bad, // not in a scope 191 "fmt": ast.Bad, // not resolved yet 192 "pi": ast.Con, 193 "T": ast.Typ, 194 "x": ast.Var, 195 "int": ast.Bad, // not resolved yet 196 "f": ast.Fun, 197 "L": ast.Lbl, 198 } 199 200 ast.Inspect(f, func(n ast.Node) bool { 201 if ident, ok := n.(*ast.Ident); ok { 202 obj := ident.Obj 203 if obj == nil { 204 if objects[ident.Name] != ast.Bad { 205 t.Errorf("no object for %s", ident.Name) 206 } 207 return true 208 } 209 if obj.Name != ident.Name { 210 t.Errorf("names don't match: obj.Name = %s, ident.Name = %s", obj.Name, ident.Name) 211 } 212 kind := objects[ident.Name] 213 if obj.Kind != kind { 214 t.Errorf("%s: obj.Kind = %s; want %s", ident.Name, obj.Kind, kind) 215 } 216 } 217 return true 218 }) 219 } 220 221 func TestUnresolved(t *testing.T) { 222 f, err := ParseFile(token.NewFileSet(), "", ` 223 package p 224 // 225 func f1a(int) 226 func f2a(byte, int, float) 227 func f3a(a, b int, c float) 228 func f4a(...complex) 229 func f5a(a s1a, b ...complex) 230 // 231 func f1b(*int) 232 func f2b([]byte, (int), *float) 233 func f3b(a, b *int, c []float) 234 func f4b(...*complex) 235 func f5b(a s1a, b ...[]complex) 236 // 237 type s1a struct { int } 238 type s2a struct { byte; int; s1a } 239 type s3a struct { a, b int; c float } 240 // 241 type s1b struct { *int } 242 type s2b struct { byte; int; *float } 243 type s3b struct { a, b *s3b; c []float } 244 `, 0) 245 if err != nil { 246 t.Fatal(err) 247 } 248 249 want := "int " + // f1a 250 "byte int float " + // f2a 251 "int float " + // f3a 252 "complex " + // f4a 253 "complex " + // f5a 254 // 255 "int " + // f1b 256 "byte int float " + // f2b 257 "int float " + // f3b 258 "complex " + // f4b 259 "complex " + // f5b 260 // 261 "int " + // s1a 262 "byte int " + // s2a 263 "int float " + // s3a 264 // 265 "int " + // s1a 266 "byte int float " + // s2a 267 "float " // s3a 268 269 // collect unresolved identifiers 270 var buf bytes.Buffer 271 for _, u := range f.Unresolved { 272 buf.WriteString(u.Name) 273 buf.WriteByte(' ') 274 } 275 got := buf.String() 276 277 if got != want { 278 t.Errorf("\ngot: %s\nwant: %s", got, want) 279 } 280 } 281 282 var imports = map[string]bool{ 283 `"a"`: true, 284 "`a`": true, 285 `"a/b"`: true, 286 `"a.b"`: true, 287 `"m\x61th"`: true, 288 `"greek/αβ"`: true, 289 `""`: false, 290 291 // Each of these pairs tests both `` vs "" strings 292 // and also use of invalid characters spelled out as 293 // escape sequences and written directly. 294 // For example `"\x00"` tests import "\x00" 295 // while "`\x00`" tests import `<actual-NUL-byte>`. 296 `"\x00"`: false, 297 "`\x00`": false, 298 `"\x7f"`: false, 299 "`\x7f`": false, 300 `"a!"`: false, 301 "`a!`": false, 302 `"a b"`: false, 303 "`a b`": false, 304 `"a\\b"`: false, 305 "`a\\b`": false, 306 "\"`a`\"": false, 307 "`\"a\"`": false, 308 `"\x80\x80"`: false, 309 "`\x80\x80`": false, 310 `"\xFFFD"`: false, 311 "`\xFFFD`": false, 312 } 313 314 func TestImports(t *testing.T) { 315 for path, isValid := range imports { 316 src := fmt.Sprintf("package p; import %s", path) 317 _, err := ParseFile(token.NewFileSet(), "", src, 0) 318 switch { 319 case err != nil && isValid: 320 t.Errorf("ParseFile(%s): got %v; expected no error", src, err) 321 case err == nil && !isValid: 322 t.Errorf("ParseFile(%s): got no error; expected one", src) 323 } 324 } 325 } 326 327 func TestCommentGroups(t *testing.T) { 328 f, err := ParseFile(token.NewFileSet(), "", ` 329 package p /* 1a */ /* 1b */ /* 1c */ // 1d 330 /* 2a 331 */ 332 // 2b 333 const pi = 3.1415 334 /* 3a */ // 3b 335 /* 3c */ const e = 2.7182 336 337 // Example from issue 3139 338 func ExampleCount() { 339 fmt.Println(strings.Count("cheese", "e")) 340 fmt.Println(strings.Count("five", "")) // before & after each rune 341 // Output: 342 // 3 343 // 5 344 } 345 `, ParseComments) 346 if err != nil { 347 t.Fatal(err) 348 } 349 expected := [][]string{ 350 {"/* 1a */", "/* 1b */", "/* 1c */", "// 1d"}, 351 {"/* 2a\n*/", "// 2b"}, 352 {"/* 3a */", "// 3b", "/* 3c */"}, 353 {"// Example from issue 3139"}, 354 {"// before & after each rune"}, 355 {"// Output:", "// 3", "// 5"}, 356 } 357 if len(f.Comments) != len(expected) { 358 t.Fatalf("got %d comment groups; expected %d", len(f.Comments), len(expected)) 359 } 360 for i, exp := range expected { 361 got := f.Comments[i].List 362 if len(got) != len(exp) { 363 t.Errorf("got %d comments in group %d; expected %d", len(got), i, len(exp)) 364 continue 365 } 366 for j, exp := range exp { 367 got := got[j].Text 368 if got != exp { 369 t.Errorf("got %q in group %d; expected %q", got, i, exp) 370 } 371 } 372 } 373 } 374 375 func getField(file *ast.File, fieldname string) *ast.Field { 376 parts := strings.Split(fieldname, ".") 377 for _, d := range file.Decls { 378 if d, ok := d.(*ast.GenDecl); ok && d.Tok == token.TYPE { 379 for _, s := range d.Specs { 380 if s, ok := s.(*ast.TypeSpec); ok && s.Name.Name == parts[0] { 381 if s, ok := s.Type.(*ast.StructType); ok { 382 for _, f := range s.Fields.List { 383 for _, name := range f.Names { 384 if name.Name == parts[1] { 385 return f 386 } 387 } 388 } 389 } 390 } 391 } 392 } 393 } 394 return nil 395 } 396 397 // Don't use ast.CommentGroup.Text() - we want to see exact comment text. 398 func commentText(c *ast.CommentGroup) string { 399 var buf bytes.Buffer 400 if c != nil { 401 for _, c := range c.List { 402 buf.WriteString(c.Text) 403 } 404 } 405 return buf.String() 406 } 407 408 func checkFieldComments(t *testing.T, file *ast.File, fieldname, lead, line string) { 409 f := getField(file, fieldname) 410 if f == nil { 411 t.Fatalf("field not found: %s", fieldname) 412 } 413 if got := commentText(f.Doc); got != lead { 414 t.Errorf("got lead comment %q; expected %q", got, lead) 415 } 416 if got := commentText(f.Comment); got != line { 417 t.Errorf("got line comment %q; expected %q", got, line) 418 } 419 } 420 421 func TestLeadAndLineComments(t *testing.T) { 422 f, err := ParseFile(token.NewFileSet(), "", ` 423 package p 424 type T struct { 425 /* F1 lead comment */ 426 // 427 F1 int /* F1 */ // line comment 428 // F2 lead 429 // comment 430 F2 int // F2 line comment 431 // f3 lead comment 432 f3 int // f3 line comment 433 } 434 `, ParseComments) 435 if err != nil { 436 t.Fatal(err) 437 } 438 checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment") 439 checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment") 440 checkFieldComments(t, f, "T.f3", "// f3 lead comment", "// f3 line comment") 441 ast.FileExports(f) 442 checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment") 443 checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment") 444 if getField(f, "T.f3") != nil { 445 t.Error("not expected to find T.f3") 446 } 447 } 448 449 // TestIssue9979 verifies that empty statements are contained within their enclosing blocks. 450 func TestIssue9979(t *testing.T) { 451 for _, src := range []string{ 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 "package p; func f() { ; }", 458 "package p; func f() { L: }", 459 "package p; func f() { L: ; }", 460 "package p; func f() { L: \n}", 461 "package p; func f() { L: \n; }", 462 } { 463 fset := token.NewFileSet() 464 f, err := ParseFile(fset, "", src, 0) 465 if err != nil { 466 t.Fatal(err) 467 } 468 469 var pos, end token.Pos 470 ast.Inspect(f, func(x ast.Node) bool { 471 switch s := x.(type) { 472 case *ast.BlockStmt: 473 pos, end = s.Pos()+1, s.End()-1 // exclude "{", "}" 474 case *ast.LabeledStmt: 475 pos, end = s.Pos()+2, s.End() // exclude "L:" 476 case *ast.EmptyStmt: 477 // check containment 478 if s.Pos() < pos || s.End() > end { 479 t.Errorf("%s: %T[%d, %d] not inside [%d, %d]", src, s, s.Pos(), s.End(), pos, end) 480 } 481 // check semicolon 482 offs := fset.Position(s.Pos()).Offset 483 if ch := src[offs]; ch != ';' != s.Implicit { 484 want := "want ';'" 485 if s.Implicit { 486 want = "but ';' is implicit" 487 } 488 t.Errorf("%s: found %q at offset %d; %s", src, ch, offs, want) 489 } 490 } 491 return true 492 }) 493 } 494 } 495 496 // TestIncompleteSelection ensures that an incomplete selector 497 // expression is parsed as a (blank) *ast.SelectorExpr, not a 498 // *ast.BadExpr. 499 func TestIncompleteSelection(t *testing.T) { 500 for _, src := range []string{ 501 "package p; var _ = fmt.", // at EOF 502 "package p; var _ = fmt.\ntype X int", // not at EOF 503 } { 504 fset := token.NewFileSet() 505 f, err := ParseFile(fset, "", src, 0) 506 if err == nil { 507 t.Errorf("ParseFile(%s) succeeded unexpectedly", src) 508 continue 509 } 510 511 const wantErr = "expected selector or type assertion" 512 if !strings.Contains(err.Error(), wantErr) { 513 t.Errorf("ParseFile returned wrong error %q, want %q", err, wantErr) 514 } 515 516 var sel *ast.SelectorExpr 517 ast.Inspect(f, func(n ast.Node) bool { 518 if n, ok := n.(*ast.SelectorExpr); ok { 519 sel = n 520 } 521 return true 522 }) 523 if sel == nil { 524 t.Error("found no *ast.SelectorExpr") 525 continue 526 } 527 const wantSel = "&{fmt _}" 528 if fmt.Sprint(sel) != wantSel { 529 t.Errorf("found selector %s, want %s", sel, wantSel) 530 continue 531 } 532 } 533 } 534 535 func TestLastLineComment(t *testing.T) { 536 const src = `package main 537 type x int // comment 538 ` 539 fset := token.NewFileSet() 540 f, err := ParseFile(fset, "", src, ParseComments) 541 if err != nil { 542 t.Fatal(err) 543 } 544 comment := f.Decls[0].(*ast.GenDecl).Specs[0].(*ast.TypeSpec).Comment.List[0].Text 545 if comment != "// comment" { 546 t.Errorf("got %q, want %q", comment, "// comment") 547 } 548 }