gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/golang.org/x/tools/go/packages/golist_overlay.go (about)

     1  package packages
     2  
     3  import (
     4  	"go/parser"
     5  	"go/token"
     6  	"path/filepath"
     7  	"strconv"
     8  	"strings"
     9  )
    10  
    11  // processGolistOverlay provides rudimentary support for adding
    12  // files that don't exist on disk to an overlay. The results can be
    13  // sometimes incorrect.
    14  // TODO(matloob): Handle unsupported cases, including the following:
    15  // - test files
    16  // - adding test and non-test files to test variants of packages
    17  // - determining the correct package to add given a new import path
    18  // - creating packages that don't exist
    19  func processGolistOverlay(cfg *Config, response *driverResponse) (modifiedPkgs, needPkgs []string, err error) {
    20  	havePkgs := make(map[string]string) // importPath -> non-test package ID
    21  	needPkgsSet := make(map[string]bool)
    22  	modifiedPkgsSet := make(map[string]bool)
    23  
    24  	for _, pkg := range response.Packages {
    25  		// This is an approximation of import path to id. This can be
    26  		// wrong for tests, vendored packages, and a number of other cases.
    27  		havePkgs[pkg.PkgPath] = pkg.ID
    28  	}
    29  
    30  outer:
    31  	for path, contents := range cfg.Overlay {
    32  		base := filepath.Base(path)
    33  		if strings.HasSuffix(path, "_test.go") {
    34  			// Overlays don't support adding new test files yet.
    35  			// TODO(matloob): support adding new test files.
    36  			continue
    37  		}
    38  		dir := filepath.Dir(path)
    39  		for _, pkg := range response.Packages {
    40  			var dirContains, fileExists bool
    41  			for _, f := range pkg.GoFiles {
    42  				if sameFile(filepath.Dir(f), dir) {
    43  					dirContains = true
    44  				}
    45  				if filepath.Base(f) == base {
    46  					fileExists = true
    47  				}
    48  			}
    49  			if dirContains {
    50  				if !fileExists {
    51  					pkg.GoFiles = append(pkg.GoFiles, path) // TODO(matloob): should the file just be added to GoFiles?
    52  					pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, path)
    53  					modifiedPkgsSet[pkg.ID] = true
    54  				}
    55  				imports, err := extractImports(path, contents)
    56  				if err != nil {
    57  					// Let the parser or type checker report errors later.
    58  					continue outer
    59  				}
    60  				for _, imp := range imports {
    61  					_, found := pkg.Imports[imp]
    62  					if !found {
    63  						needPkgsSet[imp] = true
    64  						// TODO(matloob): Handle cases when the following block isn't correct.
    65  						// These include imports of test variants, imports of vendored packages, etc.
    66  						id, ok := havePkgs[imp]
    67  						if !ok {
    68  							id = imp
    69  						}
    70  						pkg.Imports[imp] = &Package{ID: id}
    71  					}
    72  				}
    73  				continue outer
    74  			}
    75  		}
    76  	}
    77  
    78  	needPkgs = make([]string, 0, len(needPkgsSet))
    79  	for pkg := range needPkgsSet {
    80  		needPkgs = append(needPkgs, pkg)
    81  	}
    82  	modifiedPkgs = make([]string, 0, len(modifiedPkgsSet))
    83  	for pkg := range modifiedPkgsSet {
    84  		modifiedPkgs = append(modifiedPkgs, pkg)
    85  	}
    86  	return modifiedPkgs, needPkgs, err
    87  }
    88  
    89  func extractImports(filename string, contents []byte) ([]string, error) {
    90  	f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.ImportsOnly) // TODO(matloob): reuse fileset?
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	var res []string
    95  	for _, imp := range f.Imports {
    96  		quotedPath := imp.Path.Value
    97  		path, err := strconv.Unquote(quotedPath)
    98  		if err != nil {
    99  			return nil, err
   100  		}
   101  		res = append(res, path)
   102  	}
   103  	return res, nil
   104  }