Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(7)

Delta Between Two Patch Sets: src/cmd/api/goapi.go

Issue 12300043: code review 12300043: cmd/api: rewrite using go/types (Closed)
Left Patch Set: diff -r ab644299d124 https://code.google.com/p/go Created 11 years, 7 months ago
Right Patch Set: diff -r f53cee5f15de https://code.google.com/p/go Created 11 years, 7 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « src/cmd/api/clone.go ('k') | src/cmd/api/goapi_test.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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
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
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
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
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
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
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 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b