mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-28 15:50:12 +00:00
TUN-4961: Update quic-go to latest
- Updates fips-go to be the latest on cfsetup.yaml - Updates sumtype's x/tools to be latest to avoid Internal: nil pkg errors with fips.
This commit is contained in:
1730
vendor/golang.org/x/tools/internal/imports/fix.go
generated
vendored
Normal file
1730
vendor/golang.org/x/tools/internal/imports/fix.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
346
vendor/golang.org/x/tools/internal/imports/imports.go
generated
vendored
Normal file
346
vendor/golang.org/x/tools/internal/imports/imports.go
generated
vendored
Normal file
@@ -0,0 +1,346 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run mkstdlib.go
|
||||
|
||||
// Package imports implements a Go pretty-printer (like package "go/format")
|
||||
// that also adds or removes import statements as necessary.
|
||||
package imports
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
// Options is golang.org/x/tools/imports.Options with extra internal-only options.
|
||||
type Options struct {
|
||||
Env *ProcessEnv // The environment to use. Note: this contains the cached module and filesystem state.
|
||||
|
||||
// LocalPrefix is a comma-separated string of import path prefixes, which, if
|
||||
// set, instructs Process to sort the import paths with the given prefixes
|
||||
// into another group after 3rd-party packages.
|
||||
LocalPrefix string
|
||||
|
||||
Fragment bool // Accept fragment of a source file (no package statement)
|
||||
AllErrors bool // Report all errors (not just the first 10 on different lines)
|
||||
|
||||
Comments bool // Print comments (true if nil *Options provided)
|
||||
TabIndent bool // Use tabs for indent (true if nil *Options provided)
|
||||
TabWidth int // Tab width (8 if nil *Options provided)
|
||||
|
||||
FormatOnly bool // Disable the insertion and deletion of imports
|
||||
}
|
||||
|
||||
// Process implements golang.org/x/tools/imports.Process with explicit context in opt.Env.
|
||||
func Process(filename string, src []byte, opt *Options) (formatted []byte, err error) {
|
||||
fileSet := token.NewFileSet()
|
||||
file, adjust, err := parse(fileSet, filename, src, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !opt.FormatOnly {
|
||||
if err := fixImports(fileSet, file, filename, opt.Env); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return formatFile(fileSet, file, src, adjust, opt)
|
||||
}
|
||||
|
||||
// FixImports returns a list of fixes to the imports that, when applied,
|
||||
// will leave the imports in the same state as Process. src and opt must
|
||||
// be specified.
|
||||
//
|
||||
// Note that filename's directory influences which imports can be chosen,
|
||||
// so it is important that filename be accurate.
|
||||
func FixImports(filename string, src []byte, opt *Options) (fixes []*ImportFix, err error) {
|
||||
fileSet := token.NewFileSet()
|
||||
file, _, err := parse(fileSet, filename, src, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return getFixes(fileSet, file, filename, opt.Env)
|
||||
}
|
||||
|
||||
// ApplyFixes applies all of the fixes to the file and formats it. extraMode
|
||||
// is added in when parsing the file. src and opts must be specified, but no
|
||||
// env is needed.
|
||||
func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options, extraMode parser.Mode) (formatted []byte, err error) {
|
||||
// Don't use parse() -- we don't care about fragments or statement lists
|
||||
// here, and we need to work with unparseable files.
|
||||
fileSet := token.NewFileSet()
|
||||
parserMode := parser.Mode(0)
|
||||
if opt.Comments {
|
||||
parserMode |= parser.ParseComments
|
||||
}
|
||||
if opt.AllErrors {
|
||||
parserMode |= parser.AllErrors
|
||||
}
|
||||
parserMode |= extraMode
|
||||
|
||||
file, err := parser.ParseFile(fileSet, filename, src, parserMode)
|
||||
if file == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Apply the fixes to the file.
|
||||
apply(fileSet, file, fixes)
|
||||
|
||||
return formatFile(fileSet, file, src, nil, opt)
|
||||
}
|
||||
|
||||
func formatFile(fileSet *token.FileSet, file *ast.File, src []byte, adjust func(orig []byte, src []byte) []byte, opt *Options) ([]byte, error) {
|
||||
mergeImports(fileSet, file)
|
||||
sortImports(opt.LocalPrefix, fileSet, file)
|
||||
imps := astutil.Imports(fileSet, file)
|
||||
var spacesBefore []string // import paths we need spaces before
|
||||
for _, impSection := range imps {
|
||||
// Within each block of contiguous imports, see if any
|
||||
// import lines are in different group numbers. If so,
|
||||
// we'll need to put a space between them so it's
|
||||
// compatible with gofmt.
|
||||
lastGroup := -1
|
||||
for _, importSpec := range impSection {
|
||||
importPath, _ := strconv.Unquote(importSpec.Path.Value)
|
||||
groupNum := importGroup(opt.LocalPrefix, importPath)
|
||||
if groupNum != lastGroup && lastGroup != -1 {
|
||||
spacesBefore = append(spacesBefore, importPath)
|
||||
}
|
||||
lastGroup = groupNum
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
printerMode := printer.UseSpaces
|
||||
if opt.TabIndent {
|
||||
printerMode |= printer.TabIndent
|
||||
}
|
||||
printConfig := &printer.Config{Mode: printerMode, Tabwidth: opt.TabWidth}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err := printConfig.Fprint(&buf, fileSet, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := buf.Bytes()
|
||||
if adjust != nil {
|
||||
out = adjust(src, out)
|
||||
}
|
||||
if len(spacesBefore) > 0 {
|
||||
out, err = addImportSpaces(bytes.NewReader(out), spacesBefore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
out, err = format.Source(out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// parse parses src, which was read from filename,
|
||||
// as a Go source file or statement list.
|
||||
func parse(fset *token.FileSet, filename string, src []byte, opt *Options) (*ast.File, func(orig, src []byte) []byte, error) {
|
||||
parserMode := parser.Mode(0)
|
||||
if opt.Comments {
|
||||
parserMode |= parser.ParseComments
|
||||
}
|
||||
if opt.AllErrors {
|
||||
parserMode |= parser.AllErrors
|
||||
}
|
||||
|
||||
// Try as whole source file.
|
||||
file, err := parser.ParseFile(fset, filename, src, parserMode)
|
||||
if err == nil {
|
||||
return file, nil, nil
|
||||
}
|
||||
// If the error is that the source file didn't begin with a
|
||||
// package line and we accept fragmented input, fall through to
|
||||
// try as a source fragment. Stop and return on any other error.
|
||||
if !opt.Fragment || !strings.Contains(err.Error(), "expected 'package'") {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// If this is a declaration list, make it a source file
|
||||
// by inserting a package clause.
|
||||
// Insert using a ;, not a newline, so that parse errors are on
|
||||
// the correct line.
|
||||
const prefix = "package main;"
|
||||
psrc := append([]byte(prefix), src...)
|
||||
file, err = parser.ParseFile(fset, filename, psrc, parserMode)
|
||||
if err == nil {
|
||||
// Gofmt will turn the ; into a \n.
|
||||
// Do that ourselves now and update the file contents,
|
||||
// so that positions and line numbers are correct going forward.
|
||||
psrc[len(prefix)-1] = '\n'
|
||||
fset.File(file.Package).SetLinesForContent(psrc)
|
||||
|
||||
// If a main function exists, we will assume this is a main
|
||||
// package and leave the file.
|
||||
if containsMainFunc(file) {
|
||||
return file, nil, nil
|
||||
}
|
||||
|
||||
adjust := func(orig, src []byte) []byte {
|
||||
// Remove the package clause.
|
||||
src = src[len(prefix):]
|
||||
return matchSpace(orig, src)
|
||||
}
|
||||
return file, adjust, nil
|
||||
}
|
||||
// If the error is that the source file didn't begin with a
|
||||
// declaration, fall through to try as a statement list.
|
||||
// Stop and return on any other error.
|
||||
if !strings.Contains(err.Error(), "expected declaration") {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// If this is a statement list, make it a source file
|
||||
// by inserting a package clause and turning the list
|
||||
// into a function body. This handles expressions too.
|
||||
// Insert using a ;, not a newline, so that the line numbers
|
||||
// in fsrc match the ones in src.
|
||||
fsrc := append(append([]byte("package p; func _() {"), src...), '}')
|
||||
file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
|
||||
if err == nil {
|
||||
adjust := func(orig, src []byte) []byte {
|
||||
// Remove the wrapping.
|
||||
// Gofmt has turned the ; into a \n\n.
|
||||
src = src[len("package p\n\nfunc _() {"):]
|
||||
src = src[:len(src)-len("}\n")]
|
||||
// Gofmt has also indented the function body one level.
|
||||
// Remove that indent.
|
||||
src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1)
|
||||
return matchSpace(orig, src)
|
||||
}
|
||||
return file, adjust, nil
|
||||
}
|
||||
|
||||
// Failed, and out of options.
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// containsMainFunc checks if a file contains a function declaration with the
|
||||
// function signature 'func main()'
|
||||
func containsMainFunc(file *ast.File) bool {
|
||||
for _, decl := range file.Decls {
|
||||
if f, ok := decl.(*ast.FuncDecl); ok {
|
||||
if f.Name.Name != "main" {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(f.Type.Params.List) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if f.Type.Results != nil && len(f.Type.Results.List) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func cutSpace(b []byte) (before, middle, after []byte) {
|
||||
i := 0
|
||||
for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') {
|
||||
i++
|
||||
}
|
||||
j := len(b)
|
||||
for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') {
|
||||
j--
|
||||
}
|
||||
if i <= j {
|
||||
return b[:i], b[i:j], b[j:]
|
||||
}
|
||||
return nil, nil, b[j:]
|
||||
}
|
||||
|
||||
// matchSpace reformats src to use the same space context as orig.
|
||||
// 1) If orig begins with blank lines, matchSpace inserts them at the beginning of src.
|
||||
// 2) matchSpace copies the indentation of the first non-blank line in orig
|
||||
// to every non-blank line in src.
|
||||
// 3) matchSpace copies the trailing space from orig and uses it in place
|
||||
// of src's trailing space.
|
||||
func matchSpace(orig []byte, src []byte) []byte {
|
||||
before, _, after := cutSpace(orig)
|
||||
i := bytes.LastIndex(before, []byte{'\n'})
|
||||
before, indent := before[:i+1], before[i+1:]
|
||||
|
||||
_, src, _ = cutSpace(src)
|
||||
|
||||
var b bytes.Buffer
|
||||
b.Write(before)
|
||||
for len(src) > 0 {
|
||||
line := src
|
||||
if i := bytes.IndexByte(line, '\n'); i >= 0 {
|
||||
line, src = line[:i+1], line[i+1:]
|
||||
} else {
|
||||
src = nil
|
||||
}
|
||||
if len(line) > 0 && line[0] != '\n' { // not blank
|
||||
b.Write(indent)
|
||||
}
|
||||
b.Write(line)
|
||||
}
|
||||
b.Write(after)
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
var impLine = regexp.MustCompile(`^\s+(?:[\w\.]+\s+)?"(.+)"`)
|
||||
|
||||
func addImportSpaces(r io.Reader, breaks []string) ([]byte, error) {
|
||||
var out bytes.Buffer
|
||||
in := bufio.NewReader(r)
|
||||
inImports := false
|
||||
done := false
|
||||
for {
|
||||
s, err := in.ReadString('\n')
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !inImports && !done && strings.HasPrefix(s, "import") {
|
||||
inImports = true
|
||||
}
|
||||
if inImports && (strings.HasPrefix(s, "var") ||
|
||||
strings.HasPrefix(s, "func") ||
|
||||
strings.HasPrefix(s, "const") ||
|
||||
strings.HasPrefix(s, "type")) {
|
||||
done = true
|
||||
inImports = false
|
||||
}
|
||||
if inImports && len(breaks) > 0 {
|
||||
if m := impLine.FindStringSubmatch(s); m != nil {
|
||||
if m[1] == breaks[0] {
|
||||
out.WriteByte('\n')
|
||||
breaks = breaks[1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprint(&out, s)
|
||||
}
|
||||
return out.Bytes(), nil
|
||||
}
|
695
vendor/golang.org/x/tools/internal/imports/mod.go
generated
vendored
Normal file
695
vendor/golang.org/x/tools/internal/imports/mod.go
generated
vendored
Normal file
@@ -0,0 +1,695 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package imports
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/tools/internal/gocommand"
|
||||
"golang.org/x/tools/internal/gopathwalk"
|
||||
)
|
||||
|
||||
// ModuleResolver implements resolver for modules using the go command as little
|
||||
// as feasible.
|
||||
type ModuleResolver struct {
|
||||
env *ProcessEnv
|
||||
moduleCacheDir string
|
||||
dummyVendorMod *gocommand.ModuleJSON // If vendoring is enabled, the pseudo-module that represents the /vendor directory.
|
||||
roots []gopathwalk.Root
|
||||
scanSema chan struct{} // scanSema prevents concurrent scans and guards scannedRoots.
|
||||
scannedRoots map[gopathwalk.Root]bool
|
||||
|
||||
initialized bool
|
||||
main *gocommand.ModuleJSON
|
||||
modsByModPath []*gocommand.ModuleJSON // All modules, ordered by # of path components in module Path...
|
||||
modsByDir []*gocommand.ModuleJSON // ...or Dir.
|
||||
|
||||
// moduleCacheCache stores information about the module cache.
|
||||
moduleCacheCache *dirInfoCache
|
||||
otherCache *dirInfoCache
|
||||
}
|
||||
|
||||
func newModuleResolver(e *ProcessEnv) *ModuleResolver {
|
||||
r := &ModuleResolver{
|
||||
env: e,
|
||||
scanSema: make(chan struct{}, 1),
|
||||
}
|
||||
r.scanSema <- struct{}{}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) init() error {
|
||||
if r.initialized {
|
||||
return nil
|
||||
}
|
||||
|
||||
goenv, err := r.env.goEnv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
inv := gocommand.Invocation{
|
||||
BuildFlags: r.env.BuildFlags,
|
||||
ModFlag: r.env.ModFlag,
|
||||
ModFile: r.env.ModFile,
|
||||
Env: r.env.env(),
|
||||
Logf: r.env.Logf,
|
||||
WorkingDir: r.env.WorkingDir,
|
||||
}
|
||||
mainMod, vendorEnabled, err := gocommand.VendorEnabled(context.TODO(), inv, r.env.GocmdRunner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mainMod != nil && vendorEnabled {
|
||||
// Vendor mode is on, so all the non-Main modules are irrelevant,
|
||||
// and we need to search /vendor for everything.
|
||||
r.main = mainMod
|
||||
r.dummyVendorMod = &gocommand.ModuleJSON{
|
||||
Path: "",
|
||||
Dir: filepath.Join(mainMod.Dir, "vendor"),
|
||||
}
|
||||
r.modsByModPath = []*gocommand.ModuleJSON{mainMod, r.dummyVendorMod}
|
||||
r.modsByDir = []*gocommand.ModuleJSON{mainMod, r.dummyVendorMod}
|
||||
} else {
|
||||
// Vendor mode is off, so run go list -m ... to find everything.
|
||||
err := r.initAllMods()
|
||||
// We expect an error when running outside of a module with
|
||||
// GO111MODULE=on. Other errors are fatal.
|
||||
if err != nil {
|
||||
if errMsg := err.Error(); !strings.Contains(errMsg, "working directory is not part of a module") && !strings.Contains(errMsg, "go.mod file not found") {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if gmc := r.env.Env["GOMODCACHE"]; gmc != "" {
|
||||
r.moduleCacheDir = gmc
|
||||
} else {
|
||||
gopaths := filepath.SplitList(goenv["GOPATH"])
|
||||
if len(gopaths) == 0 {
|
||||
return fmt.Errorf("empty GOPATH")
|
||||
}
|
||||
r.moduleCacheDir = filepath.Join(gopaths[0], "/pkg/mod")
|
||||
}
|
||||
|
||||
sort.Slice(r.modsByModPath, func(i, j int) bool {
|
||||
count := func(x int) int {
|
||||
return strings.Count(r.modsByModPath[x].Path, "/")
|
||||
}
|
||||
return count(j) < count(i) // descending order
|
||||
})
|
||||
sort.Slice(r.modsByDir, func(i, j int) bool {
|
||||
count := func(x int) int {
|
||||
return strings.Count(r.modsByDir[x].Dir, "/")
|
||||
}
|
||||
return count(j) < count(i) // descending order
|
||||
})
|
||||
|
||||
r.roots = []gopathwalk.Root{
|
||||
{filepath.Join(goenv["GOROOT"], "/src"), gopathwalk.RootGOROOT},
|
||||
}
|
||||
if r.main != nil {
|
||||
r.roots = append(r.roots, gopathwalk.Root{r.main.Dir, gopathwalk.RootCurrentModule})
|
||||
}
|
||||
if vendorEnabled {
|
||||
r.roots = append(r.roots, gopathwalk.Root{r.dummyVendorMod.Dir, gopathwalk.RootOther})
|
||||
} else {
|
||||
addDep := func(mod *gocommand.ModuleJSON) {
|
||||
if mod.Replace == nil {
|
||||
// This is redundant with the cache, but we'll skip it cheaply enough.
|
||||
r.roots = append(r.roots, gopathwalk.Root{mod.Dir, gopathwalk.RootModuleCache})
|
||||
} else {
|
||||
r.roots = append(r.roots, gopathwalk.Root{mod.Dir, gopathwalk.RootOther})
|
||||
}
|
||||
}
|
||||
// Walk dependent modules before scanning the full mod cache, direct deps first.
|
||||
for _, mod := range r.modsByModPath {
|
||||
if !mod.Indirect && !mod.Main {
|
||||
addDep(mod)
|
||||
}
|
||||
}
|
||||
for _, mod := range r.modsByModPath {
|
||||
if mod.Indirect && !mod.Main {
|
||||
addDep(mod)
|
||||
}
|
||||
}
|
||||
r.roots = append(r.roots, gopathwalk.Root{r.moduleCacheDir, gopathwalk.RootModuleCache})
|
||||
}
|
||||
|
||||
r.scannedRoots = map[gopathwalk.Root]bool{}
|
||||
if r.moduleCacheCache == nil {
|
||||
r.moduleCacheCache = &dirInfoCache{
|
||||
dirs: map[string]*directoryPackageInfo{},
|
||||
listeners: map[*int]cacheListener{},
|
||||
}
|
||||
}
|
||||
if r.otherCache == nil {
|
||||
r.otherCache = &dirInfoCache{
|
||||
dirs: map[string]*directoryPackageInfo{},
|
||||
listeners: map[*int]cacheListener{},
|
||||
}
|
||||
}
|
||||
r.initialized = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) initAllMods() error {
|
||||
stdout, err := r.env.invokeGo(context.TODO(), "list", "-m", "-e", "-json", "...")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for dec := json.NewDecoder(stdout); dec.More(); {
|
||||
mod := &gocommand.ModuleJSON{}
|
||||
if err := dec.Decode(mod); err != nil {
|
||||
return err
|
||||
}
|
||||
if mod.Dir == "" {
|
||||
if r.env.Logf != nil {
|
||||
r.env.Logf("module %v has not been downloaded and will be ignored", mod.Path)
|
||||
}
|
||||
// Can't do anything with a module that's not downloaded.
|
||||
continue
|
||||
}
|
||||
// golang/go#36193: the go command doesn't always clean paths.
|
||||
mod.Dir = filepath.Clean(mod.Dir)
|
||||
r.modsByModPath = append(r.modsByModPath, mod)
|
||||
r.modsByDir = append(r.modsByDir, mod)
|
||||
if mod.Main {
|
||||
r.main = mod
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) ClearForNewScan() {
|
||||
<-r.scanSema
|
||||
r.scannedRoots = map[gopathwalk.Root]bool{}
|
||||
r.otherCache = &dirInfoCache{
|
||||
dirs: map[string]*directoryPackageInfo{},
|
||||
listeners: map[*int]cacheListener{},
|
||||
}
|
||||
r.scanSema <- struct{}{}
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) ClearForNewMod() {
|
||||
<-r.scanSema
|
||||
*r = ModuleResolver{
|
||||
env: r.env,
|
||||
moduleCacheCache: r.moduleCacheCache,
|
||||
otherCache: r.otherCache,
|
||||
scanSema: r.scanSema,
|
||||
}
|
||||
r.init()
|
||||
r.scanSema <- struct{}{}
|
||||
}
|
||||
|
||||
// findPackage returns the module and directory that contains the package at
|
||||
// the given import path, or returns nil, "" if no module is in scope.
|
||||
func (r *ModuleResolver) findPackage(importPath string) (*gocommand.ModuleJSON, string) {
|
||||
// This can't find packages in the stdlib, but that's harmless for all
|
||||
// the existing code paths.
|
||||
for _, m := range r.modsByModPath {
|
||||
if !strings.HasPrefix(importPath, m.Path) {
|
||||
continue
|
||||
}
|
||||
pathInModule := importPath[len(m.Path):]
|
||||
pkgDir := filepath.Join(m.Dir, pathInModule)
|
||||
if r.dirIsNestedModule(pkgDir, m) {
|
||||
continue
|
||||
}
|
||||
|
||||
if info, ok := r.cacheLoad(pkgDir); ok {
|
||||
if loaded, err := info.reachedStatus(nameLoaded); loaded {
|
||||
if err != nil {
|
||||
continue // No package in this dir.
|
||||
}
|
||||
return m, pkgDir
|
||||
}
|
||||
if scanned, err := info.reachedStatus(directoryScanned); scanned && err != nil {
|
||||
continue // Dir is unreadable, etc.
|
||||
}
|
||||
// This is slightly wrong: a directory doesn't have to have an
|
||||
// importable package to count as a package for package-to-module
|
||||
// resolution. package main or _test files should count but
|
||||
// don't.
|
||||
// TODO(heschi): fix this.
|
||||
if _, err := r.cachePackageName(info); err == nil {
|
||||
return m, pkgDir
|
||||
}
|
||||
}
|
||||
|
||||
// Not cached. Read the filesystem.
|
||||
pkgFiles, err := ioutil.ReadDir(pkgDir)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// A module only contains a package if it has buildable go
|
||||
// files in that directory. If not, it could be provided by an
|
||||
// outer module. See #29736.
|
||||
for _, fi := range pkgFiles {
|
||||
if ok, _ := r.env.matchFile(pkgDir, fi.Name()); ok {
|
||||
return m, pkgDir
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) cacheLoad(dir string) (directoryPackageInfo, bool) {
|
||||
if info, ok := r.moduleCacheCache.Load(dir); ok {
|
||||
return info, ok
|
||||
}
|
||||
return r.otherCache.Load(dir)
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) cacheStore(info directoryPackageInfo) {
|
||||
if info.rootType == gopathwalk.RootModuleCache {
|
||||
r.moduleCacheCache.Store(info.dir, info)
|
||||
} else {
|
||||
r.otherCache.Store(info.dir, info)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) cacheKeys() []string {
|
||||
return append(r.moduleCacheCache.Keys(), r.otherCache.Keys()...)
|
||||
}
|
||||
|
||||
// cachePackageName caches the package name for a dir already in the cache.
|
||||
func (r *ModuleResolver) cachePackageName(info directoryPackageInfo) (string, error) {
|
||||
if info.rootType == gopathwalk.RootModuleCache {
|
||||
return r.moduleCacheCache.CachePackageName(info)
|
||||
}
|
||||
return r.otherCache.CachePackageName(info)
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) cacheExports(ctx context.Context, env *ProcessEnv, info directoryPackageInfo) (string, []string, error) {
|
||||
if info.rootType == gopathwalk.RootModuleCache {
|
||||
return r.moduleCacheCache.CacheExports(ctx, env, info)
|
||||
}
|
||||
return r.otherCache.CacheExports(ctx, env, info)
|
||||
}
|
||||
|
||||
// findModuleByDir returns the module that contains dir, or nil if no such
|
||||
// module is in scope.
|
||||
func (r *ModuleResolver) findModuleByDir(dir string) *gocommand.ModuleJSON {
|
||||
// This is quite tricky and may not be correct. dir could be:
|
||||
// - a package in the main module.
|
||||
// - a replace target underneath the main module's directory.
|
||||
// - a nested module in the above.
|
||||
// - a replace target somewhere totally random.
|
||||
// - a nested module in the above.
|
||||
// - in the mod cache.
|
||||
// - in /vendor/ in -mod=vendor mode.
|
||||
// - nested module? Dunno.
|
||||
// Rumor has it that replace targets cannot contain other replace targets.
|
||||
for _, m := range r.modsByDir {
|
||||
if !strings.HasPrefix(dir, m.Dir) {
|
||||
continue
|
||||
}
|
||||
|
||||
if r.dirIsNestedModule(dir, m) {
|
||||
continue
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// dirIsNestedModule reports if dir is contained in a nested module underneath
|
||||
// mod, not actually in mod.
|
||||
func (r *ModuleResolver) dirIsNestedModule(dir string, mod *gocommand.ModuleJSON) bool {
|
||||
if !strings.HasPrefix(dir, mod.Dir) {
|
||||
return false
|
||||
}
|
||||
if r.dirInModuleCache(dir) {
|
||||
// Nested modules in the module cache are pruned,
|
||||
// so it cannot be a nested module.
|
||||
return false
|
||||
}
|
||||
if mod != nil && mod == r.dummyVendorMod {
|
||||
// The /vendor pseudomodule is flattened and doesn't actually count.
|
||||
return false
|
||||
}
|
||||
modDir, _ := r.modInfo(dir)
|
||||
if modDir == "" {
|
||||
return false
|
||||
}
|
||||
return modDir != mod.Dir
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) modInfo(dir string) (modDir string, modName string) {
|
||||
readModName := func(modFile string) string {
|
||||
modBytes, err := ioutil.ReadFile(modFile)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return modulePath(modBytes)
|
||||
}
|
||||
|
||||
if r.dirInModuleCache(dir) {
|
||||
if matches := modCacheRegexp.FindStringSubmatch(dir); len(matches) == 3 {
|
||||
index := strings.Index(dir, matches[1]+"@"+matches[2])
|
||||
modDir := filepath.Join(dir[:index], matches[1]+"@"+matches[2])
|
||||
return modDir, readModName(filepath.Join(modDir, "go.mod"))
|
||||
}
|
||||
}
|
||||
for {
|
||||
if info, ok := r.cacheLoad(dir); ok {
|
||||
return info.moduleDir, info.moduleName
|
||||
}
|
||||
f := filepath.Join(dir, "go.mod")
|
||||
info, err := os.Stat(f)
|
||||
if err == nil && !info.IsDir() {
|
||||
return dir, readModName(f)
|
||||
}
|
||||
|
||||
d := filepath.Dir(dir)
|
||||
if len(d) >= len(dir) {
|
||||
return "", "" // reached top of file system, no go.mod
|
||||
}
|
||||
dir = d
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) dirInModuleCache(dir string) bool {
|
||||
if r.moduleCacheDir == "" {
|
||||
return false
|
||||
}
|
||||
return strings.HasPrefix(dir, r.moduleCacheDir)
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
|
||||
if err := r.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names := map[string]string{}
|
||||
for _, path := range importPaths {
|
||||
_, packageDir := r.findPackage(path)
|
||||
if packageDir == "" {
|
||||
continue
|
||||
}
|
||||
name, err := packageDirToName(packageDir)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
names[path] = name
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error {
|
||||
if err := r.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
processDir := func(info directoryPackageInfo) {
|
||||
// Skip this directory if we were not able to get the package information successfully.
|
||||
if scanned, err := info.reachedStatus(directoryScanned); !scanned || err != nil {
|
||||
return
|
||||
}
|
||||
pkg, err := r.canonicalize(info)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !callback.dirFound(pkg) {
|
||||
return
|
||||
}
|
||||
pkg.packageName, err = r.cachePackageName(info)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !callback.packageNameLoaded(pkg) {
|
||||
return
|
||||
}
|
||||
_, exports, err := r.loadExports(ctx, pkg, false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
callback.exportsLoaded(pkg, exports)
|
||||
}
|
||||
|
||||
// Start processing everything in the cache, and listen for the new stuff
|
||||
// we discover in the walk below.
|
||||
stop1 := r.moduleCacheCache.ScanAndListen(ctx, processDir)
|
||||
defer stop1()
|
||||
stop2 := r.otherCache.ScanAndListen(ctx, processDir)
|
||||
defer stop2()
|
||||
|
||||
// We assume cached directories are fully cached, including all their
|
||||
// children, and have not changed. We can skip them.
|
||||
skip := func(root gopathwalk.Root, dir string) bool {
|
||||
info, ok := r.cacheLoad(dir)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
// This directory can be skipped as long as we have already scanned it.
|
||||
// Packages with errors will continue to have errors, so there is no need
|
||||
// to rescan them.
|
||||
packageScanned, _ := info.reachedStatus(directoryScanned)
|
||||
return packageScanned
|
||||
}
|
||||
|
||||
// Add anything new to the cache, and process it if we're still listening.
|
||||
add := func(root gopathwalk.Root, dir string) {
|
||||
r.cacheStore(r.scanDirForPackage(root, dir))
|
||||
}
|
||||
|
||||
// r.roots and the callback are not necessarily safe to use in the
|
||||
// goroutine below. Process them eagerly.
|
||||
roots := filterRoots(r.roots, callback.rootFound)
|
||||
// We can't cancel walks, because we need them to finish to have a usable
|
||||
// cache. Instead, run them in a separate goroutine and detach.
|
||||
scanDone := make(chan struct{})
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-r.scanSema:
|
||||
}
|
||||
defer func() { r.scanSema <- struct{}{} }()
|
||||
// We have the lock on r.scannedRoots, and no other scans can run.
|
||||
for _, root := range roots {
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if r.scannedRoots[root] {
|
||||
continue
|
||||
}
|
||||
gopathwalk.WalkSkip([]gopathwalk.Root{root}, add, skip, gopathwalk.Options{Logf: r.env.Logf, ModulesEnabled: true})
|
||||
r.scannedRoots[root] = true
|
||||
}
|
||||
close(scanDone)
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-scanDone:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) scoreImportPath(ctx context.Context, path string) float64 {
|
||||
if _, ok := stdlib[path]; ok {
|
||||
return MaxRelevance
|
||||
}
|
||||
mod, _ := r.findPackage(path)
|
||||
return modRelevance(mod)
|
||||
}
|
||||
|
||||
func modRelevance(mod *gocommand.ModuleJSON) float64 {
|
||||
var relevance float64
|
||||
switch {
|
||||
case mod == nil: // out of scope
|
||||
return MaxRelevance - 4
|
||||
case mod.Indirect:
|
||||
relevance = MaxRelevance - 3
|
||||
case !mod.Main:
|
||||
relevance = MaxRelevance - 2
|
||||
default:
|
||||
relevance = MaxRelevance - 1 // main module ties with stdlib
|
||||
}
|
||||
|
||||
_, versionString, ok := module.SplitPathVersion(mod.Path)
|
||||
if ok {
|
||||
index := strings.Index(versionString, "v")
|
||||
if index == -1 {
|
||||
return relevance
|
||||
}
|
||||
if versionNumber, err := strconv.ParseFloat(versionString[index+1:], 64); err == nil {
|
||||
relevance += versionNumber / 1000
|
||||
}
|
||||
}
|
||||
|
||||
return relevance
|
||||
}
|
||||
|
||||
// canonicalize gets the result of canonicalizing the packages using the results
|
||||
// of initializing the resolver from 'go list -m'.
|
||||
func (r *ModuleResolver) canonicalize(info directoryPackageInfo) (*pkg, error) {
|
||||
// Packages in GOROOT are already canonical, regardless of the std/cmd modules.
|
||||
if info.rootType == gopathwalk.RootGOROOT {
|
||||
return &pkg{
|
||||
importPathShort: info.nonCanonicalImportPath,
|
||||
dir: info.dir,
|
||||
packageName: path.Base(info.nonCanonicalImportPath),
|
||||
relevance: MaxRelevance,
|
||||
}, nil
|
||||
}
|
||||
|
||||
importPath := info.nonCanonicalImportPath
|
||||
mod := r.findModuleByDir(info.dir)
|
||||
// Check if the directory is underneath a module that's in scope.
|
||||
if mod != nil {
|
||||
// It is. If dir is the target of a replace directive,
|
||||
// our guessed import path is wrong. Use the real one.
|
||||
if mod.Dir == info.dir {
|
||||
importPath = mod.Path
|
||||
} else {
|
||||
dirInMod := info.dir[len(mod.Dir)+len("/"):]
|
||||
importPath = path.Join(mod.Path, filepath.ToSlash(dirInMod))
|
||||
}
|
||||
} else if !strings.HasPrefix(importPath, info.moduleName) {
|
||||
// The module's name doesn't match the package's import path. It
|
||||
// probably needs a replace directive we don't have.
|
||||
return nil, fmt.Errorf("package in %q is not valid without a replace statement", info.dir)
|
||||
}
|
||||
|
||||
res := &pkg{
|
||||
importPathShort: importPath,
|
||||
dir: info.dir,
|
||||
relevance: modRelevance(mod),
|
||||
}
|
||||
// We may have discovered a package that has a different version
|
||||
// in scope already. Canonicalize to that one if possible.
|
||||
if _, canonicalDir := r.findPackage(importPath); canonicalDir != "" {
|
||||
res.dir = canonicalDir
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []string, error) {
|
||||
if err := r.init(); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if info, ok := r.cacheLoad(pkg.dir); ok && !includeTest {
|
||||
return r.cacheExports(ctx, r.env, info)
|
||||
}
|
||||
return loadExportsFromFiles(ctx, r.env, pkg.dir, includeTest)
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) scanDirForPackage(root gopathwalk.Root, dir string) directoryPackageInfo {
|
||||
subdir := ""
|
||||
if dir != root.Path {
|
||||
subdir = dir[len(root.Path)+len("/"):]
|
||||
}
|
||||
importPath := filepath.ToSlash(subdir)
|
||||
if strings.HasPrefix(importPath, "vendor/") {
|
||||
// Only enter vendor directories if they're explicitly requested as a root.
|
||||
return directoryPackageInfo{
|
||||
status: directoryScanned,
|
||||
err: fmt.Errorf("unwanted vendor directory"),
|
||||
}
|
||||
}
|
||||
switch root.Type {
|
||||
case gopathwalk.RootCurrentModule:
|
||||
importPath = path.Join(r.main.Path, filepath.ToSlash(subdir))
|
||||
case gopathwalk.RootModuleCache:
|
||||
matches := modCacheRegexp.FindStringSubmatch(subdir)
|
||||
if len(matches) == 0 {
|
||||
return directoryPackageInfo{
|
||||
status: directoryScanned,
|
||||
err: fmt.Errorf("invalid module cache path: %v", subdir),
|
||||
}
|
||||
}
|
||||
modPath, err := module.UnescapePath(filepath.ToSlash(matches[1]))
|
||||
if err != nil {
|
||||
if r.env.Logf != nil {
|
||||
r.env.Logf("decoding module cache path %q: %v", subdir, err)
|
||||
}
|
||||
return directoryPackageInfo{
|
||||
status: directoryScanned,
|
||||
err: fmt.Errorf("decoding module cache path %q: %v", subdir, err),
|
||||
}
|
||||
}
|
||||
importPath = path.Join(modPath, filepath.ToSlash(matches[3]))
|
||||
}
|
||||
|
||||
modDir, modName := r.modInfo(dir)
|
||||
result := directoryPackageInfo{
|
||||
status: directoryScanned,
|
||||
dir: dir,
|
||||
rootType: root.Type,
|
||||
nonCanonicalImportPath: importPath,
|
||||
moduleDir: modDir,
|
||||
moduleName: modName,
|
||||
}
|
||||
if root.Type == gopathwalk.RootGOROOT {
|
||||
// stdlib packages are always in scope, despite the confusing go.mod
|
||||
return result
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// modCacheRegexp splits a path in a module cache into module, module version, and package.
|
||||
var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`)
|
||||
|
||||
var (
|
||||
slashSlash = []byte("//")
|
||||
moduleStr = []byte("module")
|
||||
)
|
||||
|
||||
// modulePath returns the module path from the gomod file text.
|
||||
// If it cannot find a module path, it returns an empty string.
|
||||
// It is tolerant of unrelated problems in the go.mod file.
|
||||
//
|
||||
// Copied from cmd/go/internal/modfile.
|
||||
func modulePath(mod []byte) string {
|
||||
for len(mod) > 0 {
|
||||
line := mod
|
||||
mod = nil
|
||||
if i := bytes.IndexByte(line, '\n'); i >= 0 {
|
||||
line, mod = line[:i], line[i+1:]
|
||||
}
|
||||
if i := bytes.Index(line, slashSlash); i >= 0 {
|
||||
line = line[:i]
|
||||
}
|
||||
line = bytes.TrimSpace(line)
|
||||
if !bytes.HasPrefix(line, moduleStr) {
|
||||
continue
|
||||
}
|
||||
line = line[len(moduleStr):]
|
||||
n := len(line)
|
||||
line = bytes.TrimSpace(line)
|
||||
if len(line) == n || len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if line[0] == '"' || line[0] == '`' {
|
||||
p, err := strconv.Unquote(string(line))
|
||||
if err != nil {
|
||||
return "" // malformed quoted string or multiline module path
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
return string(line)
|
||||
}
|
||||
return "" // missing module path
|
||||
}
|
236
vendor/golang.org/x/tools/internal/imports/mod_cache.go
generated
vendored
Normal file
236
vendor/golang.org/x/tools/internal/imports/mod_cache.go
generated
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package imports
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/tools/internal/gopathwalk"
|
||||
)
|
||||
|
||||
// To find packages to import, the resolver needs to know about all of the
|
||||
// the packages that could be imported. This includes packages that are
|
||||
// already in modules that are in (1) the current module, (2) replace targets,
|
||||
// and (3) packages in the module cache. Packages in (1) and (2) may change over
|
||||
// time, as the client may edit the current module and locally replaced modules.
|
||||
// The module cache (which includes all of the packages in (3)) can only
|
||||
// ever be added to.
|
||||
//
|
||||
// The resolver can thus save state about packages in the module cache
|
||||
// and guarantee that this will not change over time. To obtain information
|
||||
// about new modules added to the module cache, the module cache should be
|
||||
// rescanned.
|
||||
//
|
||||
// It is OK to serve information about modules that have been deleted,
|
||||
// as they do still exist.
|
||||
// TODO(suzmue): can we share information with the caller about
|
||||
// what module needs to be downloaded to import this package?
|
||||
|
||||
type directoryPackageStatus int
|
||||
|
||||
const (
|
||||
_ directoryPackageStatus = iota
|
||||
directoryScanned
|
||||
nameLoaded
|
||||
exportsLoaded
|
||||
)
|
||||
|
||||
type directoryPackageInfo struct {
|
||||
// status indicates the extent to which this struct has been filled in.
|
||||
status directoryPackageStatus
|
||||
// err is non-nil when there was an error trying to reach status.
|
||||
err error
|
||||
|
||||
// Set when status >= directoryScanned.
|
||||
|
||||
// dir is the absolute directory of this package.
|
||||
dir string
|
||||
rootType gopathwalk.RootType
|
||||
// nonCanonicalImportPath is the package's expected import path. It may
|
||||
// not actually be importable at that path.
|
||||
nonCanonicalImportPath string
|
||||
|
||||
// Module-related information.
|
||||
moduleDir string // The directory that is the module root of this dir.
|
||||
moduleName string // The module name that contains this dir.
|
||||
|
||||
// Set when status >= nameLoaded.
|
||||
|
||||
packageName string // the package name, as declared in the source.
|
||||
|
||||
// Set when status >= exportsLoaded.
|
||||
|
||||
exports []string
|
||||
}
|
||||
|
||||
// reachedStatus returns true when info has a status at least target and any error associated with
|
||||
// an attempt to reach target.
|
||||
func (info *directoryPackageInfo) reachedStatus(target directoryPackageStatus) (bool, error) {
|
||||
if info.err == nil {
|
||||
return info.status >= target, nil
|
||||
}
|
||||
if info.status == target {
|
||||
return true, info.err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// dirInfoCache is a concurrency safe map for storing information about
|
||||
// directories that may contain packages.
|
||||
//
|
||||
// The information in this cache is built incrementally. Entries are initialized in scan.
|
||||
// No new keys should be added in any other functions, as all directories containing
|
||||
// packages are identified in scan.
|
||||
//
|
||||
// Other functions, including loadExports and findPackage, may update entries in this cache
|
||||
// as they discover new things about the directory.
|
||||
//
|
||||
// The information in the cache is not expected to change for the cache's
|
||||
// lifetime, so there is no protection against competing writes. Users should
|
||||
// take care not to hold the cache across changes to the underlying files.
|
||||
//
|
||||
// TODO(suzmue): consider other concurrency strategies and data structures (RWLocks, sync.Map, etc)
|
||||
type dirInfoCache struct {
|
||||
mu sync.Mutex
|
||||
// dirs stores information about packages in directories, keyed by absolute path.
|
||||
dirs map[string]*directoryPackageInfo
|
||||
listeners map[*int]cacheListener
|
||||
}
|
||||
|
||||
type cacheListener func(directoryPackageInfo)
|
||||
|
||||
// ScanAndListen calls listener on all the items in the cache, and on anything
|
||||
// newly added. The returned stop function waits for all in-flight callbacks to
|
||||
// finish and blocks new ones.
|
||||
func (d *dirInfoCache) ScanAndListen(ctx context.Context, listener cacheListener) func() {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
// Flushing out all the callbacks is tricky without knowing how many there
|
||||
// are going to be. Setting an arbitrary limit makes it much easier.
|
||||
const maxInFlight = 10
|
||||
sema := make(chan struct{}, maxInFlight)
|
||||
for i := 0; i < maxInFlight; i++ {
|
||||
sema <- struct{}{}
|
||||
}
|
||||
|
||||
cookie := new(int) // A unique ID we can use for the listener.
|
||||
|
||||
// We can't hold mu while calling the listener.
|
||||
d.mu.Lock()
|
||||
var keys []string
|
||||
for key := range d.dirs {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
d.listeners[cookie] = func(info directoryPackageInfo) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-sema:
|
||||
}
|
||||
listener(info)
|
||||
sema <- struct{}{}
|
||||
}
|
||||
d.mu.Unlock()
|
||||
|
||||
stop := func() {
|
||||
cancel()
|
||||
d.mu.Lock()
|
||||
delete(d.listeners, cookie)
|
||||
d.mu.Unlock()
|
||||
for i := 0; i < maxInFlight; i++ {
|
||||
<-sema
|
||||
}
|
||||
}
|
||||
|
||||
// Process the pre-existing keys.
|
||||
for _, k := range keys {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return stop
|
||||
default:
|
||||
}
|
||||
if v, ok := d.Load(k); ok {
|
||||
listener(v)
|
||||
}
|
||||
}
|
||||
|
||||
return stop
|
||||
}
|
||||
|
||||
// Store stores the package info for dir.
|
||||
func (d *dirInfoCache) Store(dir string, info directoryPackageInfo) {
|
||||
d.mu.Lock()
|
||||
_, old := d.dirs[dir]
|
||||
d.dirs[dir] = &info
|
||||
var listeners []cacheListener
|
||||
for _, l := range d.listeners {
|
||||
listeners = append(listeners, l)
|
||||
}
|
||||
d.mu.Unlock()
|
||||
|
||||
if !old {
|
||||
for _, l := range listeners {
|
||||
l(info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load returns a copy of the directoryPackageInfo for absolute directory dir.
|
||||
func (d *dirInfoCache) Load(dir string) (directoryPackageInfo, bool) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
info, ok := d.dirs[dir]
|
||||
if !ok {
|
||||
return directoryPackageInfo{}, false
|
||||
}
|
||||
return *info, true
|
||||
}
|
||||
|
||||
// Keys returns the keys currently present in d.
|
||||
func (d *dirInfoCache) Keys() (keys []string) {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
for key := range d.dirs {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
func (d *dirInfoCache) CachePackageName(info directoryPackageInfo) (string, error) {
|
||||
if loaded, err := info.reachedStatus(nameLoaded); loaded {
|
||||
return info.packageName, err
|
||||
}
|
||||
if scanned, err := info.reachedStatus(directoryScanned); !scanned || err != nil {
|
||||
return "", fmt.Errorf("cannot read package name, scan error: %v", err)
|
||||
}
|
||||
info.packageName, info.err = packageDirToName(info.dir)
|
||||
info.status = nameLoaded
|
||||
d.Store(info.dir, info)
|
||||
return info.packageName, info.err
|
||||
}
|
||||
|
||||
func (d *dirInfoCache) CacheExports(ctx context.Context, env *ProcessEnv, info directoryPackageInfo) (string, []string, error) {
|
||||
if reached, _ := info.reachedStatus(exportsLoaded); reached {
|
||||
return info.packageName, info.exports, info.err
|
||||
}
|
||||
if reached, err := info.reachedStatus(nameLoaded); reached && err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
info.packageName, info.exports, info.err = loadExportsFromFiles(ctx, env, info.dir, false)
|
||||
if info.err == context.Canceled || info.err == context.DeadlineExceeded {
|
||||
return info.packageName, info.exports, info.err
|
||||
}
|
||||
// The cache structure wants things to proceed linearly. We can skip a
|
||||
// step here, but only if we succeed.
|
||||
if info.status == nameLoaded || info.err == nil {
|
||||
info.status = exportsLoaded
|
||||
} else {
|
||||
info.status = nameLoaded
|
||||
}
|
||||
d.Store(info.dir, info)
|
||||
return info.packageName, info.exports, info.err
|
||||
}
|
280
vendor/golang.org/x/tools/internal/imports/sortimports.go
generated
vendored
Normal file
280
vendor/golang.org/x/tools/internal/imports/sortimports.go
generated
vendored
Normal file
@@ -0,0 +1,280 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Hacked up copy of go/ast/import.go
|
||||
|
||||
package imports
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// sortImports sorts runs of consecutive import lines in import blocks in f.
|
||||
// It also removes duplicate imports when it is possible to do so without data loss.
|
||||
func sortImports(localPrefix string, fset *token.FileSet, f *ast.File) {
|
||||
for i, d := range f.Decls {
|
||||
d, ok := d.(*ast.GenDecl)
|
||||
if !ok || d.Tok != token.IMPORT {
|
||||
// Not an import declaration, so we're done.
|
||||
// Imports are always first.
|
||||
break
|
||||
}
|
||||
|
||||
if len(d.Specs) == 0 {
|
||||
// Empty import block, remove it.
|
||||
f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
|
||||
}
|
||||
|
||||
if !d.Lparen.IsValid() {
|
||||
// Not a block: sorted by default.
|
||||
continue
|
||||
}
|
||||
|
||||
// Identify and sort runs of specs on successive lines.
|
||||
i := 0
|
||||
specs := d.Specs[:0]
|
||||
for j, s := range d.Specs {
|
||||
if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line {
|
||||
// j begins a new run. End this one.
|
||||
specs = append(specs, sortSpecs(localPrefix, fset, f, d.Specs[i:j])...)
|
||||
i = j
|
||||
}
|
||||
}
|
||||
specs = append(specs, sortSpecs(localPrefix, fset, f, d.Specs[i:])...)
|
||||
d.Specs = specs
|
||||
|
||||
// Deduping can leave a blank line before the rparen; clean that up.
|
||||
if len(d.Specs) > 0 {
|
||||
lastSpec := d.Specs[len(d.Specs)-1]
|
||||
lastLine := fset.Position(lastSpec.Pos()).Line
|
||||
if rParenLine := fset.Position(d.Rparen).Line; rParenLine > lastLine+1 {
|
||||
fset.File(d.Rparen).MergeLine(rParenLine - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mergeImports merges all the import declarations into the first one.
|
||||
// Taken from golang.org/x/tools/ast/astutil.
|
||||
func mergeImports(fset *token.FileSet, f *ast.File) {
|
||||
if len(f.Decls) <= 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// Merge all the import declarations into the first one.
|
||||
var first *ast.GenDecl
|
||||
for i := 0; i < len(f.Decls); i++ {
|
||||
decl := f.Decls[i]
|
||||
gen, ok := decl.(*ast.GenDecl)
|
||||
if !ok || gen.Tok != token.IMPORT || declImports(gen, "C") {
|
||||
continue
|
||||
}
|
||||
if first == nil {
|
||||
first = gen
|
||||
continue // Don't touch the first one.
|
||||
}
|
||||
// We now know there is more than one package in this import
|
||||
// declaration. Ensure that it ends up parenthesized.
|
||||
first.Lparen = first.Pos()
|
||||
// Move the imports of the other import declaration to the first one.
|
||||
for _, spec := range gen.Specs {
|
||||
spec.(*ast.ImportSpec).Path.ValuePos = first.Pos()
|
||||
first.Specs = append(first.Specs, spec)
|
||||
}
|
||||
f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
|
||||
// declImports reports whether gen contains an import of path.
|
||||
// Taken from golang.org/x/tools/ast/astutil.
|
||||
func declImports(gen *ast.GenDecl, path string) bool {
|
||||
if gen.Tok != token.IMPORT {
|
||||
return false
|
||||
}
|
||||
for _, spec := range gen.Specs {
|
||||
impspec := spec.(*ast.ImportSpec)
|
||||
if importPath(impspec) == path {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func importPath(s ast.Spec) string {
|
||||
t, err := strconv.Unquote(s.(*ast.ImportSpec).Path.Value)
|
||||
if err == nil {
|
||||
return t
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func importName(s ast.Spec) string {
|
||||
n := s.(*ast.ImportSpec).Name
|
||||
if n == nil {
|
||||
return ""
|
||||
}
|
||||
return n.Name
|
||||
}
|
||||
|
||||
func importComment(s ast.Spec) string {
|
||||
c := s.(*ast.ImportSpec).Comment
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return c.Text()
|
||||
}
|
||||
|
||||
// collapse indicates whether prev may be removed, leaving only next.
|
||||
func collapse(prev, next ast.Spec) bool {
|
||||
if importPath(next) != importPath(prev) || importName(next) != importName(prev) {
|
||||
return false
|
||||
}
|
||||
return prev.(*ast.ImportSpec).Comment == nil
|
||||
}
|
||||
|
||||
type posSpan struct {
|
||||
Start token.Pos
|
||||
End token.Pos
|
||||
}
|
||||
|
||||
func sortSpecs(localPrefix string, fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec {
|
||||
// Can't short-circuit here even if specs are already sorted,
|
||||
// since they might yet need deduplication.
|
||||
// A lone import, however, may be safely ignored.
|
||||
if len(specs) <= 1 {
|
||||
return specs
|
||||
}
|
||||
|
||||
// Record positions for specs.
|
||||
pos := make([]posSpan, len(specs))
|
||||
for i, s := range specs {
|
||||
pos[i] = posSpan{s.Pos(), s.End()}
|
||||
}
|
||||
|
||||
// Identify comments in this range.
|
||||
// Any comment from pos[0].Start to the final line counts.
|
||||
lastLine := fset.Position(pos[len(pos)-1].End).Line
|
||||
cstart := len(f.Comments)
|
||||
cend := len(f.Comments)
|
||||
for i, g := range f.Comments {
|
||||
if g.Pos() < pos[0].Start {
|
||||
continue
|
||||
}
|
||||
if i < cstart {
|
||||
cstart = i
|
||||
}
|
||||
if fset.Position(g.End()).Line > lastLine {
|
||||
cend = i
|
||||
break
|
||||
}
|
||||
}
|
||||
comments := f.Comments[cstart:cend]
|
||||
|
||||
// Assign each comment to the import spec preceding it.
|
||||
importComment := map[*ast.ImportSpec][]*ast.CommentGroup{}
|
||||
specIndex := 0
|
||||
for _, g := range comments {
|
||||
for specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() {
|
||||
specIndex++
|
||||
}
|
||||
s := specs[specIndex].(*ast.ImportSpec)
|
||||
importComment[s] = append(importComment[s], g)
|
||||
}
|
||||
|
||||
// Sort the import specs by import path.
|
||||
// Remove duplicates, when possible without data loss.
|
||||
// Reassign the import paths to have the same position sequence.
|
||||
// Reassign each comment to abut the end of its spec.
|
||||
// Sort the comments by new position.
|
||||
sort.Sort(byImportSpec{localPrefix, specs})
|
||||
|
||||
// Dedup. Thanks to our sorting, we can just consider
|
||||
// adjacent pairs of imports.
|
||||
deduped := specs[:0]
|
||||
for i, s := range specs {
|
||||
if i == len(specs)-1 || !collapse(s, specs[i+1]) {
|
||||
deduped = append(deduped, s)
|
||||
} else {
|
||||
p := s.Pos()
|
||||
fset.File(p).MergeLine(fset.Position(p).Line)
|
||||
}
|
||||
}
|
||||
specs = deduped
|
||||
|
||||
// Fix up comment positions
|
||||
for i, s := range specs {
|
||||
s := s.(*ast.ImportSpec)
|
||||
if s.Name != nil {
|
||||
s.Name.NamePos = pos[i].Start
|
||||
}
|
||||
s.Path.ValuePos = pos[i].Start
|
||||
s.EndPos = pos[i].End
|
||||
nextSpecPos := pos[i].End
|
||||
|
||||
for _, g := range importComment[s] {
|
||||
for _, c := range g.List {
|
||||
c.Slash = pos[i].End
|
||||
nextSpecPos = c.End()
|
||||
}
|
||||
}
|
||||
if i < len(specs)-1 {
|
||||
pos[i+1].Start = nextSpecPos
|
||||
pos[i+1].End = nextSpecPos
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(byCommentPos(comments))
|
||||
|
||||
// Fixup comments can insert blank lines, because import specs are on different lines.
|
||||
// We remove those blank lines here by merging import spec to the first import spec line.
|
||||
firstSpecLine := fset.Position(specs[0].Pos()).Line
|
||||
for _, s := range specs[1:] {
|
||||
p := s.Pos()
|
||||
line := fset.File(p).Line(p)
|
||||
for previousLine := line - 1; previousLine >= firstSpecLine; {
|
||||
fset.File(p).MergeLine(previousLine)
|
||||
previousLine--
|
||||
}
|
||||
}
|
||||
return specs
|
||||
}
|
||||
|
||||
type byImportSpec struct {
|
||||
localPrefix string
|
||||
specs []ast.Spec // slice of *ast.ImportSpec
|
||||
}
|
||||
|
||||
func (x byImportSpec) Len() int { return len(x.specs) }
|
||||
func (x byImportSpec) Swap(i, j int) { x.specs[i], x.specs[j] = x.specs[j], x.specs[i] }
|
||||
func (x byImportSpec) Less(i, j int) bool {
|
||||
ipath := importPath(x.specs[i])
|
||||
jpath := importPath(x.specs[j])
|
||||
|
||||
igroup := importGroup(x.localPrefix, ipath)
|
||||
jgroup := importGroup(x.localPrefix, jpath)
|
||||
if igroup != jgroup {
|
||||
return igroup < jgroup
|
||||
}
|
||||
|
||||
if ipath != jpath {
|
||||
return ipath < jpath
|
||||
}
|
||||
iname := importName(x.specs[i])
|
||||
jname := importName(x.specs[j])
|
||||
|
||||
if iname != jname {
|
||||
return iname < jname
|
||||
}
|
||||
return importComment(x.specs[i]) < importComment(x.specs[j])
|
||||
}
|
||||
|
||||
type byCommentPos []*ast.CommentGroup
|
||||
|
||||
func (x byCommentPos) Len() int { return len(x) }
|
||||
func (x byCommentPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
func (x byCommentPos) Less(i, j int) bool { return x[i].Pos() < x[j].Pos() }
|
10733
vendor/golang.org/x/tools/internal/imports/zstdlib.go
generated
vendored
Normal file
10733
vendor/golang.org/x/tools/internal/imports/zstdlib.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user