github.com/galaxyobe/gen@v0.0.0-20220910125335-392fa8f0990f/third_party/gengo/parser/parse.go (about)

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package parser
    18  
    19  import (
    20  	"fmt"
    21  	"go/ast"
    22  	"go/build"
    23  	"go/constant"
    24  	"go/parser"
    25  	"go/token"
    26  	tc "go/types"
    27  	"io/ioutil"
    28  	"os"
    29  	"os/exec"
    30  	"path"
    31  	"path/filepath"
    32  	"regexp"
    33  	"sort"
    34  	"strings"
    35  
    36  	"k8s.io/gengo/types"
    37  	"k8s.io/klog/v2"
    38  
    39  	tptypes "github.com/galaxyobe/gen/third_party/gengo/types"
    40  )
    41  
    42  // This clarifies when a pkg path has been canonicalized.
    43  type importPathString string
    44  
    45  // Builder lets you add all the go files in all the packages that you care
    46  // about, then constructs the type source data.
    47  type Builder struct {
    48  	context *build.Context
    49  
    50  	// If true, include *_test.go
    51  	IncludeTestFiles bool
    52  
    53  	// Map of package names to more canonical information about the package.
    54  	// This might hold the same value for multiple names, e.g. if someone
    55  	// referenced ./pkg/name or in the case of vendoring, which canonicalizes
    56  	// differently that what humans would type.
    57  	//
    58  	// This must only be accessed via getLoadedBuildPackage and setLoadedBuildPackage
    59  	buildPackages map[importPathString]*build.Package
    60  
    61  	fset *token.FileSet
    62  	// map of package path to list of parsed files
    63  	parsed map[importPathString][]parsedFile
    64  	// map of package path to absolute path (to prevent overlap)
    65  	absPaths map[importPathString]string
    66  
    67  	// Set by typeCheckPackage(), used by importPackage() and friends.
    68  	typeCheckedPackages map[importPathString]*tc.Package
    69  
    70  	// Map of package path to whether the user requested it or it was from
    71  	// an import.
    72  	userRequested map[importPathString]bool
    73  
    74  	// All comments from everywhere in every parsed file.
    75  	endLineToCommentGroup map[fileLine]*ast.CommentGroup
    76  
    77  	// map of package to list of packages it imports.
    78  	importGraph map[importPathString]map[string]struct{}
    79  }
    80  
    81  // parsedFile is for tracking files with name
    82  type parsedFile struct {
    83  	name string
    84  	file *ast.File
    85  }
    86  
    87  // key type for finding comments.
    88  type fileLine struct {
    89  	file string
    90  	line int
    91  }
    92  
    93  // New constructs a new builder.
    94  func New() *Builder {
    95  	c := build.Default
    96  	if c.GOROOT == "" {
    97  		if p, err := exec.Command("which", "go").CombinedOutput(); err == nil {
    98  			// The returned string will have some/path/bin/go, so remove the last two elements.
    99  			c.GOROOT = filepath.Dir(filepath.Dir(strings.Trim(string(p), "\n")))
   100  		} else {
   101  			klog.Warningf("Warning: $GOROOT not set, and unable to run `which go` to find it: %v\n", err)
   102  		}
   103  	}
   104  	// Force this to off, since we don't properly parse CGo.  All symbols must
   105  	// have non-CGo equivalents.
   106  	c.CgoEnabled = false
   107  	return &Builder{
   108  		context:               &c,
   109  		buildPackages:         map[importPathString]*build.Package{},
   110  		typeCheckedPackages:   map[importPathString]*tc.Package{},
   111  		fset:                  token.NewFileSet(),
   112  		parsed:                map[importPathString][]parsedFile{},
   113  		absPaths:              map[importPathString]string{},
   114  		userRequested:         map[importPathString]bool{},
   115  		endLineToCommentGroup: map[fileLine]*ast.CommentGroup{},
   116  		importGraph:           map[importPathString]map[string]struct{}{},
   117  	}
   118  }
   119  
   120  // AddBuildTags adds the specified build tags to the parse context.
   121  func (b *Builder) AddBuildTags(tags ...string) {
   122  	b.context.BuildTags = append(b.context.BuildTags, tags...)
   123  }
   124  
   125  func (b *Builder) getLoadedBuildPackage(importPath string) (*build.Package, bool) {
   126  	canonicalized := canonicalizeImportPath(importPath)
   127  	if string(canonicalized) != importPath {
   128  		klog.V(5).Infof("getLoadedBuildPackage: %s normalized to %s", importPath, canonicalized)
   129  	}
   130  	buildPkg, ok := b.buildPackages[canonicalized]
   131  	return buildPkg, ok
   132  }
   133  func (b *Builder) setLoadedBuildPackage(importPath string, buildPkg *build.Package) {
   134  	canonicalizedImportPath := canonicalizeImportPath(importPath)
   135  	if string(canonicalizedImportPath) != importPath {
   136  		klog.V(5).Infof("setLoadedBuildPackage: importPath %s normalized to %s", importPath, canonicalizedImportPath)
   137  	}
   138  
   139  	canonicalizedBuildPkgImportPath := canonicalizeImportPath(buildPkg.ImportPath)
   140  	if string(canonicalizedBuildPkgImportPath) != buildPkg.ImportPath {
   141  		klog.V(5).Infof("setLoadedBuildPackage: buildPkg.ImportPath %s normalized to %s", buildPkg.ImportPath, canonicalizedBuildPkgImportPath)
   142  	}
   143  
   144  	if canonicalizedImportPath != canonicalizedBuildPkgImportPath {
   145  		klog.V(5).Infof("setLoadedBuildPackage: normalized importPath (%s) differs from buildPkg.ImportPath (%s)", canonicalizedImportPath, canonicalizedBuildPkgImportPath)
   146  	}
   147  	b.buildPackages[canonicalizedImportPath] = buildPkg
   148  	b.buildPackages[canonicalizedBuildPkgImportPath] = buildPkg
   149  }
   150  
   151  // Get package information from the go/build package. Automatically excludes
   152  // e.g. test files and files for other platforms-- there is quite a bit of
   153  // logic of that nature in the build package.
   154  func (b *Builder) importBuildPackage(dir string) (*build.Package, error) {
   155  	if buildPkg, ok := b.getLoadedBuildPackage(dir); ok {
   156  		return buildPkg, nil
   157  	}
   158  	// This validates the `package foo // github.com/bar/foo` comments.
   159  	buildPkg, err := b.importWithMode(dir, build.ImportComment)
   160  	if err != nil {
   161  		if _, ok := err.(*build.NoGoError); !ok {
   162  			return nil, fmt.Errorf("unable to import %q: %v", dir, err)
   163  		}
   164  	}
   165  	if buildPkg == nil {
   166  		// Might be an empty directory. Try to just find the dir.
   167  		buildPkg, err = b.importWithMode(dir, build.FindOnly)
   168  		if err != nil {
   169  			return nil, err
   170  		}
   171  	}
   172  
   173  	// Remember it under the user-provided name.
   174  	klog.V(5).Infof("saving buildPackage %s", dir)
   175  	b.setLoadedBuildPackage(dir, buildPkg)
   176  
   177  	return buildPkg, nil
   178  }
   179  
   180  // AddFileForTest adds a file to the set, without verifying that the provided
   181  // pkg actually exists on disk. The pkg must be of the form "canonical/pkg/path"
   182  // and the path must be the absolute path to the file.  Because this bypasses
   183  // the normal recursive finding of package dependencies (on disk), test should
   184  // sort their test files topologically first, so all deps are resolved by the
   185  // time we need them.
   186  func (b *Builder) AddFileForTest(pkg string, path string, src []byte) error {
   187  	if err := b.addFile(importPathString(pkg), path, src, true); err != nil {
   188  		return err
   189  	}
   190  	if _, err := b.typeCheckPackage(importPathString(pkg), true); err != nil {
   191  		return err
   192  	}
   193  	return nil
   194  }
   195  
   196  // addFile adds a file to the set. The pkgPath must be of the form
   197  // "canonical/pkg/path" and the path must be the absolute path to the file. A
   198  // flag indicates whether this file was user-requested or just from following
   199  // the import graph.
   200  func (b *Builder) addFile(pkgPath importPathString, path string, src []byte, userRequested bool) error {
   201  	for _, p := range b.parsed[pkgPath] {
   202  		if path == p.name {
   203  			klog.V(5).Infof("addFile %s %s already parsed, skipping", pkgPath, path)
   204  			return nil
   205  		}
   206  	}
   207  	klog.V(6).Infof("addFile %s %s", pkgPath, path)
   208  	p, err := parser.ParseFile(b.fset, path, src, parser.DeclarationErrors|parser.ParseComments)
   209  	if err != nil {
   210  		return err
   211  	}
   212  
   213  	// This is redundant with addDir, but some tests call AddFileForTest, which
   214  	// call into here without calling addDir.
   215  	b.userRequested[pkgPath] = userRequested || b.userRequested[pkgPath]
   216  
   217  	b.parsed[pkgPath] = append(b.parsed[pkgPath], parsedFile{path, p})
   218  	for _, c := range p.Comments {
   219  		position := b.fset.Position(c.End())
   220  		b.endLineToCommentGroup[fileLine{position.Filename, position.Line}] = c
   221  	}
   222  
   223  	// We have to get the packages from this specific file, in case the
   224  	// user added individual files instead of entire directories.
   225  	if b.importGraph[pkgPath] == nil {
   226  		b.importGraph[pkgPath] = map[string]struct{}{}
   227  	}
   228  	for _, im := range p.Imports {
   229  		importedPath := strings.Trim(im.Path.Value, `"`)
   230  		b.importGraph[pkgPath][importedPath] = struct{}{}
   231  	}
   232  	return nil
   233  }
   234  
   235  // AddDir adds an entire directory, scanning it for go files. 'dir' should have
   236  // a single go package in it. GOPATH, GOROOT, and the location of your go
   237  // binary (`which go`) will all be searched if dir doesn't literally resolve.
   238  func (b *Builder) AddDir(dir string) error {
   239  	_, err := b.importPackage(dir, true)
   240  	return err
   241  }
   242  
   243  // AddDirRecursive is just like AddDir, but it also recursively adds
   244  // subdirectories; it returns an error only if the path couldn't be resolved;
   245  // any directories recursed into without go source are ignored.
   246  func (b *Builder) AddDirRecursive(dir string) error {
   247  	// Add the root.
   248  	if _, err := b.importPackage(dir, true); err != nil {
   249  		klog.Warningf("Ignoring directory %v: %v", dir, err)
   250  	}
   251  
   252  	// filepath.Walk does not follow symlinks. We therefore evaluate symlinks and use that with
   253  	// filepath.Walk.
   254  	buildPkg, ok := b.getLoadedBuildPackage(dir)
   255  	if !ok {
   256  		return fmt.Errorf("no loaded build package for %s", dir)
   257  	}
   258  	realPath, err := filepath.EvalSymlinks(buildPkg.Dir)
   259  	if err != nil {
   260  		return err
   261  	}
   262  
   263  	fn := func(filePath string, info os.FileInfo, err error) error {
   264  		if info != nil && info.IsDir() {
   265  			rel := filepath.ToSlash(strings.TrimPrefix(filePath, realPath))
   266  			if rel != "" {
   267  				// Make a pkg path.
   268  				buildPkg, ok := b.getLoadedBuildPackage(dir)
   269  				if !ok {
   270  					return fmt.Errorf("no loaded build package for %s", dir)
   271  				}
   272  				pkg := path.Join(string(canonicalizeImportPath(buildPkg.ImportPath)), rel)
   273  
   274  				// Add it.
   275  				if _, err := b.importPackage(pkg, true); err != nil {
   276  					klog.Warningf("Ignoring child directory %v: %v", pkg, err)
   277  				}
   278  			}
   279  		}
   280  		return nil
   281  	}
   282  	if err := filepath.Walk(realPath, fn); err != nil {
   283  		return err
   284  	}
   285  	return nil
   286  }
   287  
   288  // AddDirTo adds an entire directory to a given Universe. Unlike AddDir, this
   289  // processes the package immediately, which makes it safe to use from within a
   290  // generator (rather than just at init time. 'dir' must be a single go package.
   291  // GOPATH, GOROOT, and the location of your go binary (`which go`) will all be
   292  // searched if dir doesn't literally resolve.
   293  // Deprecated. Please use AddDirectoryTo.
   294  func (b *Builder) AddDirTo(dir string, u *types.Universe) error {
   295  	// We want all types from this package, as if they were directly added
   296  	// by the user.  They WERE added by the user, in effect.
   297  	if _, err := b.importPackage(dir, true); err != nil {
   298  		return err
   299  	}
   300  	pkg, ok := b.getLoadedBuildPackage(dir)
   301  	if !ok {
   302  		return fmt.Errorf("no such package: %q", dir)
   303  	}
   304  	return b.findTypesIn(canonicalizeImportPath(pkg.ImportPath), u)
   305  }
   306  
   307  // AddDirectoryTo adds an entire directory to a given Universe. Unlike AddDir,
   308  // this processes the package immediately, which makes it safe to use from
   309  // within a generator (rather than just at init time. 'dir' must be a single go
   310  // package. GOPATH, GOROOT, and the location of your go binary (`which go`)
   311  // will all be searched if dir doesn't literally resolve.
   312  func (b *Builder) AddDirectoryTo(dir string, u *types.Universe) (*types.Package, error) {
   313  	// We want all types from this package, as if they were directly added
   314  	// by the user.  They WERE added by the user, in effect.
   315  	if _, err := b.importPackage(dir, true); err != nil {
   316  		return nil, err
   317  	}
   318  	pkg, ok := b.getLoadedBuildPackage(dir)
   319  	if !ok || pkg == nil {
   320  		return nil, fmt.Errorf("no such package: %q", dir)
   321  	}
   322  	path := canonicalizeImportPath(pkg.ImportPath)
   323  	if err := b.findTypesIn(path, u); err != nil {
   324  		return nil, err
   325  	}
   326  	return u.Package(string(path)), nil
   327  }
   328  
   329  // The implementation of AddDir. A flag indicates whether this directory was
   330  // user-requested or just from following the import graph.
   331  func (b *Builder) addDir(dir string, userRequested bool) error {
   332  	klog.V(5).Infof("addDir %s", dir)
   333  	buildPkg, err := b.importBuildPackage(dir)
   334  	if err != nil {
   335  		return err
   336  	}
   337  	canonicalPackage := canonicalizeImportPath(buildPkg.ImportPath)
   338  	pkgPath := canonicalPackage
   339  	if dir != string(canonicalPackage) {
   340  		klog.V(5).Infof("addDir %s, canonical path is %s", dir, pkgPath)
   341  	}
   342  
   343  	// Sanity check the pkg dir has not changed.
   344  	if prev, found := b.absPaths[pkgPath]; found {
   345  		if buildPkg.Dir != prev {
   346  			return fmt.Errorf("package %q (%s) previously resolved to %s", pkgPath, buildPkg.Dir, prev)
   347  		}
   348  	} else {
   349  		b.absPaths[pkgPath] = buildPkg.Dir
   350  	}
   351  
   352  	files := []string{}
   353  	files = append(files, buildPkg.GoFiles...)
   354  	if b.IncludeTestFiles {
   355  		files = append(files, buildPkg.TestGoFiles...)
   356  	}
   357  
   358  	for _, file := range files {
   359  		if !strings.HasSuffix(file, ".go") {
   360  			continue
   361  		}
   362  		absPath := filepath.Join(buildPkg.Dir, file)
   363  		data, err := ioutil.ReadFile(absPath)
   364  		if err != nil {
   365  			return fmt.Errorf("while loading %q: %v", absPath, err)
   366  		}
   367  		err = b.addFile(pkgPath, absPath, data, userRequested)
   368  		if err != nil {
   369  			return fmt.Errorf("while parsing %q: %v", absPath, err)
   370  		}
   371  	}
   372  	return nil
   373  }
   374  
   375  // regexErrPackageNotFound helps test the expected error for not finding a package.
   376  var regexErrPackageNotFound = regexp.MustCompile(`^unable to import ".*?":.*`)
   377  
   378  func isErrPackageNotFound(err error) bool {
   379  	return regexErrPackageNotFound.MatchString(err.Error())
   380  }
   381  
   382  // importPackage is a function that will be called by the type check package when it
   383  // needs to import a go package. 'path' is the import path.
   384  func (b *Builder) importPackage(dir string, userRequested bool) (*tc.Package, error) {
   385  	klog.V(5).Infof("importPackage %s", dir)
   386  
   387  	var pkgPath = importPathString(dir)
   388  
   389  	// Get the canonical path if we can.
   390  	if buildPkg, _ := b.getLoadedBuildPackage(dir); buildPkg != nil {
   391  		canonicalPackage := canonicalizeImportPath(buildPkg.ImportPath)
   392  		klog.V(5).Infof("importPackage %s, canonical path is %s", dir, canonicalPackage)
   393  		pkgPath = canonicalPackage
   394  	}
   395  
   396  	// If we have not seen this before, process it now.
   397  	ignoreError := false
   398  	if _, found := b.parsed[pkgPath]; !found {
   399  		// Ignore errors in paths that we're importing solely because
   400  		// they're referenced by other packages.
   401  		ignoreError = true
   402  
   403  		// Add it.
   404  		if err := b.addDir(dir, userRequested); err != nil {
   405  			if isErrPackageNotFound(err) {
   406  				klog.V(6).Info(err)
   407  				return nil, nil
   408  			}
   409  
   410  			return nil, err
   411  		}
   412  
   413  		// Get the canonical path now that it has been added.
   414  		if buildPkg, _ := b.getLoadedBuildPackage(dir); buildPkg != nil {
   415  			canonicalPackage := canonicalizeImportPath(buildPkg.ImportPath)
   416  			klog.V(5).Infof("importPackage %s, canonical path is %s", dir, canonicalPackage)
   417  			pkgPath = canonicalPackage
   418  		}
   419  	}
   420  
   421  	// If it was previously known, just check that the user-requestedness hasn't
   422  	// changed.
   423  	b.userRequested[pkgPath] = userRequested || b.userRequested[pkgPath]
   424  
   425  	// Run the type checker.  We may end up doing this to pkgs that are already
   426  	// done, or are in the queue to be done later, but it will short-circuit,
   427  	// and we can't miss pkgs that are only depended on.
   428  	pkg, err := b.typeCheckPackage(pkgPath, !ignoreError)
   429  	if err != nil {
   430  		switch {
   431  		case ignoreError && pkg != nil:
   432  			klog.V(4).Infof("type checking encountered some issues in %q, but ignoring.\n", pkgPath)
   433  		case !ignoreError && pkg != nil:
   434  			klog.V(3).Infof("type checking encountered some errors in %q\n", pkgPath)
   435  			return nil, err
   436  		default:
   437  			return nil, err
   438  		}
   439  	}
   440  
   441  	return pkg, nil
   442  }
   443  
   444  type importAdapter struct {
   445  	b *Builder
   446  }
   447  
   448  func (a importAdapter) Import(path string) (*tc.Package, error) {
   449  	return a.b.importPackage(path, false)
   450  }
   451  
   452  // typeCheckPackage will attempt to return the package even if there are some
   453  // errors, so you may check whether the package is nil or not even if you get
   454  // an error.
   455  func (b *Builder) typeCheckPackage(pkgPath importPathString, logErr bool) (*tc.Package, error) {
   456  	klog.V(5).Infof("typeCheckPackage %s", pkgPath)
   457  	if pkg, ok := b.typeCheckedPackages[pkgPath]; ok {
   458  		if pkg != nil {
   459  			klog.V(6).Infof("typeCheckPackage %s already done", pkgPath)
   460  			return pkg, nil
   461  		}
   462  		// We store a nil right before starting work on a package. So
   463  		// if we get here and it's present and nil, that means there's
   464  		// another invocation of this function on the call stack
   465  		// already processing this package.
   466  		return nil, fmt.Errorf("circular dependency for %q", pkgPath)
   467  	}
   468  	parsedFiles, ok := b.parsed[pkgPath]
   469  	if !ok {
   470  		return nil, fmt.Errorf("No files for pkg %q", pkgPath)
   471  	}
   472  	files := make([]*ast.File, len(parsedFiles))
   473  	for i := range parsedFiles {
   474  		files[i] = parsedFiles[i].file
   475  	}
   476  	b.typeCheckedPackages[pkgPath] = nil
   477  	c := tc.Config{
   478  		IgnoreFuncBodies: true,
   479  		// Note that importAdapter can call b.importPackage which calls this
   480  		// method. So there can't be cycles in the import graph.
   481  		Importer: importAdapter{b},
   482  		Error: func(err error) {
   483  			if logErr {
   484  				klog.V(2).Infof("type checker: %v\n", err)
   485  			} else {
   486  				klog.V(3).Infof("type checker: %v\n", err)
   487  			}
   488  		},
   489  	}
   490  	pkg, err := c.Check(string(pkgPath), b.fset, files, nil)
   491  	b.typeCheckedPackages[pkgPath] = pkg // record the result whether or not there was an error
   492  	return pkg, err
   493  }
   494  
   495  // FindPackages fetches a list of the user-imported packages.
   496  // Note that you need to call b.FindTypes() first.
   497  func (b *Builder) FindPackages() []string {
   498  	// Iterate packages in a predictable order.
   499  	pkgPaths := []string{}
   500  	for k := range b.typeCheckedPackages {
   501  		pkgPaths = append(pkgPaths, string(k))
   502  	}
   503  	sort.Strings(pkgPaths)
   504  
   505  	result := []string{}
   506  	for _, pkgPath := range pkgPaths {
   507  		if b.userRequested[importPathString(pkgPath)] {
   508  			// Since walkType is recursive, all types that are in packages that
   509  			// were directly mentioned will be included.  We don't need to
   510  			// include all types in all transitive packages, though.
   511  			result = append(result, pkgPath)
   512  		}
   513  	}
   514  	return result
   515  }
   516  
   517  // FindTypes finalizes the package imports, and searches through all the
   518  // packages for types.
   519  func (b *Builder) FindTypes() (types.Universe, error) {
   520  	// Take a snapshot of pkgs to iterate, since this will recursively mutate
   521  	// b.parsed. Iterate in a predictable order.
   522  	pkgPaths := []string{}
   523  	for pkgPath := range b.parsed {
   524  		pkgPaths = append(pkgPaths, string(pkgPath))
   525  	}
   526  	sort.Strings(pkgPaths)
   527  
   528  	u := types.Universe{}
   529  	for _, pkgPath := range pkgPaths {
   530  		if err := b.findTypesIn(importPathString(pkgPath), &u); err != nil {
   531  			return nil, err
   532  		}
   533  	}
   534  	return u, nil
   535  }
   536  
   537  // addCommentsToType takes any accumulated comment lines prior to obj and
   538  // attaches them to the type t.
   539  func (b *Builder) addCommentsToType(obj tc.Object, t *types.Type) {
   540  	c1 := b.priorCommentLines(obj.Pos(), 1)
   541  	// c1.Text() is safe if c1 is nil
   542  	t.CommentLines = splitLines(c1.Text())
   543  	if c1 == nil {
   544  		t.SecondClosestCommentLines = splitLines(b.priorCommentLines(obj.Pos(), 2).Text())
   545  	} else {
   546  		t.SecondClosestCommentLines = splitLines(b.priorCommentLines(c1.List[0].Slash, 2).Text())
   547  	}
   548  }
   549  
   550  // findTypesIn finalizes the package import and searches through the package
   551  // for types.
   552  func (b *Builder) findTypesIn(pkgPath importPathString, u *types.Universe) error {
   553  	klog.V(5).Infof("findTypesIn %s", pkgPath)
   554  	pkg := b.typeCheckedPackages[pkgPath]
   555  	if pkg == nil {
   556  		return fmt.Errorf("findTypesIn(%s): package is not known", pkgPath)
   557  	}
   558  	if !b.userRequested[pkgPath] {
   559  		// Since walkType is recursive, all types that the
   560  		// packages they asked for depend on will be included.
   561  		// But we don't need to include all types in all
   562  		// *packages* they depend on.
   563  		klog.V(5).Infof("findTypesIn %s: package is not user requested", pkgPath)
   564  		return nil
   565  	}
   566  
   567  	// We're keeping this package.  This call will create the record.
   568  	u.Package(string(pkgPath)).Name = pkg.Name()
   569  	u.Package(string(pkgPath)).Path = pkg.Path()
   570  	u.Package(string(pkgPath)).SourcePath = b.absPaths[pkgPath]
   571  
   572  	for _, f := range b.parsed[pkgPath] {
   573  		if _, fileName := filepath.Split(f.name); fileName == "doc.go" {
   574  			tp := u.Package(string(pkgPath))
   575  			// findTypesIn might be called multiple times. Clean up tp.Comments
   576  			// to avoid repeatedly fill same comments to it.
   577  			tp.Comments = []string{}
   578  			for i := range f.file.Comments {
   579  				tp.Comments = append(tp.Comments, splitLines(f.file.Comments[i].Text())...)
   580  			}
   581  			if f.file.Doc != nil {
   582  				tp.DocComments = splitLines(f.file.Doc.Text())
   583  			}
   584  		}
   585  	}
   586  
   587  	s := pkg.Scope()
   588  	for _, n := range s.Names() {
   589  		obj := s.Lookup(n)
   590  		tn, ok := obj.(*tc.TypeName)
   591  		if ok {
   592  			t := b.walkType(*u, nil, tn.Type())
   593  			b.addCommentsToType(obj, t)
   594  		}
   595  		tf, ok := obj.(*tc.Func)
   596  		// We only care about functions, not concrete/abstract methods.
   597  		if ok && tf.Type() != nil && tf.Type().(*tc.Signature).Recv() == nil {
   598  			t := b.addFunction(*u, nil, tf)
   599  			b.addCommentsToType(obj, t)
   600  		}
   601  		tv, ok := obj.(*tc.Var)
   602  		if ok && !tv.IsField() {
   603  			t := b.addVariable(*u, nil, tv)
   604  			b.addCommentsToType(obj, t)
   605  		}
   606  		tconst, ok := obj.(*tc.Const)
   607  		if ok {
   608  			t := b.addConstant(*u, nil, tconst)
   609  			b.addCommentsToType(obj, t)
   610  		}
   611  	}
   612  
   613  	importedPkgs := []string{}
   614  	for k := range b.importGraph[pkgPath] {
   615  		importedPkgs = append(importedPkgs, string(k))
   616  	}
   617  	sort.Strings(importedPkgs)
   618  	for _, p := range importedPkgs {
   619  		u.AddImports(string(pkgPath), p)
   620  	}
   621  	return nil
   622  }
   623  
   624  func (b *Builder) importWithMode(dir string, mode build.ImportMode) (*build.Package, error) {
   625  	// This is a bit of a hack.  The srcDir argument to Import() should
   626  	// properly be the dir of the file which depends on the package to be
   627  	// imported, so that vendoring can work properly and local paths can
   628  	// resolve.  We assume that there is only one level of vendoring, and that
   629  	// the CWD is inside the GOPATH, so this should be safe. Nobody should be
   630  	// using local (relative) paths except on the CLI, so CWD is also
   631  	// sufficient.
   632  	cwd, err := os.Getwd()
   633  	if err != nil {
   634  		return nil, fmt.Errorf("unable to get current directory: %v", err)
   635  	}
   636  
   637  	// normalize to drop /vendor/ if present
   638  	dir = string(canonicalizeImportPath(dir))
   639  
   640  	buildPkg, err := b.context.Import(filepath.ToSlash(dir), cwd, mode)
   641  	if err != nil {
   642  		return nil, err
   643  	}
   644  	return buildPkg, nil
   645  }
   646  
   647  // if there's a comment on the line `lines` before pos, return its text, otherwise "".
   648  func (b *Builder) priorCommentLines(pos token.Pos, lines int) *ast.CommentGroup {
   649  	position := b.fset.Position(pos)
   650  	key := fileLine{position.Filename, position.Line - lines}
   651  	return b.endLineToCommentGroup[key]
   652  }
   653  
   654  func splitLines(str string) []string {
   655  	return strings.Split(strings.TrimRight(str, "\n"), "\n")
   656  }
   657  
   658  func tcFuncNameToName(in string) types.Name {
   659  	name := strings.TrimPrefix(in, "func ")
   660  	nameParts := strings.Split(name, "(")
   661  	return tcNameToName(nameParts[0])
   662  }
   663  
   664  func tcVarNameToName(in string) types.Name {
   665  	nameParts := strings.Split(in, " ")
   666  	// nameParts[0] is "var".
   667  	// nameParts[2:] is the type of the variable, we ignore it for now.
   668  	return tcNameToName(nameParts[1])
   669  }
   670  
   671  func tcNameToName(in string) types.Name {
   672  	// Detect anonymous type names. (These may have '.' characters because
   673  	// embedded types may have packages, so we detect them specially.)
   674  	if strings.HasPrefix(in, "struct{") ||
   675  		strings.HasPrefix(in, "<-chan") ||
   676  		strings.HasPrefix(in, "chan<-") ||
   677  		strings.HasPrefix(in, "chan ") ||
   678  		strings.HasPrefix(in, "func(") ||
   679  		strings.HasPrefix(in, "func (") ||
   680  		strings.HasPrefix(in, "*") ||
   681  		strings.HasPrefix(in, "map[") ||
   682  		strings.HasPrefix(in, "[") {
   683  		return types.Name{Name: in}
   684  	}
   685  
   686  	// Otherwise, if there are '.' characters present, the name has a
   687  	// package path in front.
   688  	nameParts := strings.Split(in, ".")
   689  	name := types.Name{Name: in}
   690  	if n := len(nameParts); n >= 2 {
   691  		// The final "." is the name of the type--previous ones must
   692  		// have been in the package path.
   693  		name.Package, name.Name = strings.Join(nameParts[:n-1], "."), nameParts[n-1]
   694  	}
   695  	return name
   696  }
   697  
   698  func (b *Builder) convertSignature(u types.Universe, t *tc.Signature) *types.Signature {
   699  	signature := &types.Signature{}
   700  	for i := 0; i < t.Params().Len(); i++ {
   701  		signature.Parameters = append(signature.Parameters, b.walkType(u, nil, t.Params().At(i).Type()))
   702  		signature.ParameterNames = append(signature.ParameterNames, t.Params().At(i).Name())
   703  	}
   704  	for i := 0; i < t.Results().Len(); i++ {
   705  		signature.Results = append(signature.Results, b.walkType(u, nil, t.Results().At(i).Type()))
   706  		signature.ResultNames = append(signature.ResultNames, t.Results().At(i).Name())
   707  	}
   708  	if r := t.Recv(); r != nil {
   709  		signature.Receiver = b.walkType(u, nil, r.Type())
   710  	}
   711  	signature.Variadic = t.Variadic()
   712  	return signature
   713  }
   714  
   715  // walkType adds the type, and any necessary child types.
   716  func (b *Builder) walkType(u types.Universe, useName *types.Name, in tc.Type) *types.Type {
   717  	// Most of the cases are underlying types of the named type.
   718  	name := tcNameToName(in.String())
   719  	if useName != nil {
   720  		name = *useName
   721  	}
   722  
   723  	switch t := in.(type) {
   724  	case *tc.Struct:
   725  		out := u.Type(name)
   726  		if out.Kind != types.Unknown {
   727  			return out
   728  		}
   729  		out.Kind = types.Struct
   730  		for i := 0; i < t.NumFields(); i++ {
   731  			f := t.Field(i)
   732  			m := types.Member{
   733  				Name:         f.Name(),
   734  				Embedded:     f.Anonymous(),
   735  				Tags:         t.Tag(i),
   736  				Type:         b.walkType(u, nil, f.Type()),
   737  				CommentLines: splitLines(b.priorCommentLines(f.Pos(), 1).Text()),
   738  			}
   739  			out.Members = append(out.Members, m)
   740  		}
   741  		return out
   742  	case *tc.Map:
   743  		out := u.Type(name)
   744  		if out.Kind != types.Unknown {
   745  			return out
   746  		}
   747  		out.Kind = types.Map
   748  		out.Elem = b.walkType(u, nil, t.Elem())
   749  		out.Key = b.walkType(u, nil, t.Key())
   750  		return out
   751  	case *tc.Pointer:
   752  		out := u.Type(name)
   753  		if out.Kind != types.Unknown {
   754  			return out
   755  		}
   756  		out.Kind = types.Pointer
   757  		out.Elem = b.walkType(u, nil, t.Elem())
   758  		return out
   759  	case *tc.Slice:
   760  		out := u.Type(name)
   761  		if out.Kind != types.Unknown {
   762  			return out
   763  		}
   764  		out.Kind = types.Slice
   765  		out.Elem = b.walkType(u, nil, t.Elem())
   766  		return out
   767  	case *tc.Array:
   768  		out := u.Type(name)
   769  		if out.Kind != types.Unknown {
   770  			return out
   771  		}
   772  		out.Kind = types.Array
   773  		out.Elem = b.walkType(u, nil, t.Elem())
   774  		out.Len = in.(*tc.Array).Len()
   775  		return out
   776  	case *tc.Chan:
   777  		out := u.Type(name)
   778  		if out.Kind != types.Unknown {
   779  			return out
   780  		}
   781  		out.Kind = types.Chan
   782  		out.Elem = b.walkType(u, nil, t.Elem())
   783  		// TODO: need to store direction, otherwise raw type name
   784  		// cannot be properly written.
   785  		return out
   786  	case *tc.Basic:
   787  		out := u.Type(types.Name{
   788  			Package: "",
   789  			Name:    t.Name(),
   790  		})
   791  		// modify basic types
   792  		out = tptypes.ModifyType(u, t.Name(), out)
   793  		if out.Kind != types.Unknown {
   794  			return out
   795  		}
   796  		out.Kind = types.Unsupported
   797  		return out
   798  	case *tc.Signature:
   799  		out := u.Type(name)
   800  		if out.Kind != types.Unknown {
   801  			return out
   802  		}
   803  		out.Kind = types.Func
   804  		out.Signature = b.convertSignature(u, t)
   805  		return out
   806  	case *tc.Interface:
   807  		out := u.Type(name)
   808  		if out.Kind != types.Unknown {
   809  			return out
   810  		}
   811  		out.Kind = types.Interface
   812  		t.Complete()
   813  		for i := 0; i < t.NumMethods(); i++ {
   814  			if out.Methods == nil {
   815  				out.Methods = map[string]*types.Type{}
   816  			}
   817  			method := t.Method(i)
   818  			name := tcNameToName(method.String())
   819  			mt := b.walkType(u, &name, method.Type())
   820  			mt.CommentLines = splitLines(b.priorCommentLines(method.Pos(), 1).Text())
   821  			out.Methods[method.Name()] = mt
   822  		}
   823  		return out
   824  	case *tc.Named:
   825  		var out *types.Type
   826  		switch t.Underlying().(type) {
   827  		case *tc.Named, *tc.Basic, *tc.Map, *tc.Slice:
   828  			name := tcNameToName(t.String())
   829  			out = u.Type(name)
   830  			if out.Kind != types.Unknown {
   831  				return out
   832  			}
   833  			out.Kind = types.Alias
   834  			out.Underlying = b.walkType(u, nil, t.Underlying())
   835  		default:
   836  			// tc package makes everything "named" with an
   837  			// underlying anonymous type--we remove that annoying
   838  			// "feature" for users. This flattens those types
   839  			// together.
   840  			name := tcNameToName(t.String())
   841  			if out := u.Type(name); out.Kind != types.Unknown {
   842  				return out // short circuit if we've already made this.
   843  			}
   844  			out = b.walkType(u, &name, t.Underlying())
   845  		}
   846  		// If the underlying type didn't already add methods, add them.
   847  		// (Interface types will have already added methods.)
   848  		if len(out.Methods) == 0 {
   849  			for i := 0; i < t.NumMethods(); i++ {
   850  				if out.Methods == nil {
   851  					out.Methods = map[string]*types.Type{}
   852  				}
   853  				method := t.Method(i)
   854  				name := tcNameToName(method.String())
   855  				mt := b.walkType(u, &name, method.Type())
   856  				mt.CommentLines = splitLines(b.priorCommentLines(method.Pos(), 1).Text())
   857  				out.Methods[method.Name()] = mt
   858  			}
   859  		}
   860  		return out
   861  	default:
   862  		out := u.Type(name)
   863  		if out.Kind != types.Unknown {
   864  			return out
   865  		}
   866  		out.Kind = types.Unsupported
   867  		klog.Warningf("Making unsupported type entry %q for: %#v\n", out, t)
   868  		return out
   869  	}
   870  }
   871  
   872  func (b *Builder) addFunction(u types.Universe, useName *types.Name, in *tc.Func) *types.Type {
   873  	name := tcFuncNameToName(in.String())
   874  	if useName != nil {
   875  		name = *useName
   876  	}
   877  	out := u.Function(name)
   878  	out.Kind = types.DeclarationOf
   879  	out.Underlying = b.walkType(u, nil, in.Type())
   880  	return out
   881  }
   882  
   883  func (b *Builder) addVariable(u types.Universe, useName *types.Name, in *tc.Var) *types.Type {
   884  	name := tcVarNameToName(in.String())
   885  	if useName != nil {
   886  		name = *useName
   887  	}
   888  	out := u.Variable(name)
   889  	out.Kind = types.DeclarationOf
   890  	out.Underlying = b.walkType(u, nil, in.Type())
   891  	return out
   892  }
   893  
   894  func (b *Builder) addConstant(u types.Universe, useName *types.Name, in *tc.Const) *types.Type {
   895  	name := tcVarNameToName(in.String())
   896  	if useName != nil {
   897  		name = *useName
   898  	}
   899  	out := u.Constant(name)
   900  	out.Kind = types.DeclarationOf
   901  	out.Underlying = b.walkType(u, nil, in.Type())
   902  
   903  	var constval string
   904  
   905  	// For strings, we use `StringVal()` to get the un-truncated,
   906  	// un-quoted string. For other values, `.String()` is preferable to
   907  	// get something relatively human readable (especially since for
   908  	// floating point types, `ExactString()` will generate numeric
   909  	// expressions using `big.(*Float).Text()`.
   910  	switch in.Val().Kind() {
   911  	case constant.String:
   912  		constval = constant.StringVal(in.Val())
   913  	default:
   914  		constval = in.Val().String()
   915  	}
   916  
   917  	out.ConstValue = &constval
   918  	return out
   919  }
   920  
   921  // canonicalizeImportPath takes an import path and returns the actual package.
   922  // It doesn't support nested vendoring.
   923  func canonicalizeImportPath(importPath string) importPathString {
   924  	if !strings.Contains(importPath, "/vendor/") {
   925  		return importPathString(importPath)
   926  	}
   927  
   928  	return importPathString(importPath[strings.Index(importPath, "/vendor/")+len("/vendor/"):])
   929  }