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