LEFT | RIGHT |
| 1 // +build api_tool |
| 2 |
1 // Copyright 2011 The Go Authors. All rights reserved. | 3 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 4 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 5 // license that can be found in the LICENSE file. |
4 | 6 |
5 // Binary api computes the exported API of a set of Go packages. | 7 // Binary api computes the exported API of a set of Go packages. |
6 package main | 8 package main |
7 | 9 |
8 import ( | 10 import ( |
9 "bufio" | 11 "bufio" |
10 "bytes" | 12 "bytes" |
11 "flag" | 13 "flag" |
12 "fmt" | 14 "fmt" |
13 "go/ast" | 15 "go/ast" |
14 "go/build" | 16 "go/build" |
15 "go/parser" | 17 "go/parser" |
16 "go/token" | 18 "go/token" |
17 "io" | 19 "io" |
18 "io/ioutil" | 20 "io/ioutil" |
19 "log" | 21 "log" |
20 "os" | 22 "os" |
21 "os/exec" | 23 "os/exec" |
22 "path/filepath" | 24 "path/filepath" |
23 "regexp" | 25 "regexp" |
24 "runtime" | 26 "runtime" |
25 "sort" | 27 "sort" |
26 "strconv" | |
27 "strings" | 28 "strings" |
28 | 29 |
29 "code.google.com/p/go.tools/go/types" | 30 "code.google.com/p/go.tools/go/types" |
30 ) | 31 ) |
31 | 32 |
32 // Flags | 33 // Flags |
33 var ( | 34 var ( |
34 checkFile = flag.String("c", "", "optional comma-separated filename(s)
to check API against") | 35 checkFile = flag.String("c", "", "optional comma-separated filename(s)
to check API against") |
35 allowNew = flag.Bool("allow_new", true, "allow API additions") | 36 allowNew = flag.Bool("allow_new", true, "allow API additions") |
36 exceptFile = flag.String("except", "", "optional filename of packages th
at are allowed to change without triggering a failure in the tool") | 37 exceptFile = flag.String("except", "", "optional filename of packages th
at are allowed to change without triggering a failure in the tool") |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
116 } | 117 } |
117 } | 118 } |
118 | 119 |
119 if *forceCtx != "" { | 120 if *forceCtx != "" { |
120 setContexts() | 121 setContexts() |
121 } | 122 } |
122 for _, c := range contexts { | 123 for _, c := range contexts { |
123 c.Compiler = build.Default.Compiler | 124 c.Compiler = build.Default.Compiler |
124 } | 125 } |
125 | 126 |
126 » var pkgs []string | 127 » var pkgNames []string |
127 if flag.NArg() > 0 { | 128 if flag.NArg() > 0 { |
128 » » pkgs = flag.Args() | 129 » » pkgNames = flag.Args() |
129 } else { | 130 } else { |
130 stds, err := exec.Command("go", "list", "std").Output() | 131 stds, err := exec.Command("go", "list", "std").Output() |
131 if err != nil { | 132 if err != nil { |
132 log.Fatal(err) | 133 log.Fatal(err) |
133 } | 134 } |
134 » » pkgs = strings.Fields(string(stds)) | 135 » » pkgNames = strings.Fields(string(stds)) |
135 } | 136 } |
136 | 137 |
137 var featureCtx = make(map[string]map[string]bool) // feature -> context
name -> true | 138 var featureCtx = make(map[string]map[string]bool) // feature -> context
name -> true |
138 for _, context := range contexts { | 139 for _, context := range contexts { |
139 » » w := NewWalker() | 140 » » w := NewWalker(context, filepath.Join(build.Default.GOROOT, "src
/pkg")) |
140 » » w.context = context | 141 |
141 | 142 » » for _, name := range pkgNames { |
142 » » for _, pkg := range pkgs { | 143 » » » // - Package "unsafe" contains special signatures requir
ing |
143 » » » w.wantedPkg[pkg] = true | 144 » » » // extra care when printing them - ignore since it is
not |
144 » » } | 145 » » » // going to change w/o a language change. |
145 | 146 » » » // - We don't care about the API of commands. |
146 » » for _, pkg := range pkgs { | 147 » » » if name != "unsafe" && !strings.HasPrefix(name, "cmd/")
{ |
147 » » » if strings.HasPrefix(pkg, "cmd/") { | 148 » » » » w.export(w.Import(name)) |
148 » » » » continue | |
149 } | 149 } |
150 » » » if fi, err := os.Stat(filepath.Join(w.root, pkg)); err !
= nil || !fi.IsDir() { | 150 » » } |
151 » » » » log.Fatalf("no source in tree for package %q", p
kg) | 151 |
152 » » » } | |
153 » » » //w.WalkPackage(pkg) | |
154 » » » w.Import(pkg) | |
155 » » } | |
156 ctxName := contextName(context) | 152 ctxName := contextName(context) |
157 for _, f := range w.Features() { | 153 for _, f := range w.Features() { |
158 if featureCtx[f] == nil { | 154 if featureCtx[f] == nil { |
159 featureCtx[f] = make(map[string]bool) | 155 featureCtx[f] = make(map[string]bool) |
160 } | 156 } |
161 featureCtx[f][ctxName] = true | 157 featureCtx[f][ctxName] = true |
162 } | 158 } |
163 } | 159 } |
164 | 160 |
165 var features []string | 161 var features []string |
(...skipping 27 matching lines...) Expand all Loading... |
193 return | 189 return |
194 } | 190 } |
195 | 191 |
196 var required []string | 192 var required []string |
197 for _, file := range strings.Split(*checkFile, ",") { | 193 for _, file := range strings.Split(*checkFile, ",") { |
198 required = append(required, fileFeatures(file)...) | 194 required = append(required, fileFeatures(file)...) |
199 } | 195 } |
200 optional := fileFeatures(*nextFile) | 196 optional := fileFeatures(*nextFile) |
201 exception := fileFeatures(*exceptFile) | 197 exception := fileFeatures(*exceptFile) |
202 fail = !compareAPI(bw, features, required, optional, exception) | 198 fail = !compareAPI(bw, features, required, optional, exception) |
| 199 } |
| 200 |
| 201 // export emits the exported package features. |
| 202 func (w *Walker) export(pkg *types.Package) { |
| 203 if *verbose { |
| 204 log.Println(pkg) |
| 205 } |
| 206 pop := w.pushScope("pkg " + pkg.Path()) |
| 207 w.current = pkg |
| 208 scope := pkg.Scope() |
| 209 for _, name := range scope.Names() { |
| 210 if ast.IsExported(name) { |
| 211 w.emitObj(scope.Lookup(name)) |
| 212 } |
| 213 } |
| 214 pop() |
203 } | 215 } |
204 | 216 |
205 func set(items []string) map[string]bool { | 217 func set(items []string) map[string]bool { |
206 s := make(map[string]bool) | 218 s := make(map[string]bool) |
207 for _, v := range items { | 219 for _, v := range items { |
208 s[v] = true | 220 s[v] = true |
209 } | 221 } |
210 return s | 222 return s |
211 } | 223 } |
212 | 224 |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
294 text := strings.TrimSpace(string(bs)) | 306 text := strings.TrimSpace(string(bs)) |
295 if text == "" { | 307 if text == "" { |
296 return nil | 308 return nil |
297 } | 309 } |
298 return strings.Split(text, "\n") | 310 return strings.Split(text, "\n") |
299 } | 311 } |
300 | 312 |
301 var fset = token.NewFileSet() | 313 var fset = token.NewFileSet() |
302 | 314 |
303 type Walker struct { | 315 type Walker struct { |
304 » context *build.Context | 316 » context *build.Context |
305 » root string | 317 » root string |
306 » scope []string | 318 » scope []string |
307 » features map[string]bool // set | 319 » current *types.Package |
308 » curPkg *types.Package | 320 » features map[string]bool // set |
309 » packageState map[string]loadState | 321 » imported map[string]*types.Package // packages already imported |
310 » importing map[string]bool | 322 } |
311 » importedPkg map[string]*types.Package // packages already imported | 323 |
312 » wantedPkg map[string]bool // packages requested on the comm
and line | 324 func NewWalker(context *build.Context, root string) *Walker { |
313 } | |
314 | |
315 func NewWalker() *Walker { | |
316 return &Walker{ | 325 return &Walker{ |
317 » » features: make(map[string]bool), | 326 » » context: context, |
318 » » packageState: make(map[string]loadState), | 327 » » root: root, |
319 » » importing: make(map[string]bool), | 328 » » features: map[string]bool{}, |
320 » » importedPkg: make(map[string]*types.Package), | 329 » » imported: map[string]*types.Package{"unsafe": types.Unsafe}, |
321 » » wantedPkg: make(map[string]bool), | 330 » } |
322 » » root: filepath.Join(build.Default.GOROOT, "src/pkg"), | 331 } |
323 » } | |
324 } | |
325 | |
326 // loadState is the state of a package's parsing. | |
327 type loadState int | |
328 | |
329 const ( | |
330 » notLoaded loadState = iota | |
331 » loading | |
332 » loaded | |
333 ) | |
334 | 332 |
335 func (w *Walker) Features() (fs []string) { | 333 func (w *Walker) Features() (fs []string) { |
336 for f := range w.features { | 334 for f := range w.features { |
337 fs = append(fs, f) | 335 fs = append(fs, f) |
338 } | 336 } |
339 sort.Strings(fs) | 337 sort.Strings(fs) |
340 return | |
341 } | |
342 | |
343 // fileDeps returns the imports in a file. | |
344 func fileDeps(f *ast.File) (pkgs []string) { | |
345 for _, is := range f.Imports { | |
346 fpkg, err := strconv.Unquote(is.Path.Value) | |
347 if err != nil { | |
348 log.Fatalf("error unquoting import string %q: %v", is.Pa
th.Value, err) | |
349 } | |
350 if fpkg != "C" { | |
351 pkgs = append(pkgs, fpkg) | |
352 } | |
353 } | |
354 return | 338 return |
355 } | 339 } |
356 | 340 |
357 var parsedFileCache = make(map[string]*ast.File) | 341 var parsedFileCache = make(map[string]*ast.File) |
358 | 342 |
359 func (w *Walker) parseFile(dir, file string) (*ast.File, error) { | 343 func (w *Walker) parseFile(dir, file string) (*ast.File, error) { |
360 filename := filepath.Join(dir, file) | 344 filename := filepath.Join(dir, file) |
361 f, _ := parsedFileCache[filename] | 345 f, _ := parsedFileCache[filename] |
362 if f != nil { | 346 if f != nil { |
363 return f, nil | 347 return f, nil |
364 } | 348 } |
365 | 349 |
366 var err error | 350 var err error |
367 | 351 |
368 » // TODO(gri) HACK: generate missing context-dependent files. | 352 » // generate missing context-dependent files. |
369 | 353 |
370 if w.context != nil && file == fmt.Sprintf("zgoos_%s.go", w.context.GOOS
) { | 354 if w.context != nil && file == fmt.Sprintf("zgoos_%s.go", w.context.GOOS
) { |
371 src := fmt.Sprintf("package runtime; const theGoos = `%s`", w.co
ntext.GOOS) | 355 src := fmt.Sprintf("package runtime; const theGoos = `%s`", w.co
ntext.GOOS) |
372 f, err = parser.ParseFile(fset, filename, src, 0) | 356 f, err = parser.ParseFile(fset, filename, src, 0) |
373 if err != nil { | 357 if err != nil { |
374 » » » panic(err) | 358 » » » log.Fatalf("incorrect generated file: %s", err) |
375 } | 359 } |
376 } | 360 } |
377 | 361 |
378 if w.context != nil && file == fmt.Sprintf("zgoarch_%s.go", w.context.GO
ARCH) { | 362 if w.context != nil && file == fmt.Sprintf("zgoarch_%s.go", w.context.GO
ARCH) { |
379 src := fmt.Sprintf("package runtime; const theGoarch = `%s`", w.
context.GOARCH) | 363 src := fmt.Sprintf("package runtime; const theGoarch = `%s`", w.
context.GOARCH) |
380 f, err = parser.ParseFile(fset, filename, src, 0) | 364 f, err = parser.ParseFile(fset, filename, src, 0) |
381 if err != nil { | 365 if err != nil { |
382 » » » panic(err) | 366 » » » log.Fatalf("incorrect generated file: %s", err) |
383 } | 367 } |
384 } | 368 } |
385 | 369 |
386 if f == nil { | 370 if f == nil { |
387 f, err = parser.ParseFile(fset, filename, nil, 0) | 371 f, err = parser.ParseFile(fset, filename, nil, 0) |
388 if err != nil { | 372 if err != nil { |
389 return nil, err | 373 return nil, err |
390 } | 374 } |
391 } | 375 } |
392 | 376 |
393 parsedFileCache[filename] = f | 377 parsedFileCache[filename] = f |
394 return f, nil | 378 return f, nil |
395 } | 379 } |
396 | 380 |
397 func contains(list []string, s string) bool { | 381 func contains(list []string, s string) bool { |
398 for _, t := range list { | 382 for _, t := range list { |
399 if t == s { | 383 if t == s { |
400 return true | 384 return true |
401 } | 385 } |
402 } | 386 } |
403 return false | 387 return false |
404 } | 388 } |
405 | 389 |
406 func print(list []string) { | 390 // Importing is a sentinel taking the place in Walker.imported |
407 » for _, s := range list { | 391 // for a package that is in the process of being imported. |
408 » » fmt.Println(s) | 392 var importing types.Package |
409 » } | |
410 } | |
411 | 393 |
412 func (w *Walker) Import(name string) (pkg *types.Package) { | 394 func (w *Walker) Import(name string) (pkg *types.Package) { |
413 » if name == "unsafe" { | 395 » pkg = w.imported[name] |
414 » » return types.Unsafe | |
415 » } | |
416 | |
417 » if name == "C" { | |
418 » » return types.NewPackage(token.NoPos, name, "C", types.NewScope(n
il), make(map[string]*types.Package), true) | |
419 » } | |
420 | |
421 » pkg = w.importedPkg[name] | |
422 if pkg != nil { | 396 if pkg != nil { |
423 » » return | 397 » » if pkg == &importing { |
424 » } | 398 » » » log.Fatalf("cycle importing package %q", name) |
425 | 399 » » } |
426 » if w.importing[name] { | 400 » » return pkg |
427 » » log.Fatalf("import cycle loading package %q?", name) | 401 » } |
428 » } | 402 » w.imported[name] = &importing |
429 » w.importing[name] = true | 403 |
430 | 404 » // Determine package files. |
431 » if *verbose { | |
432 » » fmt.Printf("importing %s\n", name) | |
433 » » if w.context != nil { | |
434 » » » fmt.Println(contextName(w.context)) | |
435 » » } | |
436 » } | |
437 | |
438 dir := filepath.Join(w.root, filepath.FromSlash(name)) | 405 dir := filepath.Join(w.root, filepath.FromSlash(name)) |
439 | 406 » if fi, err := os.Stat(dir); err != nil || !fi.IsDir() { |
440 » ctxt := w.context | 407 » » log.Fatalf("no source in tree for package %q", pkg) |
441 » if ctxt == nil { | 408 » } |
442 » » ctxt = &build.Default | 409 |
443 » } | 410 » context := w.context |
444 » info, err := ctxt.ImportDir(dir, 0) | 411 » if context == nil { |
| 412 » » context = &build.Default |
| 413 » } |
| 414 » info, err := context.ImportDir(dir, 0) |
445 if err != nil { | 415 if err != nil { |
446 » » /* | 416 » » if _, nogo := err.(*build.NoGoError); nogo { |
447 » » » if _, nogo := err.(*build.NoGoError); nogo { | 417 » » » return |
448 » » » » return | 418 » » } |
449 » » » } | |
450 » » */ | |
451 » » panic(err) | |
452 log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, dir, err) | 419 log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, dir, err) |
453 } | 420 } |
454 | |
455 filenames := append(append([]string{}, info.GoFiles...), info.CgoFiles..
.) | 421 filenames := append(append([]string{}, info.GoFiles...), info.CgoFiles..
.) |
456 | 422 |
457 // Certain files only exist when building for the specified context. | 423 // Certain files only exist when building for the specified context. |
458 // Add them manually. | 424 // Add them manually. |
459 if name == "runtime" { | 425 if name == "runtime" { |
460 » » file := fmt.Sprintf("zgoos_%s.go", w.context.GOOS) | 426 » » n := fmt.Sprintf("zgoos_%s.go", w.context.GOOS) |
461 » » if !contains(filenames, file) { | 427 » » if !contains(filenames, n) { |
462 » » » filenames = append(filenames, file) | 428 » » » filenames = append(filenames, n) |
463 » » } | 429 » » } |
464 | 430 |
465 » » file = fmt.Sprintf("zgoarch_%s.go", w.context.GOARCH) | 431 » » n = fmt.Sprintf("zgoarch_%s.go", w.context.GOARCH) |
466 » » if !contains(filenames, file) { | 432 » » if !contains(filenames, n) { |
467 » » » filenames = append(filenames, file) | 433 » » » filenames = append(filenames, n) |
468 » » } | 434 » » } |
469 » } | 435 » } |
470 | 436 |
| 437 » // Parse package files. |
471 var files []*ast.File | 438 var files []*ast.File |
472 for _, file := range filenames { | 439 for _, file := range filenames { |
473 f, err := w.parseFile(dir, file) | 440 f, err := w.parseFile(dir, file) |
474 if err != nil { | 441 if err != nil { |
475 log.Fatalf("error parsing package %s: %s", name, err) | 442 log.Fatalf("error parsing package %s: %s", name, err) |
476 } | 443 } |
477 files = append(files, f) | 444 files = append(files, f) |
478 } | 445 } |
479 | 446 |
| 447 // Type-check package files. |
480 conf := types.Config{ | 448 conf := types.Config{ |
481 IgnoreFuncBodies: true, | 449 IgnoreFuncBodies: true, |
| 450 FakeImportC: true, |
482 Import: func(imports map[string]*types.Package, name string) (*t
ypes.Package, error) { | 451 Import: func(imports map[string]*types.Package, name string) (*t
ypes.Package, error) { |
483 pkg := w.Import(name) | 452 pkg := w.Import(name) |
484 imports[name] = pkg | 453 imports[name] = pkg |
485 return pkg, nil | 454 return pkg, nil |
486 }, | 455 }, |
487 } | 456 } |
488 » pkg, err = conf.Check(dir, fset, files, nil) | 457 » pkg, err = conf.Check(name, fset, files, nil) |
489 if err != nil { | 458 if err != nil { |
490 ctxt := "<no context>" | 459 ctxt := "<no context>" |
491 if w.context != nil { | 460 if w.context != nil { |
492 ctxt = fmt.Sprintf("%s-%s", w.context.GOOS, w.context.GO
ARCH) | 461 ctxt = fmt.Sprintf("%s-%s", w.context.GOOS, w.context.GO
ARCH) |
493 } | 462 } |
494 log.Fatalf("error typechecking package %s: %s (%s)", name, err,
ctxt) | 463 log.Fatalf("error typechecking package %s: %s (%s)", name, err,
ctxt) |
495 } | 464 } |
496 | 465 |
497 » w.importedPkg[name] = pkg | 466 » w.imported[name] = pkg |
498 » w.importing[name] = false | |
499 | |
500 » // export data | |
501 » if *verbose { | |
502 » » log.Println("pkg", pkg) | |
503 » } | |
504 » defer w.pushScope("pkg " + name)() | |
505 | |
506 » w.curPkg = pkg | |
507 » scope := pkg.Scope() | |
508 » for _, name := range scope.Names() { | |
509 » » if ast.IsExported(name) { | |
510 » » » w.emitObj(scope.Lookup(name)) | |
511 » » } | |
512 » } | |
513 | |
514 return | 467 return |
515 } | |
516 | |
517 // WalkPackage walks all files in package `name'. | |
518 // WalkPackage does nothing if the package has already been loaded. | |
519 func (w *Walker) WalkPackage(name string) { | |
520 switch w.packageState[name] { | |
521 case loading: | |
522 log.Fatalf("import cycle loading package %q?", name) | |
523 case loaded: | |
524 return | |
525 } | |
526 w.packageState[name] = loading | |
527 defer func() { | |
528 w.packageState[name] = loaded | |
529 }() | |
530 dir := filepath.Join(w.root, filepath.FromSlash(name)) | |
531 | |
532 ctxt := w.context | |
533 if ctxt == nil { | |
534 ctxt = &build.Default | |
535 } | |
536 info, err := ctxt.ImportDir(dir, 0) | |
537 if err != nil { | |
538 if _, nogo := err.(*build.NoGoError); nogo { | |
539 return | |
540 } | |
541 log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, dir, err) | |
542 } | |
543 | |
544 filenames := append(append([]string{}, info.GoFiles...), info.CgoFiles..
.) | |
545 | |
546 // TODO(gri) HACK: Certain files only exist when building | |
547 // for a given context. Add them manually. | |
548 if name == "runtime" { | |
549 file := fmt.Sprintf("zgoos_%s.go", w.context.GOOS) | |
550 if !contains(filenames, file) { | |
551 filenames = append(filenames, file) | |
552 } | |
553 | |
554 file = fmt.Sprintf("zgoarch_%s.go", w.context.GOARCH) | |
555 if !contains(filenames, file) { | |
556 filenames = append(filenames, file) | |
557 } | |
558 } | |
559 | |
560 var files []*ast.File | |
561 for _, file := range filenames { | |
562 f, err := w.parseFile(dir, file) | |
563 if err != nil { | |
564 log.Fatalf("error parsing package %s: %s", name, err) | |
565 } | |
566 files = append(files, f) | |
567 | |
568 for _, dep := range fileDeps(f) { | |
569 w.WalkPackage(dep) | |
570 } | |
571 } | |
572 | |
573 conf := types.Config{ | |
574 IgnoreFuncBodies: true, | |
575 } | |
576 pkg, err := conf.Check(dir, fset, files, nil) | |
577 if err != nil { | |
578 if w.context != nil { | |
579 fmt.Printf("%s_%s: %s\n", w.context.GOOS, w.context.GOAR
CH, dir) | |
580 } | |
581 log.Fatalf("error typechecking package %s: %s", name, err) | |
582 } | |
583 | |
584 if *verbose { | |
585 log.Println("pkg", pkg) | |
586 } | |
587 defer w.pushScope("pkg " + name)() | |
588 | |
589 w.curPkg = pkg | |
590 scope := pkg.Scope() | |
591 for _, name := range scope.Names() { | |
592 if ast.IsExported(name) { | |
593 w.emitObj(scope.Lookup(name)) | |
594 } | |
595 } | |
596 } | 468 } |
597 | 469 |
598 // pushScope enters a new scope (walking a package, type, node, etc) | 470 // pushScope enters a new scope (walking a package, type, node, etc) |
599 // and returns a function that will leave the scope (with sanity checking | 471 // and returns a function that will leave the scope (with sanity checking |
600 // for mismatched pushes & pops) | 472 // for mismatched pushes & pops) |
601 func (w *Walker) pushScope(name string) (popFunc func()) { | 473 func (w *Walker) pushScope(name string) (popFunc func()) { |
602 w.scope = append(w.scope, name) | 474 w.scope = append(w.scope, name) |
603 return func() { | 475 return func() { |
604 if len(w.scope) == 0 { | 476 if len(w.scope) == 0 { |
605 log.Fatalf("attempt to leave scope %q with empty scope l
ist", name) | 477 log.Fatalf("attempt to leave scope %q with empty scope l
ist", name) |
(...skipping 13 matching lines...) Expand all Loading... |
619 } | 491 } |
620 sort.Strings(list) | 492 sort.Strings(list) |
621 return list | 493 return list |
622 } | 494 } |
623 | 495 |
624 func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) { | 496 func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) { |
625 switch typ := typ.(type) { | 497 switch typ := typ.(type) { |
626 case *types.Basic: | 498 case *types.Basic: |
627 s := typ.Name() | 499 s := typ.Name() |
628 switch typ.Kind() { | 500 switch typ.Kind() { |
| 501 case types.UnsafePointer: |
| 502 s = "unsafe.Pointer" |
629 case types.UntypedBool: | 503 case types.UntypedBool: |
630 s = "ideal-bool" | 504 s = "ideal-bool" |
631 case types.UntypedInt: | 505 case types.UntypedInt: |
632 s = "ideal-int" | 506 s = "ideal-int" |
633 case types.UntypedRune: | 507 case types.UntypedRune: |
634 » » » s = "ideal-rune" | 508 » » » // "ideal-char" for compatibility with old tool |
| 509 » » » // TODO(gri) change to "ideal-rune" |
| 510 » » » s = "ideal-char" |
635 case types.UntypedFloat: | 511 case types.UntypedFloat: |
636 s = "ideal-float" | 512 s = "ideal-float" |
637 case types.UntypedComplex: | 513 case types.UntypedComplex: |
638 s = "ideal-complex" | 514 s = "ideal-complex" |
639 case types.UntypedString: | 515 case types.UntypedString: |
640 s = "ideal-string" | 516 s = "ideal-string" |
641 case types.UntypedNil: | 517 case types.UntypedNil: |
642 panic("should never see untyped nil type") | 518 panic("should never see untyped nil type") |
643 default: | 519 default: |
644 switch s { | 520 switch s { |
(...skipping 17 matching lines...) Expand all Loading... |
662 buf.WriteString("struct") | 538 buf.WriteString("struct") |
663 | 539 |
664 case *types.Pointer: | 540 case *types.Pointer: |
665 buf.WriteByte('*') | 541 buf.WriteByte('*') |
666 w.writeType(buf, typ.Elem()) | 542 w.writeType(buf, typ.Elem()) |
667 | 543 |
668 case *types.Tuple: | 544 case *types.Tuple: |
669 panic("should never see a tuple type") | 545 panic("should never see a tuple type") |
670 | 546 |
671 case *types.Signature: | 547 case *types.Signature: |
672 » » w.writeParams(buf, typ.Params(), typ.IsVariadic()) | 548 » » buf.WriteString("func") |
673 » » switch res := typ.Results(); res.Len() { | 549 » » w.writeSignature(buf, typ) |
674 » » case 0: | |
675 » » » // nothing to do | |
676 » » case 1: | |
677 » » » buf.WriteByte(' ') | |
678 » » » w.writeType(buf, res.At(0).Type()) | |
679 » » default: | |
680 » » » buf.WriteByte(' ') | |
681 » » » w.writeParams(buf, res, false) | |
682 » » } | |
683 | 550 |
684 case *types.Interface: | 551 case *types.Interface: |
685 buf.WriteString("interface{") | 552 buf.WriteString("interface{") |
686 if typ.NumMethods() > 0 { | 553 if typ.NumMethods() > 0 { |
687 buf.WriteByte(' ') | 554 buf.WriteByte(' ') |
688 buf.WriteString(strings.Join(sortedMethodNames(typ), ",
")) | 555 buf.WriteString(strings.Join(sortedMethodNames(typ), ",
")) |
689 buf.WriteByte(' ') | 556 buf.WriteByte(' ') |
690 } | 557 } |
691 buf.WriteString("}") | 558 buf.WriteString("}") |
692 | 559 |
693 case *types.Map: | 560 case *types.Map: |
694 buf.WriteString("map[") | 561 buf.WriteString("map[") |
695 w.writeType(buf, typ.Key()) | 562 w.writeType(buf, typ.Key()) |
696 buf.WriteByte(']') | 563 buf.WriteByte(']') |
697 w.writeType(buf, typ.Elem()) | 564 w.writeType(buf, typ.Elem()) |
698 | 565 |
699 case *types.Chan: | 566 case *types.Chan: |
700 » » buf.WriteString("chan") | 567 » » var s string |
| 568 » » switch typ.Dir() { |
| 569 » » case ast.SEND: |
| 570 » » » s = "chan<- " |
| 571 » » case ast.RECV: |
| 572 » » » s = "<-chan " |
| 573 » » default: |
| 574 » » » s = "chan " |
| 575 » » } |
| 576 » » buf.WriteString(s) |
701 w.writeType(buf, typ.Elem()) | 577 w.writeType(buf, typ.Elem()) |
702 | 578 |
703 case *types.Named: | 579 case *types.Named: |
704 obj := typ.Obj() | 580 obj := typ.Obj() |
705 pkg := obj.Pkg() | 581 pkg := obj.Pkg() |
706 » » if pkg != nil && pkg != w.curPkg { | 582 » » if pkg != nil && pkg != w.current { |
707 buf.WriteString(pkg.Name()) | 583 buf.WriteString(pkg.Name()) |
708 buf.WriteByte('.') | 584 buf.WriteByte('.') |
709 } | 585 } |
710 buf.WriteString(typ.Obj().Name()) | 586 buf.WriteString(typ.Obj().Name()) |
711 | 587 |
712 default: | 588 default: |
713 panic(fmt.Sprintf("unknown type %T", typ)) | 589 panic(fmt.Sprintf("unknown type %T", typ)) |
| 590 } |
| 591 } |
| 592 |
| 593 func (w *Walker) writeSignature(buf *bytes.Buffer, sig *types.Signature) { |
| 594 w.writeParams(buf, sig.Params(), sig.IsVariadic()) |
| 595 switch res := sig.Results(); res.Len() { |
| 596 case 0: |
| 597 // nothing to do |
| 598 case 1: |
| 599 buf.WriteByte(' ') |
| 600 w.writeType(buf, res.At(0).Type()) |
| 601 default: |
| 602 buf.WriteByte(' ') |
| 603 w.writeParams(buf, res, false) |
714 } | 604 } |
715 } | 605 } |
716 | 606 |
717 func (w *Walker) writeParams(buf *bytes.Buffer, t *types.Tuple, variadic bool) { | 607 func (w *Walker) writeParams(buf *bytes.Buffer, t *types.Tuple, variadic bool) { |
718 buf.WriteByte('(') | 608 buf.WriteByte('(') |
719 for i, n := 0, t.Len(); i < n; i++ { | 609 for i, n := 0, t.Len(); i < n; i++ { |
720 if i > 0 { | 610 if i > 0 { |
721 buf.WriteString(", ") | 611 buf.WriteString(", ") |
722 } | 612 } |
723 typ := t.At(i).Type() | 613 typ := t.At(i).Type() |
724 if variadic && i+1 == n { | 614 if variadic && i+1 == n { |
725 buf.WriteString("...") | 615 buf.WriteString("...") |
726 typ = typ.(*types.Slice).Elem() | 616 typ = typ.(*types.Slice).Elem() |
727 } | 617 } |
728 w.writeType(buf, typ) | 618 w.writeType(buf, typ) |
729 } | 619 } |
730 buf.WriteByte(')') | 620 buf.WriteByte(')') |
731 } | 621 } |
732 | 622 |
733 func (w *Walker) typeString(typ types.Type) string { | 623 func (w *Walker) typeString(typ types.Type) string { |
734 var buf bytes.Buffer | 624 var buf bytes.Buffer |
735 w.writeType(&buf, typ) | 625 w.writeType(&buf, typ) |
736 return buf.String() | 626 return buf.String() |
737 } | 627 } |
738 | 628 |
| 629 func (w *Walker) signatureString(sig *types.Signature) string { |
| 630 var buf bytes.Buffer |
| 631 w.writeSignature(&buf, sig) |
| 632 return buf.String() |
| 633 } |
| 634 |
739 func (w *Walker) emitObj(obj types.Object) { | 635 func (w *Walker) emitObj(obj types.Object) { |
740 switch obj := obj.(type) { | 636 switch obj := obj.(type) { |
741 case *types.Const: | 637 case *types.Const: |
742 » » w.emitConst(obj) | 638 » » w.emitf("const %s %s", obj.Name(), w.typeString(obj.Type())) |
| 639 |
| 640 » case *types.Var: |
| 641 » » w.emitf("var %s %s", obj.Name(), w.typeString(obj.Type())) |
| 642 |
743 case *types.TypeName: | 643 case *types.TypeName: |
744 w.emitType(obj) | 644 w.emitType(obj) |
745 » » typ := obj.Type().(*types.Named) | 645 |
746 » » for i, n := 0, typ.NumMethods(); i < n; i++ { | |
747 » » » m := typ.Method(i) | |
748 » » » if m.IsExported() { | |
749 » » » » w.emitFunc(m) | |
750 » » » } | |
751 » » } | |
752 » » /* | |
753 » » » mset := obj.Type().MethodSet() | |
754 » » » for i, n := 0, mset.Len(); i < n; i++ { | |
755 » » » » m := mset.At(i).Obj().(*types.Func) | |
756 » » » » if m.IsExported() { | |
757 » » » » » w.emitFunc(m) | |
758 » » » » } | |
759 » » » } | |
760 » » */ | |
761 » case *types.Var: | |
762 » » w.emitVar(obj) | |
763 case *types.Func: | 646 case *types.Func: |
764 w.emitFunc(obj) | 647 w.emitFunc(obj) |
765 » } | 648 |
766 } | 649 » default: |
767 | 650 » » panic("unknown object: " + obj.String()) |
768 func (w *Walker) emitConst(obj *types.Const) { | 651 » } |
769 » w.emitf("const %s %s", obj.Name(), w.typeString(obj.Type())) | |
770 } | |
771 | |
772 func (w *Walker) emitVar(obj *types.Var) { | |
773 » w.emitf("var %s %s", obj.Name(), w.typeString(obj.Type())) | |
774 } | 652 } |
775 | 653 |
776 func (w *Walker) emitType(obj *types.TypeName) { | 654 func (w *Walker) emitType(obj *types.TypeName) { |
777 name := obj.Name() | 655 name := obj.Name() |
778 typ := obj.Type() | 656 typ := obj.Type() |
779 switch typ := typ.Underlying().(type) { | 657 switch typ := typ.Underlying().(type) { |
780 case *types.Struct: | 658 case *types.Struct: |
781 w.emitStructType(name, typ) | 659 w.emitStructType(name, typ) |
782 case *types.Interface: | 660 case *types.Interface: |
783 w.emitIfaceType(name, typ) | 661 w.emitIfaceType(name, typ) |
| 662 return // methods are handled by emitIfaceType |
784 default: | 663 default: |
785 w.emitf("type %s %s", name, w.typeString(typ.Underlying())) | 664 w.emitf("type %s %s", name, w.typeString(typ.Underlying())) |
| 665 } |
| 666 |
| 667 // emit methods with value receiver |
| 668 var methodNames map[string]bool |
| 669 vset := typ.MethodSet() |
| 670 for i, n := 0, vset.Len(); i < n; i++ { |
| 671 m := vset.At(i) |
| 672 if m.Obj().IsExported() { |
| 673 w.emitMethod(m) |
| 674 if methodNames == nil { |
| 675 methodNames = make(map[string]bool) |
| 676 } |
| 677 methodNames[m.Obj().Name()] = true |
| 678 } |
| 679 } |
| 680 |
| 681 // emit methods with pointer receiver; exclude |
| 682 // methods that we have emitted already |
| 683 // (the method set of *T includes the methods of T) |
| 684 pset := types.NewPointer(typ).MethodSet() |
| 685 for i, n := 0, pset.Len(); i < n; i++ { |
| 686 m := pset.At(i) |
| 687 if m.Obj().IsExported() && !methodNames[m.Obj().Name()] { |
| 688 w.emitMethod(m) |
| 689 } |
786 } | 690 } |
787 } | 691 } |
788 | 692 |
789 func (w *Walker) emitStructType(name string, typ *types.Struct) { | 693 func (w *Walker) emitStructType(name string, typ *types.Struct) { |
790 typeStruct := fmt.Sprintf("type %s struct", name) | 694 typeStruct := fmt.Sprintf("type %s struct", name) |
791 w.emitf(typeStruct) | 695 w.emitf(typeStruct) |
792 defer w.pushScope(typeStruct)() | 696 defer w.pushScope(typeStruct)() |
793 | 697 |
794 for i := 0; i < typ.NumFields(); i++ { | 698 for i := 0; i < typ.NumFields(); i++ { |
795 f := typ.Field(i) | 699 f := typ.Field(i) |
(...skipping 15 matching lines...) Expand all Loading... |
811 var methodNames []string | 715 var methodNames []string |
812 complete := true | 716 complete := true |
813 mset := typ.MethodSet() | 717 mset := typ.MethodSet() |
814 for i, n := 0, mset.Len(); i < n; i++ { | 718 for i, n := 0, mset.Len(); i < n; i++ { |
815 m := mset.At(i).Obj().(*types.Func) | 719 m := mset.At(i).Obj().(*types.Func) |
816 if !m.IsExported() { | 720 if !m.IsExported() { |
817 complete = false | 721 complete = false |
818 continue | 722 continue |
819 } | 723 } |
820 methodNames = append(methodNames, m.Name()) | 724 methodNames = append(methodNames, m.Name()) |
821 » » w.emitf("%s%s", m.Name(), w.typeString(m.Type())) | 725 » » w.emitf("%s%s", m.Name(), w.signatureString(m.Type().(*types.Sig
nature))) |
822 » } | 726 » } |
823 » /* | |
824 » » var methodNames []string | |
825 » » complete := true | |
826 » » for i, n := 0, typ.NumMethods(); i < n; i++ { | |
827 » » » m := typ.Method(i) | |
828 » » » if !m.IsExported() { | |
829 » » » » complete = false | |
830 » » » » continue | |
831 » » » } | |
832 » » » methodNames = append(methodNames, m.Name()) | |
833 » » » w.emitf("%s%s", m.Name(), w.typeString(m.Type())) | |
834 » » } | |
835 » */ | |
836 | 727 |
837 if !complete { | 728 if !complete { |
838 // The method set has unexported methods, so all the | 729 // The method set has unexported methods, so all the |
839 // implementations are provided by the same package, | 730 // implementations are provided by the same package, |
840 // so the method set can be extended. Instead of recording | 731 // so the method set can be extended. Instead of recording |
841 // the full set of names (below), record only that there were | 732 // the full set of names (below), record only that there were |
842 // unexported methods. (If the interface shrinks, we will notice | 733 // unexported methods. (If the interface shrinks, we will notice |
843 // because a method signature emitted during the last loop | 734 // because a method signature emitted during the last loop |
844 // will disappear.) | 735 // will disappear.) |
845 w.emitf("unexported methods") | 736 w.emitf("unexported methods") |
846 } | 737 } |
847 | 738 |
848 pop() | 739 pop() |
849 | 740 |
850 if !complete { | 741 if !complete { |
851 return | 742 return |
852 } | 743 } |
853 | 744 |
854 if len(methodNames) == 0 { | 745 if len(methodNames) == 0 { |
855 w.emitf("type %s interface {}", name) | 746 w.emitf("type %s interface {}", name) |
856 return | 747 return |
857 } | 748 } |
858 | 749 |
859 sort.Strings(methodNames) | 750 sort.Strings(methodNames) |
860 w.emitf("type %s interface { %s }", name, strings.Join(methodNames, ", "
)) | 751 w.emitf("type %s interface { %s }", name, strings.Join(methodNames, ", "
)) |
861 } | 752 } |
862 | 753 |
863 func (w *Walker) emitFunc(f *types.Func) { | 754 func (w *Walker) emitFunc(f *types.Func) { |
864 sig := f.Type().(*types.Signature) | 755 sig := f.Type().(*types.Signature) |
865 » if recv := sig.Recv(); recv != nil { | 756 » if sig.Recv() != nil { |
866 » » // method | 757 » » panic("method considered a regular function: " + f.String()) |
867 » » // TODO(gri) The original code excluded this method if the recei
ver | 758 » } |
868 » » // base type name was not exported. Do we need to do t
his? | 759 » w.emitf("func %s%s", f.Name(), w.signatureString(sig)) |
869 » » w.emitf("method (%s) %s%s", w.typeString(recv.Type()), f.Name(),
w.typeString(sig)) | 760 } |
870 » » return | 761 |
871 » } | 762 func (w *Walker) emitMethod(m *types.Selection) { |
872 | 763 » sig := m.Type().(*types.Signature) |
873 » // regular function | 764 » recv := sig.Recv().Type() |
874 » w.emitf("func %s%s", f.Name(), w.typeString(sig)) | 765 » // report exported methods with unexported reveiver base type |
| 766 » if true { |
| 767 » » base := recv |
| 768 » » if p, _ := recv.(*types.Pointer); p != nil { |
| 769 » » » base = p.Elem() |
| 770 » » } |
| 771 » » if obj := base.(*types.Named).Obj(); !obj.IsExported() { |
| 772 » » » log.Fatalf("exported method with unexported receiver bas
e type: %s", m) |
| 773 » » } |
| 774 » } |
| 775 » w.emitf("method (%s) %s%s", w.typeString(recv), m.Obj().Name(), w.signat
ureString(sig)) |
875 } | 776 } |
876 | 777 |
877 func (w *Walker) emitf(format string, args ...interface{}) { | 778 func (w *Walker) emitf(format string, args ...interface{}) { |
878 if !w.wantedPkg[w.curPkg.Name()] { | |
879 return | |
880 } | |
881 | |
882 f := strings.Join(w.scope, ", ") + ", " + fmt.Sprintf(format, args...) | 779 f := strings.Join(w.scope, ", ") + ", " + fmt.Sprintf(format, args...) |
883 if strings.Contains(f, "\n") { | 780 if strings.Contains(f, "\n") { |
884 panic("feature contains newlines: " + f) | 781 panic("feature contains newlines: " + f) |
885 } | 782 } |
886 | 783 |
887 if _, dup := w.features[f]; dup { | 784 if _, dup := w.features[f]; dup { |
888 panic("duplicate feature inserted: " + f) | 785 panic("duplicate feature inserted: " + f) |
889 } | 786 } |
890 w.features[f] = true | 787 w.features[f] = true |
891 | 788 |
892 if *verbose { | 789 if *verbose { |
893 log.Printf("feature: %s", f) | 790 log.Printf("feature: %s", f) |
894 } | 791 } |
895 } | 792 } |
LEFT | RIGHT |