github.com/golang-haiku/go-1.4.3@v0.0.0-20190609233734-1f5ae41cc308/src/cmd/api/goapi.go (about)

     1  // Copyright 2011 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  // +build api_tool
     6  
     7  // Binary api computes the exported API of a set of Go packages.
     8  package main
     9  
    10  import (
    11  	"bufio"
    12  	"bytes"
    13  	"flag"
    14  	"fmt"
    15  	"go/ast"
    16  	"go/build"
    17  	"go/parser"
    18  	"go/token"
    19  	"io"
    20  	"io/ioutil"
    21  	"log"
    22  	"os"
    23  	"os/exec"
    24  	"path/filepath"
    25  	"regexp"
    26  	"runtime"
    27  	"sort"
    28  	"strings"
    29  
    30  	"code.google.com/p/go.tools/go/types"
    31  )
    32  
    33  // Flags
    34  var (
    35  	checkFile  = flag.String("c", "", "optional comma-separated filename(s) to check API against")
    36  	allowNew   = flag.Bool("allow_new", true, "allow API additions")
    37  	exceptFile = flag.String("except", "", "optional filename of packages that are allowed to change without triggering a failure in the tool")
    38  	nextFile   = flag.String("next", "", "optional filename of tentative upcoming API features for the next release. This file can be lazily maintained. It only affects the delta warnings from the -c file printed on success.")
    39  	verbose    = flag.Bool("v", false, "verbose debugging")
    40  	forceCtx   = flag.String("contexts", "", "optional comma-separated list of <goos>-<goarch>[-cgo] to override default contexts.")
    41  )
    42  
    43  // contexts are the default contexts which are scanned, unless
    44  // overridden by the -contexts flag.
    45  var contexts = []*build.Context{
    46  	{GOOS: "linux", GOARCH: "386", CgoEnabled: true},
    47  	{GOOS: "linux", GOARCH: "386"},
    48  	{GOOS: "linux", GOARCH: "amd64", CgoEnabled: true},
    49  	{GOOS: "linux", GOARCH: "amd64"},
    50  	{GOOS: "linux", GOARCH: "arm", CgoEnabled: true},
    51  	{GOOS: "linux", GOARCH: "arm"},
    52  	{GOOS: "darwin", GOARCH: "386", CgoEnabled: true},
    53  	{GOOS: "darwin", GOARCH: "386"},
    54  	{GOOS: "darwin", GOARCH: "amd64", CgoEnabled: true},
    55  	{GOOS: "darwin", GOARCH: "amd64"},
    56  	{GOOS: "windows", GOARCH: "amd64"},
    57  	{GOOS: "windows", GOARCH: "386"},
    58  	{GOOS: "freebsd", GOARCH: "386", CgoEnabled: true},
    59  	{GOOS: "freebsd", GOARCH: "386"},
    60  	{GOOS: "freebsd", GOARCH: "amd64", CgoEnabled: true},
    61  	{GOOS: "freebsd", GOARCH: "amd64"},
    62  	{GOOS: "freebsd", GOARCH: "arm", CgoEnabled: true},
    63  	{GOOS: "freebsd", GOARCH: "arm"},
    64  	{GOOS: "netbsd", GOARCH: "386", CgoEnabled: true},
    65  	{GOOS: "netbsd", GOARCH: "386"},
    66  	{GOOS: "netbsd", GOARCH: "amd64", CgoEnabled: true},
    67  	{GOOS: "netbsd", GOARCH: "amd64"},
    68  	{GOOS: "netbsd", GOARCH: "arm", CgoEnabled: true},
    69  	{GOOS: "netbsd", GOARCH: "arm"},
    70  	{GOOS: "openbsd", GOARCH: "386", CgoEnabled: true},
    71  	{GOOS: "openbsd", GOARCH: "386"},
    72  	{GOOS: "openbsd", GOARCH: "amd64", CgoEnabled: true},
    73  	{GOOS: "openbsd", GOARCH: "amd64"},
    74  }
    75  
    76  func contextName(c *build.Context) string {
    77  	s := c.GOOS + "-" + c.GOARCH
    78  	if c.CgoEnabled {
    79  		return s + "-cgo"
    80  	}
    81  	return s
    82  }
    83  
    84  func parseContext(c string) *build.Context {
    85  	parts := strings.Split(c, "-")
    86  	if len(parts) < 2 {
    87  		log.Fatalf("bad context: %q", c)
    88  	}
    89  	bc := &build.Context{
    90  		GOOS:   parts[0],
    91  		GOARCH: parts[1],
    92  	}
    93  	if len(parts) == 3 {
    94  		if parts[2] == "cgo" {
    95  			bc.CgoEnabled = true
    96  		} else {
    97  			log.Fatalf("bad context: %q", c)
    98  		}
    99  	}
   100  	return bc
   101  }
   102  
   103  func setContexts() {
   104  	contexts = []*build.Context{}
   105  	for _, c := range strings.Split(*forceCtx, ",") {
   106  		contexts = append(contexts, parseContext(c))
   107  	}
   108  }
   109  
   110  var internalPkg = regexp.MustCompile(`(^|/)internal($|/)`)
   111  
   112  func main() {
   113  	flag.Parse()
   114  
   115  	if !strings.Contains(runtime.Version(), "weekly") && !strings.Contains(runtime.Version(), "devel") {
   116  		if *nextFile != "" {
   117  			fmt.Printf("Go version is %q, ignoring -next %s\n", runtime.Version(), *nextFile)
   118  			*nextFile = ""
   119  		}
   120  	}
   121  
   122  	if *forceCtx != "" {
   123  		setContexts()
   124  	}
   125  	for _, c := range contexts {
   126  		c.Compiler = build.Default.Compiler
   127  	}
   128  
   129  	var pkgNames []string
   130  	if flag.NArg() > 0 {
   131  		pkgNames = flag.Args()
   132  	} else {
   133  		stds, err := exec.Command("go", "list", "std").Output()
   134  		if err != nil {
   135  			log.Fatal(err)
   136  		}
   137  		for _, pkg := range strings.Fields(string(stds)) {
   138  			if !internalPkg.MatchString(pkg) {
   139  				pkgNames = append(pkgNames, pkg)
   140  			}
   141  		}
   142  	}
   143  
   144  	var featureCtx = make(map[string]map[string]bool) // feature -> context name -> true
   145  	for _, context := range contexts {
   146  		w := NewWalker(context, filepath.Join(build.Default.GOROOT, "src"))
   147  
   148  		for _, name := range pkgNames {
   149  			// - Package "unsafe" contains special signatures requiring
   150  			//   extra care when printing them - ignore since it is not
   151  			//   going to change w/o a language change.
   152  			// - We don't care about the API of commands.
   153  			if name != "unsafe" && !strings.HasPrefix(name, "cmd/") {
   154  				if name == "runtime/cgo" && !context.CgoEnabled {
   155  					// w.Import(name) will return nil
   156  					continue
   157  				}
   158  				w.export(w.Import(name))
   159  			}
   160  		}
   161  
   162  		ctxName := contextName(context)
   163  		for _, f := range w.Features() {
   164  			if featureCtx[f] == nil {
   165  				featureCtx[f] = make(map[string]bool)
   166  			}
   167  			featureCtx[f][ctxName] = true
   168  		}
   169  	}
   170  
   171  	var features []string
   172  	for f, cmap := range featureCtx {
   173  		if len(cmap) == len(contexts) {
   174  			features = append(features, f)
   175  			continue
   176  		}
   177  		comma := strings.Index(f, ",")
   178  		for cname := range cmap {
   179  			f2 := fmt.Sprintf("%s (%s)%s", f[:comma], cname, f[comma:])
   180  			features = append(features, f2)
   181  		}
   182  	}
   183  
   184  	fail := false
   185  	defer func() {
   186  		if fail {
   187  			os.Exit(1)
   188  		}
   189  	}()
   190  
   191  	bw := bufio.NewWriter(os.Stdout)
   192  	defer bw.Flush()
   193  
   194  	if *checkFile == "" {
   195  		sort.Strings(features)
   196  		for _, f := range features {
   197  			fmt.Fprintln(bw, f)
   198  		}
   199  		return
   200  	}
   201  
   202  	var required []string
   203  	for _, file := range strings.Split(*checkFile, ",") {
   204  		required = append(required, fileFeatures(file)...)
   205  	}
   206  	optional := fileFeatures(*nextFile)
   207  	exception := fileFeatures(*exceptFile)
   208  	fail = !compareAPI(bw, features, required, optional, exception)
   209  }
   210  
   211  // export emits the exported package features.
   212  func (w *Walker) export(pkg *types.Package) {
   213  	if *verbose {
   214  		log.Println(pkg)
   215  	}
   216  	pop := w.pushScope("pkg " + pkg.Path())
   217  	w.current = pkg
   218  	scope := pkg.Scope()
   219  	for _, name := range scope.Names() {
   220  		if ast.IsExported(name) {
   221  			w.emitObj(scope.Lookup(name))
   222  		}
   223  	}
   224  	pop()
   225  }
   226  
   227  func set(items []string) map[string]bool {
   228  	s := make(map[string]bool)
   229  	for _, v := range items {
   230  		s[v] = true
   231  	}
   232  	return s
   233  }
   234  
   235  var spaceParensRx = regexp.MustCompile(` \(\S+?\)`)
   236  
   237  func featureWithoutContext(f string) string {
   238  	if !strings.Contains(f, "(") {
   239  		return f
   240  	}
   241  	return spaceParensRx.ReplaceAllString(f, "")
   242  }
   243  
   244  func compareAPI(w io.Writer, features, required, optional, exception []string) (ok bool) {
   245  	ok = true
   246  
   247  	optionalSet := set(optional)
   248  	exceptionSet := set(exception)
   249  	featureSet := set(features)
   250  
   251  	sort.Strings(features)
   252  	sort.Strings(required)
   253  
   254  	take := func(sl *[]string) string {
   255  		s := (*sl)[0]
   256  		*sl = (*sl)[1:]
   257  		return s
   258  	}
   259  
   260  	for len(required) > 0 || len(features) > 0 {
   261  		switch {
   262  		case len(features) == 0 || (len(required) > 0 && required[0] < features[0]):
   263  			feature := take(&required)
   264  			if exceptionSet[feature] {
   265  				// An "unfortunate" case: the feature was once
   266  				// included in the API (e.g. go1.txt), but was
   267  				// subsequently removed. These are already
   268  				// acknowledged by being in the file
   269  				// "api/except.txt". No need to print them out
   270  				// here.
   271  			} else if featureSet[featureWithoutContext(feature)] {
   272  				// okay.
   273  			} else {
   274  				fmt.Fprintf(w, "-%s\n", feature)
   275  				ok = false // broke compatibility
   276  			}
   277  		case len(required) == 0 || (len(features) > 0 && required[0] > features[0]):
   278  			newFeature := take(&features)
   279  			if optionalSet[newFeature] {
   280  				// Known added feature to the upcoming release.
   281  				// Delete it from the map so we can detect any upcoming features
   282  				// which were never seen.  (so we can clean up the nextFile)
   283  				delete(optionalSet, newFeature)
   284  			} else {
   285  				fmt.Fprintf(w, "+%s\n", newFeature)
   286  				if !*allowNew || !strings.Contains(runtime.Version(), "devel") {
   287  					ok = false // we're in lock-down mode for next release
   288  				}
   289  			}
   290  		default:
   291  			take(&required)
   292  			take(&features)
   293  		}
   294  	}
   295  
   296  	// In next file, but not in API.
   297  	var missing []string
   298  	for feature := range optionalSet {
   299  		missing = append(missing, feature)
   300  	}
   301  	sort.Strings(missing)
   302  	for _, feature := range missing {
   303  		fmt.Fprintf(w, "±%s\n", feature)
   304  	}
   305  	return
   306  }
   307  
   308  func fileFeatures(filename string) []string {
   309  	if filename == "" {
   310  		return nil
   311  	}
   312  	bs, err := ioutil.ReadFile(filename)
   313  	if err != nil {
   314  		log.Fatalf("Error reading file %s: %v", filename, err)
   315  	}
   316  	lines := strings.Split(string(bs), "\n")
   317  	var nonblank []string
   318  	for _, line := range lines {
   319  		line = strings.TrimSpace(line)
   320  		if line != "" && !strings.HasPrefix(line, "#") {
   321  			nonblank = append(nonblank, line)
   322  		}
   323  	}
   324  	return nonblank
   325  }
   326  
   327  var fset = token.NewFileSet()
   328  
   329  type Walker struct {
   330  	context  *build.Context
   331  	root     string
   332  	scope    []string
   333  	current  *types.Package
   334  	features map[string]bool           // set
   335  	imported map[string]*types.Package // packages already imported
   336  }
   337  
   338  func NewWalker(context *build.Context, root string) *Walker {
   339  	return &Walker{
   340  		context:  context,
   341  		root:     root,
   342  		features: map[string]bool{},
   343  		imported: map[string]*types.Package{"unsafe": types.Unsafe},
   344  	}
   345  }
   346  
   347  func (w *Walker) Features() (fs []string) {
   348  	for f := range w.features {
   349  		fs = append(fs, f)
   350  	}
   351  	sort.Strings(fs)
   352  	return
   353  }
   354  
   355  var parsedFileCache = make(map[string]*ast.File)
   356  
   357  func (w *Walker) parseFile(dir, file string) (*ast.File, error) {
   358  	filename := filepath.Join(dir, file)
   359  	f, _ := parsedFileCache[filename]
   360  	if f != nil {
   361  		return f, nil
   362  	}
   363  
   364  	var err error
   365  
   366  	// generate missing context-dependent files.
   367  
   368  	if w.context != nil && file == fmt.Sprintf("zgoos_%s.go", w.context.GOOS) {
   369  		src := fmt.Sprintf("package runtime; const theGoos = `%s`", w.context.GOOS)
   370  		f, err = parser.ParseFile(fset, filename, src, 0)
   371  		if err != nil {
   372  			log.Fatalf("incorrect generated file: %s", err)
   373  		}
   374  	}
   375  
   376  	if w.context != nil && file == fmt.Sprintf("zgoarch_%s.go", w.context.GOARCH) {
   377  		src := fmt.Sprintf("package runtime; const theGoarch = `%s`", w.context.GOARCH)
   378  		f, err = parser.ParseFile(fset, filename, src, 0)
   379  		if err != nil {
   380  			log.Fatalf("incorrect generated file: %s", err)
   381  		}
   382  	}
   383  	if w.context != nil && file == fmt.Sprintf("zruntime_defs_%s_%s.go", w.context.GOOS, w.context.GOARCH) {
   384  		// Just enough to keep the api checker happy. Keep sorted.
   385  		src := "package runtime; type (" +
   386  			" _defer struct{};" +
   387  			" _func struct{};" +
   388  			" _panic struct{};" +
   389  			" _select struct{}; " +
   390  			" _type struct{};" +
   391  			" alg struct{};" +
   392  			" chantype struct{};" +
   393  			" context struct{};" + // windows
   394  			" eface struct{};" +
   395  			" epollevent struct{};" +
   396  			" funcval struct{};" +
   397  			" g struct{};" +
   398  			" gobuf struct{};" +
   399  			" hchan struct{};" +
   400  			" iface struct{};" +
   401  			" interfacetype struct{};" +
   402  			" itab struct{};" +
   403  			" keventt struct{};" +
   404  			" m struct{};" +
   405  			" maptype struct{};" +
   406  			" mcache struct{};" +
   407  			" mspan struct{};" +
   408  			" mutex struct{};" +
   409  			" note struct{};" +
   410  			" p struct{};" +
   411  			" parfor struct{};" +
   412  			" slicetype struct{};" +
   413  			" stkframe struct{};" +
   414  			" sudog struct{};" +
   415  			" timespec struct{};" +
   416  			" waitq struct{};" +
   417  			" wincallbackcontext struct{};" +
   418  			"); " +
   419  			"const (" +
   420  			" cb_max = 2000;" +
   421  			" _CacheLineSize = 64;" +
   422  			" _Gidle = 1;" +
   423  			" _Grunnable = 2;" +
   424  			" _Grunning = 3;" +
   425  			" _Gsyscall = 4;" +
   426  			" _Gwaiting = 5;" +
   427  			" _Gdead = 6;" +
   428  			" _Genqueue = 7;" +
   429  			" _Gcopystack = 8;" +
   430  			" _NSIG = 32;" +
   431  			" _FlagNoScan = iota;" +
   432  			" _FlagNoZero;" +
   433  			" _TinySize;" +
   434  			" _TinySizeClass;" +
   435  			" _MaxSmallSize;" +
   436  			" _PageShift;" +
   437  			" _PageSize;" +
   438  			" _PageMask;" +
   439  			" _BitsPerPointer;" +
   440  			" _BitsMask;" +
   441  			" _PointersPerByte;" +
   442  			" _MaxGCMask;" +
   443  			" _BitsDead;" +
   444  			" _BitsPointer;" +
   445  			" _MSpanInUse;" +
   446  			" _ConcurrentSweep;" +
   447  			" _KindBool;" +
   448  			" _KindInt;" +
   449  			" _KindInt8;" +
   450  			" _KindInt16;" +
   451  			" _KindInt32;" +
   452  			" _KindInt64;" +
   453  			" _KindUint;" +
   454  			" _KindUint8;" +
   455  			" _KindUint16;" +
   456  			" _KindUint32;" +
   457  			" _KindUint64;" +
   458  			" _KindUintptr;" +
   459  			" _KindFloat32;" +
   460  			" _KindFloat64;" +
   461  			" _KindComplex64;" +
   462  			" _KindComplex128;" +
   463  			" _KindArray;" +
   464  			" _KindChan;" +
   465  			" _KindFunc;" +
   466  			" _KindInterface;" +
   467  			" _KindMap;" +
   468  			" _KindPtr;" +
   469  			" _KindSlice;" +
   470  			" _KindString;" +
   471  			" _KindStruct;" +
   472  			" _KindUnsafePointer;" +
   473  			" _KindDirectIface;" +
   474  			" _KindGCProg;" +
   475  			" _KindNoPointers;" +
   476  			" _KindMask;" +
   477  			")"
   478  		f, err = parser.ParseFile(fset, filename, src, 0)
   479  		if err != nil {
   480  			log.Fatalf("incorrect generated file: %s", err)
   481  		}
   482  	}
   483  
   484  	if f == nil {
   485  		f, err = parser.ParseFile(fset, filename, nil, 0)
   486  		if err != nil {
   487  			return nil, err
   488  		}
   489  	}
   490  
   491  	parsedFileCache[filename] = f
   492  	return f, nil
   493  }
   494  
   495  func contains(list []string, s string) bool {
   496  	for _, t := range list {
   497  		if t == s {
   498  			return true
   499  		}
   500  	}
   501  	return false
   502  }
   503  
   504  // The package cache doesn't operate correctly in rare (so far artificial)
   505  // circumstances (issue 8425). Disable before debugging non-obvious errors
   506  // from the type-checker.
   507  const usePkgCache = true
   508  
   509  var (
   510  	pkgCache = map[string]*types.Package{} // map tagKey to package
   511  	pkgTags  = map[string][]string{}       // map import dir to list of relevant tags
   512  )
   513  
   514  // tagKey returns the tag-based key to use in the pkgCache.
   515  // It is a comma-separated string; the first part is dir, the rest tags.
   516  // The satisfied tags are derived from context but only those that
   517  // matter (the ones listed in the tags argument) are used.
   518  // The tags list, which came from go/build's Package.AllTags,
   519  // is known to be sorted.
   520  func tagKey(dir string, context *build.Context, tags []string) string {
   521  	ctags := map[string]bool{
   522  		context.GOOS:   true,
   523  		context.GOARCH: true,
   524  	}
   525  	if context.CgoEnabled {
   526  		ctags["cgo"] = true
   527  	}
   528  	for _, tag := range context.BuildTags {
   529  		ctags[tag] = true
   530  	}
   531  	// TODO: ReleaseTags (need to load default)
   532  	key := dir
   533  	for _, tag := range tags {
   534  		if ctags[tag] {
   535  			key += "," + tag
   536  		}
   537  	}
   538  	return key
   539  }
   540  
   541  // Importing is a sentinel taking the place in Walker.imported
   542  // for a package that is in the process of being imported.
   543  var importing types.Package
   544  
   545  func (w *Walker) Import(name string) (pkg *types.Package) {
   546  	pkg = w.imported[name]
   547  	if pkg != nil {
   548  		if pkg == &importing {
   549  			log.Fatalf("cycle importing package %q", name)
   550  		}
   551  		return pkg
   552  	}
   553  	w.imported[name] = &importing
   554  
   555  	// Determine package files.
   556  	dir := filepath.Join(w.root, filepath.FromSlash(name))
   557  	if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
   558  		log.Fatalf("no source in tree for package %q", pkg)
   559  	}
   560  
   561  	context := w.context
   562  	if context == nil {
   563  		context = &build.Default
   564  	}
   565  
   566  	// Look in cache.
   567  	// If we've already done an import with the same set
   568  	// of relevant tags, reuse the result.
   569  	var key string
   570  	if usePkgCache {
   571  		if tags, ok := pkgTags[dir]; ok {
   572  			key = tagKey(dir, context, tags)
   573  			if pkg := pkgCache[key]; pkg != nil {
   574  				w.imported[name] = pkg
   575  				return pkg
   576  			}
   577  		}
   578  	}
   579  
   580  	info, err := context.ImportDir(dir, 0)
   581  	if err != nil {
   582  		if _, nogo := err.(*build.NoGoError); nogo {
   583  			return
   584  		}
   585  		log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, dir, err)
   586  	}
   587  
   588  	// Save tags list first time we see a directory.
   589  	if usePkgCache {
   590  		if _, ok := pkgTags[dir]; !ok {
   591  			pkgTags[dir] = info.AllTags
   592  			key = tagKey(dir, context, info.AllTags)
   593  		}
   594  	}
   595  
   596  	filenames := append(append([]string{}, info.GoFiles...), info.CgoFiles...)
   597  
   598  	// Certain files only exist when building for the specified context.
   599  	// Add them manually.
   600  	if name == "runtime" {
   601  		n := fmt.Sprintf("zgoos_%s.go", w.context.GOOS)
   602  		if !contains(filenames, n) {
   603  			filenames = append(filenames, n)
   604  		}
   605  
   606  		n = fmt.Sprintf("zgoarch_%s.go", w.context.GOARCH)
   607  		if !contains(filenames, n) {
   608  			filenames = append(filenames, n)
   609  		}
   610  
   611  		n = fmt.Sprintf("zruntime_defs_%s_%s.go", w.context.GOOS, w.context.GOARCH)
   612  		if !contains(filenames, n) {
   613  			filenames = append(filenames, n)
   614  		}
   615  	}
   616  
   617  	// Parse package files.
   618  	var files []*ast.File
   619  	for _, file := range filenames {
   620  		f, err := w.parseFile(dir, file)
   621  		if err != nil {
   622  			log.Fatalf("error parsing package %s: %s", name, err)
   623  		}
   624  		files = append(files, f)
   625  	}
   626  
   627  	// Type-check package files.
   628  	conf := types.Config{
   629  		IgnoreFuncBodies: true,
   630  		FakeImportC:      true,
   631  		Import: func(imports map[string]*types.Package, name string) (*types.Package, error) {
   632  			pkg := w.Import(name)
   633  			imports[name] = pkg
   634  			return pkg, nil
   635  		},
   636  	}
   637  	pkg, err = conf.Check(name, fset, files, nil)
   638  	if err != nil {
   639  		ctxt := "<no context>"
   640  		if w.context != nil {
   641  			ctxt = fmt.Sprintf("%s-%s", w.context.GOOS, w.context.GOARCH)
   642  		}
   643  		log.Fatalf("error typechecking package %s: %s (%s)", name, err, ctxt)
   644  	}
   645  
   646  	if usePkgCache {
   647  		pkgCache[key] = pkg
   648  	}
   649  
   650  	w.imported[name] = pkg
   651  	return
   652  }
   653  
   654  // pushScope enters a new scope (walking a package, type, node, etc)
   655  // and returns a function that will leave the scope (with sanity checking
   656  // for mismatched pushes & pops)
   657  func (w *Walker) pushScope(name string) (popFunc func()) {
   658  	w.scope = append(w.scope, name)
   659  	return func() {
   660  		if len(w.scope) == 0 {
   661  			log.Fatalf("attempt to leave scope %q with empty scope list", name)
   662  		}
   663  		if w.scope[len(w.scope)-1] != name {
   664  			log.Fatalf("attempt to leave scope %q, but scope is currently %#v", name, w.scope)
   665  		}
   666  		w.scope = w.scope[:len(w.scope)-1]
   667  	}
   668  }
   669  
   670  func sortedMethodNames(typ *types.Interface) []string {
   671  	n := typ.NumMethods()
   672  	list := make([]string, n)
   673  	for i := range list {
   674  		list[i] = typ.Method(i).Name()
   675  	}
   676  	sort.Strings(list)
   677  	return list
   678  }
   679  
   680  func (w *Walker) writeType(buf *bytes.Buffer, typ types.Type) {
   681  	switch typ := typ.(type) {
   682  	case *types.Basic:
   683  		s := typ.Name()
   684  		switch typ.Kind() {
   685  		case types.UnsafePointer:
   686  			s = "unsafe.Pointer"
   687  		case types.UntypedBool:
   688  			s = "ideal-bool"
   689  		case types.UntypedInt:
   690  			s = "ideal-int"
   691  		case types.UntypedRune:
   692  			// "ideal-char" for compatibility with old tool
   693  			// TODO(gri) change to "ideal-rune"
   694  			s = "ideal-char"
   695  		case types.UntypedFloat:
   696  			s = "ideal-float"
   697  		case types.UntypedComplex:
   698  			s = "ideal-complex"
   699  		case types.UntypedString:
   700  			s = "ideal-string"
   701  		case types.UntypedNil:
   702  			panic("should never see untyped nil type")
   703  		default:
   704  			switch s {
   705  			case "byte":
   706  				s = "uint8"
   707  			case "rune":
   708  				s = "int32"
   709  			}
   710  		}
   711  		buf.WriteString(s)
   712  
   713  	case *types.Array:
   714  		fmt.Fprintf(buf, "[%d]", typ.Len())
   715  		w.writeType(buf, typ.Elem())
   716  
   717  	case *types.Slice:
   718  		buf.WriteString("[]")
   719  		w.writeType(buf, typ.Elem())
   720  
   721  	case *types.Struct:
   722  		buf.WriteString("struct")
   723  
   724  	case *types.Pointer:
   725  		buf.WriteByte('*')
   726  		w.writeType(buf, typ.Elem())
   727  
   728  	case *types.Tuple:
   729  		panic("should never see a tuple type")
   730  
   731  	case *types.Signature:
   732  		buf.WriteString("func")
   733  		w.writeSignature(buf, typ)
   734  
   735  	case *types.Interface:
   736  		buf.WriteString("interface{")
   737  		if typ.NumMethods() > 0 {
   738  			buf.WriteByte(' ')
   739  			buf.WriteString(strings.Join(sortedMethodNames(typ), ", "))
   740  			buf.WriteByte(' ')
   741  		}
   742  		buf.WriteString("}")
   743  
   744  	case *types.Map:
   745  		buf.WriteString("map[")
   746  		w.writeType(buf, typ.Key())
   747  		buf.WriteByte(']')
   748  		w.writeType(buf, typ.Elem())
   749  
   750  	case *types.Chan:
   751  		var s string
   752  		switch typ.Dir() {
   753  		case ast.SEND:
   754  			s = "chan<- "
   755  		case ast.RECV:
   756  			s = "<-chan "
   757  		default:
   758  			s = "chan "
   759  		}
   760  		buf.WriteString(s)
   761  		w.writeType(buf, typ.Elem())
   762  
   763  	case *types.Named:
   764  		obj := typ.Obj()
   765  		pkg := obj.Pkg()
   766  		if pkg != nil && pkg != w.current {
   767  			buf.WriteString(pkg.Name())
   768  			buf.WriteByte('.')
   769  		}
   770  		buf.WriteString(typ.Obj().Name())
   771  
   772  	default:
   773  		panic(fmt.Sprintf("unknown type %T", typ))
   774  	}
   775  }
   776  
   777  func (w *Walker) writeSignature(buf *bytes.Buffer, sig *types.Signature) {
   778  	w.writeParams(buf, sig.Params(), sig.IsVariadic())
   779  	switch res := sig.Results(); res.Len() {
   780  	case 0:
   781  		// nothing to do
   782  	case 1:
   783  		buf.WriteByte(' ')
   784  		w.writeType(buf, res.At(0).Type())
   785  	default:
   786  		buf.WriteByte(' ')
   787  		w.writeParams(buf, res, false)
   788  	}
   789  }
   790  
   791  func (w *Walker) writeParams(buf *bytes.Buffer, t *types.Tuple, variadic bool) {
   792  	buf.WriteByte('(')
   793  	for i, n := 0, t.Len(); i < n; i++ {
   794  		if i > 0 {
   795  			buf.WriteString(", ")
   796  		}
   797  		typ := t.At(i).Type()
   798  		if variadic && i+1 == n {
   799  			buf.WriteString("...")
   800  			typ = typ.(*types.Slice).Elem()
   801  		}
   802  		w.writeType(buf, typ)
   803  	}
   804  	buf.WriteByte(')')
   805  }
   806  
   807  func (w *Walker) typeString(typ types.Type) string {
   808  	var buf bytes.Buffer
   809  	w.writeType(&buf, typ)
   810  	return buf.String()
   811  }
   812  
   813  func (w *Walker) signatureString(sig *types.Signature) string {
   814  	var buf bytes.Buffer
   815  	w.writeSignature(&buf, sig)
   816  	return buf.String()
   817  }
   818  
   819  func (w *Walker) emitObj(obj types.Object) {
   820  	switch obj := obj.(type) {
   821  	case *types.Const:
   822  		w.emitf("const %s %s", obj.Name(), w.typeString(obj.Type()))
   823  		w.emitf("const %s = %s", obj.Name(), obj.Val())
   824  	case *types.Var:
   825  		w.emitf("var %s %s", obj.Name(), w.typeString(obj.Type()))
   826  	case *types.TypeName:
   827  		w.emitType(obj)
   828  	case *types.Func:
   829  		w.emitFunc(obj)
   830  	default:
   831  		panic("unknown object: " + obj.String())
   832  	}
   833  }
   834  
   835  func (w *Walker) emitType(obj *types.TypeName) {
   836  	name := obj.Name()
   837  	typ := obj.Type()
   838  	switch typ := typ.Underlying().(type) {
   839  	case *types.Struct:
   840  		w.emitStructType(name, typ)
   841  	case *types.Interface:
   842  		w.emitIfaceType(name, typ)
   843  		return // methods are handled by emitIfaceType
   844  	default:
   845  		w.emitf("type %s %s", name, w.typeString(typ.Underlying()))
   846  	}
   847  
   848  	// emit methods with value receiver
   849  	var methodNames map[string]bool
   850  	vset := typ.MethodSet()
   851  	for i, n := 0, vset.Len(); i < n; i++ {
   852  		m := vset.At(i)
   853  		if m.Obj().IsExported() {
   854  			w.emitMethod(m)
   855  			if methodNames == nil {
   856  				methodNames = make(map[string]bool)
   857  			}
   858  			methodNames[m.Obj().Name()] = true
   859  		}
   860  	}
   861  
   862  	// emit methods with pointer receiver; exclude
   863  	// methods that we have emitted already
   864  	// (the method set of *T includes the methods of T)
   865  	pset := types.NewPointer(typ).MethodSet()
   866  	for i, n := 0, pset.Len(); i < n; i++ {
   867  		m := pset.At(i)
   868  		if m.Obj().IsExported() && !methodNames[m.Obj().Name()] {
   869  			w.emitMethod(m)
   870  		}
   871  	}
   872  }
   873  
   874  func (w *Walker) emitStructType(name string, typ *types.Struct) {
   875  	typeStruct := fmt.Sprintf("type %s struct", name)
   876  	w.emitf(typeStruct)
   877  	defer w.pushScope(typeStruct)()
   878  
   879  	for i := 0; i < typ.NumFields(); i++ {
   880  		f := typ.Field(i)
   881  		if !f.IsExported() {
   882  			continue
   883  		}
   884  		typ := f.Type()
   885  		if f.Anonymous() {
   886  			w.emitf("embedded %s", w.typeString(typ))
   887  			continue
   888  		}
   889  		w.emitf("%s %s", f.Name(), w.typeString(typ))
   890  	}
   891  }
   892  
   893  func (w *Walker) emitIfaceType(name string, typ *types.Interface) {
   894  	pop := w.pushScope("type " + name + " interface")
   895  
   896  	var methodNames []string
   897  	complete := true
   898  	mset := typ.MethodSet()
   899  	for i, n := 0, mset.Len(); i < n; i++ {
   900  		m := mset.At(i).Obj().(*types.Func)
   901  		if !m.IsExported() {
   902  			complete = false
   903  			continue
   904  		}
   905  		methodNames = append(methodNames, m.Name())
   906  		w.emitf("%s%s", m.Name(), w.signatureString(m.Type().(*types.Signature)))
   907  	}
   908  
   909  	if !complete {
   910  		// The method set has unexported methods, so all the
   911  		// implementations are provided by the same package,
   912  		// so the method set can be extended. Instead of recording
   913  		// the full set of names (below), record only that there were
   914  		// unexported methods. (If the interface shrinks, we will notice
   915  		// because a method signature emitted during the last loop
   916  		// will disappear.)
   917  		w.emitf("unexported methods")
   918  	}
   919  
   920  	pop()
   921  
   922  	if !complete {
   923  		return
   924  	}
   925  
   926  	if len(methodNames) == 0 {
   927  		w.emitf("type %s interface {}", name)
   928  		return
   929  	}
   930  
   931  	sort.Strings(methodNames)
   932  	w.emitf("type %s interface { %s }", name, strings.Join(methodNames, ", "))
   933  }
   934  
   935  func (w *Walker) emitFunc(f *types.Func) {
   936  	sig := f.Type().(*types.Signature)
   937  	if sig.Recv() != nil {
   938  		panic("method considered a regular function: " + f.String())
   939  	}
   940  	w.emitf("func %s%s", f.Name(), w.signatureString(sig))
   941  }
   942  
   943  func (w *Walker) emitMethod(m *types.Selection) {
   944  	sig := m.Type().(*types.Signature)
   945  	recv := sig.Recv().Type()
   946  	// report exported methods with unexported receiver base type
   947  	if true {
   948  		base := recv
   949  		if p, _ := recv.(*types.Pointer); p != nil {
   950  			base = p.Elem()
   951  		}
   952  		if obj := base.(*types.Named).Obj(); !obj.IsExported() {
   953  			log.Fatalf("exported method with unexported receiver base type: %s", m)
   954  		}
   955  	}
   956  	w.emitf("method (%s) %s%s", w.typeString(recv), m.Obj().Name(), w.signatureString(sig))
   957  }
   958  
   959  func (w *Walker) emitf(format string, args ...interface{}) {
   960  	f := strings.Join(w.scope, ", ") + ", " + fmt.Sprintf(format, args...)
   961  	if strings.Contains(f, "\n") {
   962  		panic("feature contains newlines: " + f)
   963  	}
   964  
   965  	if _, dup := w.features[f]; dup {
   966  		panic("duplicate feature inserted: " + f)
   967  	}
   968  	w.features[f] = true
   969  
   970  	if *verbose {
   971  		log.Printf("feature: %s", f)
   972  	}
   973  }