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