diff --git a/go.mod b/go.mod index 72e1ed02778d..7d2dfb10c9c5 100644 --- a/go.mod +++ b/go.mod @@ -56,7 +56,7 @@ require ( github.com/spf13/viper v1.0.2 github.com/stretchr/testify v1.2.1 golang.org/x/crypto v0.0.0-20180505025534-4ec37c66abab // indirect - golang.org/x/tools v0.0.0-20181220024903-92cdcd90bf52 + golang.org/x/tools v0.0.0-20190205201329-379209517ffe gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect gopkg.in/yaml.v2 v2.2.1 diff --git a/go.sum b/go.sum index 7f840c92e2b6..628c945cc12f 100644 --- a/go.sum +++ b/go.sum @@ -150,6 +150,8 @@ golang.org/x/tools v0.0.0-20181201035826-d0ca3933b724/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20181205014116-22934f0fdb62/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181220024903-92cdcd90bf52 h1:oOIe9Zzq27JsS/3ACpGF1HwWnWNflZWT/3EvM7mtcEk= golang.org/x/tools v0.0.0-20181220024903-92cdcd90bf52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190205201329-379209517ffe h1:wgpl8ZFbbyCTwUEyUPYafSzqRqUUGAuN9JSXU9iBjts= +golang.org/x/tools v0.0.0-20190205201329-379209517ffe/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go b/vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go index 9f6504914043..a807d0aaa281 100644 --- a/vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go @@ -127,10 +127,10 @@ func BExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) // --- generic export data --- // populate type map with predeclared "known" types - for index, typ := range predeclared { + for index, typ := range predeclared() { p.typIndex[typ] = index } - if len(p.typIndex) != len(predeclared) { + if len(p.typIndex) != len(predeclared()) { return nil, internalError("duplicate entries in type map?") } diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go b/vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go index b31eacfc0fc9..e3c310782516 100644 --- a/vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go @@ -126,7 +126,7 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data [] // --- generic export data --- // populate typList with predeclared "known" types - p.typList = append(p.typList, predeclared...) + p.typList = append(p.typList, predeclared()...) // read package data pkg = p.pkg() @@ -976,50 +976,58 @@ const ( aliasTag ) -var predeclared = []types.Type{ - // basic types - types.Typ[types.Bool], - types.Typ[types.Int], - types.Typ[types.Int8], - types.Typ[types.Int16], - types.Typ[types.Int32], - types.Typ[types.Int64], - types.Typ[types.Uint], - types.Typ[types.Uint8], - types.Typ[types.Uint16], - types.Typ[types.Uint32], - types.Typ[types.Uint64], - types.Typ[types.Uintptr], - types.Typ[types.Float32], - types.Typ[types.Float64], - types.Typ[types.Complex64], - types.Typ[types.Complex128], - types.Typ[types.String], - - // basic type aliases - types.Universe.Lookup("byte").Type(), - types.Universe.Lookup("rune").Type(), - - // error - types.Universe.Lookup("error").Type(), - - // untyped types - types.Typ[types.UntypedBool], - types.Typ[types.UntypedInt], - types.Typ[types.UntypedRune], - types.Typ[types.UntypedFloat], - types.Typ[types.UntypedComplex], - types.Typ[types.UntypedString], - types.Typ[types.UntypedNil], - - // package unsafe - types.Typ[types.UnsafePointer], - - // invalid type - types.Typ[types.Invalid], // only appears in packages with errors - - // used internally by gc; never used by this package or in .a files - anyType{}, +var predecl []types.Type // initialized lazily + +func predeclared() []types.Type { + if predecl == nil { + // initialize lazily to be sure that all + // elements have been initialized before + predecl = []types.Type{ // basic types + types.Typ[types.Bool], + types.Typ[types.Int], + types.Typ[types.Int8], + types.Typ[types.Int16], + types.Typ[types.Int32], + types.Typ[types.Int64], + types.Typ[types.Uint], + types.Typ[types.Uint8], + types.Typ[types.Uint16], + types.Typ[types.Uint32], + types.Typ[types.Uint64], + types.Typ[types.Uintptr], + types.Typ[types.Float32], + types.Typ[types.Float64], + types.Typ[types.Complex64], + types.Typ[types.Complex128], + types.Typ[types.String], + + // basic type aliases + types.Universe.Lookup("byte").Type(), + types.Universe.Lookup("rune").Type(), + + // error + types.Universe.Lookup("error").Type(), + + // untyped types + types.Typ[types.UntypedBool], + types.Typ[types.UntypedInt], + types.Typ[types.UntypedRune], + types.Typ[types.UntypedFloat], + types.Typ[types.UntypedComplex], + types.Typ[types.UntypedString], + types.Typ[types.UntypedNil], + + // package unsafe + types.Typ[types.UnsafePointer], + + // invalid type + types.Typ[types.Invalid], // only appears in packages with errors + + // used internally by gc; never used by this package or in .a files + anyType{}, + } + } + return predecl } type anyType struct{} diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go b/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go new file mode 100644 index 000000000000..be671c79b701 --- /dev/null +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/iexport.go @@ -0,0 +1,723 @@ +// 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. + +// Indexed binary package export. +// This file was derived from $GOROOT/src/cmd/compile/internal/gc/iexport.go; +// see that file for specification of the format. + +// +build go1.11 + +package gcimporter + +import ( + "bytes" + "encoding/binary" + "go/ast" + "go/constant" + "go/token" + "go/types" + "io" + "math/big" + "reflect" + "sort" +) + +// Current indexed export format version. Increase with each format change. +// 0: Go1.11 encoding +const iexportVersion = 0 + +// IExportData returns the binary export data for pkg. +// If no file set is provided, position info will be missing. +func IExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) { + defer func() { + if e := recover(); e != nil { + if ierr, ok := e.(internalError); ok { + err = ierr + return + } + // Not an internal error; panic again. + panic(e) + } + }() + + p := iexporter{ + out: bytes.NewBuffer(nil), + fset: fset, + allPkgs: map[*types.Package]bool{}, + stringIndex: map[string]uint64{}, + declIndex: map[types.Object]uint64{}, + typIndex: map[types.Type]uint64{}, + } + + for i, pt := range predeclared() { + p.typIndex[pt] = uint64(i) + } + if len(p.typIndex) > predeclReserved { + panic(internalErrorf("too many predeclared types: %d > %d", len(p.typIndex), predeclReserved)) + } + + // Initialize work queue with exported declarations. + scope := pkg.Scope() + for _, name := range scope.Names() { + if ast.IsExported(name) { + p.pushDecl(scope.Lookup(name)) + } + } + + // Loop until no more work. + for !p.declTodo.empty() { + p.doDecl(p.declTodo.popHead()) + } + + // Append indices to data0 section. + dataLen := uint64(p.data0.Len()) + w := p.newWriter() + w.writeIndex(p.declIndex, pkg) + w.flush() + + // Assemble header. + var hdr intWriter + hdr.WriteByte('i') + hdr.uint64(iexportVersion) + hdr.uint64(uint64(p.strings.Len())) + hdr.uint64(dataLen) + + // Flush output. + io.Copy(p.out, &hdr) + io.Copy(p.out, &p.strings) + io.Copy(p.out, &p.data0) + + return p.out.Bytes(), nil +} + +// writeIndex writes out an object index. mainIndex indicates whether +// we're writing out the main index, which is also read by +// non-compiler tools and includes a complete package description +// (i.e., name and height). +func (w *exportWriter) writeIndex(index map[types.Object]uint64, localpkg *types.Package) { + // Build a map from packages to objects from that package. + pkgObjs := map[*types.Package][]types.Object{} + + // For the main index, make sure to include every package that + // we reference, even if we're not exporting (or reexporting) + // any symbols from it. + pkgObjs[localpkg] = nil + for pkg := range w.p.allPkgs { + pkgObjs[pkg] = nil + } + + for obj := range index { + pkgObjs[obj.Pkg()] = append(pkgObjs[obj.Pkg()], obj) + } + + var pkgs []*types.Package + for pkg, objs := range pkgObjs { + pkgs = append(pkgs, pkg) + + sort.Slice(objs, func(i, j int) bool { + return objs[i].Name() < objs[j].Name() + }) + } + + sort.Slice(pkgs, func(i, j int) bool { + return pkgs[i].Path() < pkgs[j].Path() + }) + + w.uint64(uint64(len(pkgs))) + for _, pkg := range pkgs { + w.string(pkg.Path()) + w.string(pkg.Name()) + w.uint64(uint64(0)) // package height is not needed for go/types + + objs := pkgObjs[pkg] + w.uint64(uint64(len(objs))) + for _, obj := range objs { + w.string(obj.Name()) + w.uint64(index[obj]) + } + } +} + +type iexporter struct { + fset *token.FileSet + out *bytes.Buffer + + // allPkgs tracks all packages that have been referenced by + // the export data, so we can ensure to include them in the + // main index. + allPkgs map[*types.Package]bool + + declTodo objQueue + + strings intWriter + stringIndex map[string]uint64 + + data0 intWriter + declIndex map[types.Object]uint64 + typIndex map[types.Type]uint64 +} + +// stringOff returns the offset of s within the string section. +// If not already present, it's added to the end. +func (p *iexporter) stringOff(s string) uint64 { + off, ok := p.stringIndex[s] + if !ok { + off = uint64(p.strings.Len()) + p.stringIndex[s] = off + + p.strings.uint64(uint64(len(s))) + p.strings.WriteString(s) + } + return off +} + +// pushDecl adds n to the declaration work queue, if not already present. +func (p *iexporter) pushDecl(obj types.Object) { + // Package unsafe is known to the compiler and predeclared. + assert(obj.Pkg() != types.Unsafe) + + if _, ok := p.declIndex[obj]; ok { + return + } + + p.declIndex[obj] = ^uint64(0) // mark n present in work queue + p.declTodo.pushTail(obj) +} + +// exportWriter handles writing out individual data section chunks. +type exportWriter struct { + p *iexporter + + data intWriter + currPkg *types.Package + prevFile string + prevLine int64 +} + +func (p *iexporter) doDecl(obj types.Object) { + w := p.newWriter() + w.setPkg(obj.Pkg(), false) + + switch obj := obj.(type) { + case *types.Var: + w.tag('V') + w.pos(obj.Pos()) + w.typ(obj.Type(), obj.Pkg()) + + case *types.Func: + sig, _ := obj.Type().(*types.Signature) + if sig.Recv() != nil { + panic(internalErrorf("unexpected method: %v", sig)) + } + w.tag('F') + w.pos(obj.Pos()) + w.signature(sig) + + case *types.Const: + w.tag('C') + w.pos(obj.Pos()) + w.value(obj.Type(), obj.Val()) + + case *types.TypeName: + if obj.IsAlias() { + w.tag('A') + w.pos(obj.Pos()) + w.typ(obj.Type(), obj.Pkg()) + break + } + + // Defined type. + w.tag('T') + w.pos(obj.Pos()) + + underlying := obj.Type().Underlying() + w.typ(underlying, obj.Pkg()) + + t := obj.Type() + if types.IsInterface(t) { + break + } + + named, ok := t.(*types.Named) + if !ok { + panic(internalErrorf("%s is not a defined type", t)) + } + + n := named.NumMethods() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + m := named.Method(i) + w.pos(m.Pos()) + w.string(m.Name()) + sig, _ := m.Type().(*types.Signature) + w.param(sig.Recv()) + w.signature(sig) + } + + default: + panic(internalErrorf("unexpected object: %v", obj)) + } + + p.declIndex[obj] = w.flush() +} + +func (w *exportWriter) tag(tag byte) { + w.data.WriteByte(tag) +} + +func (w *exportWriter) pos(pos token.Pos) { + p := w.p.fset.Position(pos) + file := p.Filename + line := int64(p.Line) + + // When file is the same as the last position (common case), + // we can save a few bytes by delta encoding just the line + // number. + // + // Note: Because data objects may be read out of order (or not + // at all), we can only apply delta encoding within a single + // object. This is handled implicitly by tracking prevFile and + // prevLine as fields of exportWriter. + + if file == w.prevFile { + delta := line - w.prevLine + w.int64(delta) + if delta == deltaNewFile { + w.int64(-1) + } + } else { + w.int64(deltaNewFile) + w.int64(line) // line >= 0 + w.string(file) + w.prevFile = file + } + w.prevLine = line +} + +func (w *exportWriter) pkg(pkg *types.Package) { + // Ensure any referenced packages are declared in the main index. + w.p.allPkgs[pkg] = true + + w.string(pkg.Path()) +} + +func (w *exportWriter) qualifiedIdent(obj types.Object) { + // Ensure any referenced declarations are written out too. + w.p.pushDecl(obj) + + w.string(obj.Name()) + w.pkg(obj.Pkg()) +} + +func (w *exportWriter) typ(t types.Type, pkg *types.Package) { + w.data.uint64(w.p.typOff(t, pkg)) +} + +func (p *iexporter) newWriter() *exportWriter { + return &exportWriter{p: p} +} + +func (w *exportWriter) flush() uint64 { + off := uint64(w.p.data0.Len()) + io.Copy(&w.p.data0, &w.data) + return off +} + +func (p *iexporter) typOff(t types.Type, pkg *types.Package) uint64 { + off, ok := p.typIndex[t] + if !ok { + w := p.newWriter() + w.doTyp(t, pkg) + off = predeclReserved + w.flush() + p.typIndex[t] = off + } + return off +} + +func (w *exportWriter) startType(k itag) { + w.data.uint64(uint64(k)) +} + +func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { + switch t := t.(type) { + case *types.Named: + w.startType(definedType) + w.qualifiedIdent(t.Obj()) + + case *types.Pointer: + w.startType(pointerType) + w.typ(t.Elem(), pkg) + + case *types.Slice: + w.startType(sliceType) + w.typ(t.Elem(), pkg) + + case *types.Array: + w.startType(arrayType) + w.uint64(uint64(t.Len())) + w.typ(t.Elem(), pkg) + + case *types.Chan: + w.startType(chanType) + // 1 RecvOnly; 2 SendOnly; 3 SendRecv + var dir uint64 + switch t.Dir() { + case types.RecvOnly: + dir = 1 + case types.SendOnly: + dir = 2 + case types.SendRecv: + dir = 3 + } + w.uint64(dir) + w.typ(t.Elem(), pkg) + + case *types.Map: + w.startType(mapType) + w.typ(t.Key(), pkg) + w.typ(t.Elem(), pkg) + + case *types.Signature: + w.startType(signatureType) + w.setPkg(pkg, true) + w.signature(t) + + case *types.Struct: + w.startType(structType) + w.setPkg(pkg, true) + + n := t.NumFields() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + f := t.Field(i) + w.pos(f.Pos()) + w.string(f.Name()) + w.typ(f.Type(), pkg) + w.bool(f.Embedded()) + w.string(t.Tag(i)) // note (or tag) + } + + case *types.Interface: + w.startType(interfaceType) + w.setPkg(pkg, true) + + n := t.NumEmbeddeds() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + f := t.Embedded(i) + w.pos(f.Obj().Pos()) + w.typ(f.Obj().Type(), f.Obj().Pkg()) + } + + n = t.NumExplicitMethods() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + m := t.ExplicitMethod(i) + w.pos(m.Pos()) + w.string(m.Name()) + sig, _ := m.Type().(*types.Signature) + w.signature(sig) + } + + default: + panic(internalErrorf("unexpected type: %v, %v", t, reflect.TypeOf(t))) + } +} + +func (w *exportWriter) setPkg(pkg *types.Package, write bool) { + if write { + w.pkg(pkg) + } + + w.currPkg = pkg +} + +func (w *exportWriter) signature(sig *types.Signature) { + w.paramList(sig.Params()) + w.paramList(sig.Results()) + if sig.Params().Len() > 0 { + w.bool(sig.Variadic()) + } +} + +func (w *exportWriter) paramList(tup *types.Tuple) { + n := tup.Len() + w.uint64(uint64(n)) + for i := 0; i < n; i++ { + w.param(tup.At(i)) + } +} + +func (w *exportWriter) param(obj types.Object) { + w.pos(obj.Pos()) + w.localIdent(obj) + w.typ(obj.Type(), obj.Pkg()) +} + +func (w *exportWriter) value(typ types.Type, v constant.Value) { + w.typ(typ, nil) + + switch v.Kind() { + case constant.Bool: + w.bool(constant.BoolVal(v)) + case constant.Int: + var i big.Int + if i64, exact := constant.Int64Val(v); exact { + i.SetInt64(i64) + } else if ui64, exact := constant.Uint64Val(v); exact { + i.SetUint64(ui64) + } else { + i.SetString(v.ExactString(), 10) + } + w.mpint(&i, typ) + case constant.Float: + f := constantToFloat(v) + w.mpfloat(f, typ) + case constant.Complex: + w.mpfloat(constantToFloat(constant.Real(v)), typ) + w.mpfloat(constantToFloat(constant.Imag(v)), typ) + case constant.String: + w.string(constant.StringVal(v)) + case constant.Unknown: + // package contains type errors + default: + panic(internalErrorf("unexpected value %v (%T)", v, v)) + } +} + +// constantToFloat converts a constant.Value with kind constant.Float to a +// big.Float. +func constantToFloat(x constant.Value) *big.Float { + assert(x.Kind() == constant.Float) + // Use the same floating-point precision (512) as cmd/compile + // (see Mpprec in cmd/compile/internal/gc/mpfloat.go). + const mpprec = 512 + var f big.Float + f.SetPrec(mpprec) + if v, exact := constant.Float64Val(x); exact { + // float64 + f.SetFloat64(v) + } else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int { + // TODO(gri): add big.Rat accessor to constant.Value. + n := valueToRat(num) + d := valueToRat(denom) + f.SetRat(n.Quo(n, d)) + } else { + // Value too large to represent as a fraction => inaccessible. + // TODO(gri): add big.Float accessor to constant.Value. + _, ok := f.SetString(x.ExactString()) + assert(ok) + } + return &f +} + +// mpint exports a multi-precision integer. +// +// For unsigned types, small values are written out as a single +// byte. Larger values are written out as a length-prefixed big-endian +// byte string, where the length prefix is encoded as its complement. +// For example, bytes 0, 1, and 2 directly represent the integer +// values 0, 1, and 2; while bytes 255, 254, and 253 indicate a 1-, +// 2-, and 3-byte big-endian string follow. +// +// Encoding for signed types use the same general approach as for +// unsigned types, except small values use zig-zag encoding and the +// bottom bit of length prefix byte for large values is reserved as a +// sign bit. +// +// The exact boundary between small and large encodings varies +// according to the maximum number of bytes needed to encode a value +// of type typ. As a special case, 8-bit types are always encoded as a +// single byte. +// +// TODO(mdempsky): Is this level of complexity really worthwhile? +func (w *exportWriter) mpint(x *big.Int, typ types.Type) { + basic, ok := typ.Underlying().(*types.Basic) + if !ok { + panic(internalErrorf("unexpected type %v (%T)", typ.Underlying(), typ.Underlying())) + } + + signed, maxBytes := intSize(basic) + + negative := x.Sign() < 0 + if !signed && negative { + panic(internalErrorf("negative unsigned integer; type %v, value %v", typ, x)) + } + + b := x.Bytes() + if len(b) > 0 && b[0] == 0 { + panic(internalErrorf("leading zeros")) + } + if uint(len(b)) > maxBytes { + panic(internalErrorf("bad mpint length: %d > %d (type %v, value %v)", len(b), maxBytes, typ, x)) + } + + maxSmall := 256 - maxBytes + if signed { + maxSmall = 256 - 2*maxBytes + } + if maxBytes == 1 { + maxSmall = 256 + } + + // Check if x can use small value encoding. + if len(b) <= 1 { + var ux uint + if len(b) == 1 { + ux = uint(b[0]) + } + if signed { + ux <<= 1 + if negative { + ux-- + } + } + if ux < maxSmall { + w.data.WriteByte(byte(ux)) + return + } + } + + n := 256 - uint(len(b)) + if signed { + n = 256 - 2*uint(len(b)) + if negative { + n |= 1 + } + } + if n < maxSmall || n >= 256 { + panic(internalErrorf("encoding mistake: %d, %v, %v => %d", len(b), signed, negative, n)) + } + + w.data.WriteByte(byte(n)) + w.data.Write(b) +} + +// mpfloat exports a multi-precision floating point number. +// +// The number's value is decomposed into mantissa × 2**exponent, where +// mantissa is an integer. The value is written out as mantissa (as a +// multi-precision integer) and then the exponent, except exponent is +// omitted if mantissa is zero. +func (w *exportWriter) mpfloat(f *big.Float, typ types.Type) { + if f.IsInf() { + panic("infinite constant") + } + + // Break into f = mant × 2**exp, with 0.5 <= mant < 1. + var mant big.Float + exp := int64(f.MantExp(&mant)) + + // Scale so that mant is an integer. + prec := mant.MinPrec() + mant.SetMantExp(&mant, int(prec)) + exp -= int64(prec) + + manti, acc := mant.Int(nil) + if acc != big.Exact { + panic(internalErrorf("mantissa scaling failed for %f (%s)", f, acc)) + } + w.mpint(manti, typ) + if manti.Sign() != 0 { + w.int64(exp) + } +} + +func (w *exportWriter) bool(b bool) bool { + var x uint64 + if b { + x = 1 + } + w.uint64(x) + return b +} + +func (w *exportWriter) int64(x int64) { w.data.int64(x) } +func (w *exportWriter) uint64(x uint64) { w.data.uint64(x) } +func (w *exportWriter) string(s string) { w.uint64(w.p.stringOff(s)) } + +func (w *exportWriter) localIdent(obj types.Object) { + // Anonymous parameters. + if obj == nil { + w.string("") + return + } + + name := obj.Name() + if name == "_" { + w.string("_") + return + } + + w.string(name) +} + +type intWriter struct { + bytes.Buffer +} + +func (w *intWriter) int64(x int64) { + var buf [binary.MaxVarintLen64]byte + n := binary.PutVarint(buf[:], x) + w.Write(buf[:n]) +} + +func (w *intWriter) uint64(x uint64) { + var buf [binary.MaxVarintLen64]byte + n := binary.PutUvarint(buf[:], x) + w.Write(buf[:n]) +} + +func assert(cond bool) { + if !cond { + panic("internal error: assertion failed") + } +} + +// The below is copied from go/src/cmd/compile/internal/gc/syntax.go. + +// objQueue is a FIFO queue of types.Object. The zero value of objQueue is +// a ready-to-use empty queue. +type objQueue struct { + ring []types.Object + head, tail int +} + +// empty returns true if q contains no Nodes. +func (q *objQueue) empty() bool { + return q.head == q.tail +} + +// pushTail appends n to the tail of the queue. +func (q *objQueue) pushTail(obj types.Object) { + if len(q.ring) == 0 { + q.ring = make([]types.Object, 16) + } else if q.head+len(q.ring) == q.tail { + // Grow the ring. + nring := make([]types.Object, len(q.ring)*2) + // Copy the old elements. + part := q.ring[q.head%len(q.ring):] + if q.tail-q.head <= len(part) { + part = part[:q.tail-q.head] + copy(nring, part) + } else { + pos := copy(nring, part) + copy(nring[pos:], q.ring[:q.tail%len(q.ring)]) + } + q.ring, q.head, q.tail = nring, 0, q.tail-q.head + } + + q.ring[q.tail%len(q.ring)] = obj + q.tail++ +} + +// popHead pops a node from the head of the queue. It panics if q is empty. +func (q *objQueue) popHead() types.Object { + if q.empty() { + panic("dequeue empty") + } + obj := q.ring[q.head%len(q.ring)] + q.head++ + return obj +} diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go b/vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go index 0fd22bb03845..3cb7ae5b9ea8 100644 --- a/vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go +++ b/vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go @@ -109,7 +109,7 @@ func IImportData(fset *token.FileSet, imports map[string]*types.Package, data [] }, } - for i, pt := range predeclared { + for i, pt := range predeclared() { p.typCache[uint64(i)] = pt } @@ -142,8 +142,12 @@ func IImportData(fset *token.FileSet, imports map[string]*types.Package, data [] p.pkgIndex[pkg] = nameIndex pkgList[i] = pkg } - - localpkg := pkgList[0] + var localpkg *types.Package + for _, pkg := range pkgList { + if pkg.Path() == path { + localpkg = pkg + } + } names := make([]string, 0, len(p.pkgIndex[localpkg])) for name := range p.pkgIndex[localpkg] { @@ -330,6 +334,10 @@ func (r *importReader) value() (typ types.Type, val constant.Value) { val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) default: + if b.Kind() == types.Invalid { + val = constant.MakeUnknown() + return + } errorf("unexpected type %v", typ) // panics panic("unreachable") } diff --git a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go index eecf07fee96a..fdc7da05689b 100644 --- a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go +++ b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go @@ -86,6 +86,9 @@ func GetSizesGolist(ctx context.Context, buildFlags, env []string, dir string, u return nil, err } fields := strings.Fields(stdout.String()) + if len(fields) < 2 { + return nil, fmt.Errorf("could not determine GOARCH and Go compiler") + } goarch := fields[0] compiler := fields[1] return types.SizesFor(compiler, goarch), nil diff --git a/vendor/golang.org/x/tools/go/packages/doc.go b/vendor/golang.org/x/tools/go/packages/doc.go index e645651f1b8c..3799f8ed8be1 100644 --- a/vendor/golang.org/x/tools/go/packages/doc.go +++ b/vendor/golang.org/x/tools/go/packages/doc.go @@ -5,11 +5,6 @@ /* Package packages loads Go packages for inspection and analysis. -Note: Though this package is ready for widespread use, we may make minor -breaking changes if absolutely necessary. Any such change will be -announced on golang-tools@ at least one week before it is committed. No -more breaking changes will be made after December 1, 2018. - The Load function takes as input a list of patterns and return a list of Package structs describing individual packages matched by those patterns. The LoadMode controls the amount of detail in the loaded packages. @@ -19,22 +14,17 @@ but all patterns with the prefix "query=", where query is a non-empty string of letters from [a-z], are reserved and may be interpreted as query operators. -Only two query operators are currently supported, "file" and "pattern". +Two query operators are currently supported: "file" and "pattern". The query "file=path/to/file.go" matches the package or packages enclosing the Go source file path/to/file.go. For example "file=~/go/src/fmt/print.go" -might returns the packages "fmt" and "fmt [fmt.test]". +might return the packages "fmt" and "fmt [fmt.test]". The query "pattern=string" causes "string" to be passed directly to the underlying build tool. In most cases this is unnecessary, but an application can use Load("pattern=" + x) as an escaping mechanism to ensure that x is not interpreted as a query operator if it contains '='. -A third query "name=identifier" will be added soon. -It will match packages whose package declaration contains the specified identifier. -For example, "name=rand" would match the packages "math/rand" and "crypto/rand", -and "name=main" would match all executables. - All other query operators are reserved for future use and currently cause Load to report an error. diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go index e5551905b113..2fee7fb1f074 100644 --- a/vendor/golang.org/x/tools/go/packages/golist.go +++ b/vendor/golang.org/x/tools/go/packages/golist.go @@ -16,6 +16,7 @@ import ( "path/filepath" "reflect" "regexp" + "strconv" "strings" "sync" "time" @@ -26,7 +27,7 @@ import ( ) // debug controls verbose logging. -const debug = false +var debug, _ = strconv.ParseBool(os.Getenv("GOPACKAGESDEBUG")) // A goTooOldError reports that the go command // found by exec.LookPath is too old to use the new go list behavior. @@ -34,6 +35,42 @@ type goTooOldError struct { error } +// responseDeduper wraps a driverResponse, deduplicating its contents. +type responseDeduper struct { + seenRoots map[string]bool + seenPackages map[string]*Package + dr *driverResponse +} + +// init fills in r with a driverResponse. +func (r *responseDeduper) init(dr *driverResponse) { + r.dr = dr + r.seenRoots = map[string]bool{} + r.seenPackages = map[string]*Package{} + for _, pkg := range dr.Packages { + r.seenPackages[pkg.ID] = pkg + } + for _, root := range dr.Roots { + r.seenRoots[root] = true + } +} + +func (r *responseDeduper) addPackage(p *Package) { + if r.seenPackages[p.ID] != nil { + return + } + r.seenPackages[p.ID] = p + r.dr.Packages = append(r.dr.Packages, p) +} + +func (r *responseDeduper) addRoot(id string) { + if r.seenRoots[id] { + return + } + r.seenRoots[id] = true + r.dr.Roots = append(r.dr.Roots, id) +} + // goListDriver uses the go list command to interpret the patterns and produce // the build system package structure. // See driver for more details. @@ -67,7 +104,7 @@ extractQueries: containFiles = append(containFiles, value) case "pattern": restPatterns = append(restPatterns, value) - case "name": + case "iamashamedtousethedisabledqueryname": packagesNamed = append(packagesNamed, value) case "": // not a reserved query restPatterns = append(restPatterns, pattern) @@ -83,7 +120,6 @@ extractQueries: } } } - patterns = restPatterns // TODO(matloob): Remove the definition of listfunc and just use golistPackages once go1.12 is released. var listfunc driver @@ -99,17 +135,20 @@ extractQueries: return response, err } - var response *driverResponse + response := &responseDeduper{} var err error - // see if we have any patterns to pass through to go list. - if len(restPatterns) > 0 { - response, err = listfunc(cfg, restPatterns...) + // See if we have any patterns to pass through to go list. Zero initial + // patterns also requires a go list call, since it's the equivalent of + // ".". + if len(restPatterns) > 0 || len(patterns) == 0 { + dr, err := listfunc(cfg, restPatterns...) if err != nil { return nil, err } + response.init(dr) } else { - response = &driverResponse{} + response.init(&driverResponse{}) } sizeswg.Wait() @@ -117,38 +156,23 @@ extractQueries: return nil, sizeserr } // types.SizesFor always returns nil or a *types.StdSizes - response.Sizes, _ = sizes.(*types.StdSizes) - - seenPkgs := make(map[string]*Package) // for deduplication. different containing queries could produce same packages - for _, pkg := range response.Packages { - seenPkgs[pkg.ID] = pkg - } - addPkg := func(p *Package) { - if _, ok := seenPkgs[p.ID]; ok { - return - } - seenPkgs[p.ID] = p - response.Packages = append(response.Packages, p) - } + response.dr.Sizes, _ = sizes.(*types.StdSizes) var containsCandidates []string if len(containFiles) != 0 { - containsCandidates, err = runContainsQueries(cfg, listfunc, isFallback, addPkg, containFiles) - if err != nil { + if err := runContainsQueries(cfg, listfunc, isFallback, response, containFiles); err != nil { return nil, err } } if len(packagesNamed) != 0 { - namedResults, err := runNamedQueries(cfg, listfunc, addPkg, packagesNamed) - if err != nil { + if err := runNamedQueries(cfg, listfunc, response, packagesNamed); err != nil { return nil, err } - response.Roots = append(response.Roots, namedResults...) } - modifiedPkgs, needPkgs, err := processGolistOverlay(cfg, response) + modifiedPkgs, needPkgs, err := processGolistOverlay(cfg, response.dr) if err != nil { return nil, err } @@ -158,7 +182,7 @@ extractQueries: } if len(needPkgs) > 0 { - addNeededOverlayPackages(cfg, listfunc, addPkg, needPkgs) + addNeededOverlayPackages(cfg, listfunc, response, needPkgs) if err != nil { return nil, err } @@ -166,33 +190,32 @@ extractQueries: // Check candidate packages for containFiles. if len(containFiles) > 0 { for _, id := range containsCandidates { - pkg := seenPkgs[id] + pkg := response.seenPackages[id] for _, f := range containFiles { for _, g := range pkg.GoFiles { if sameFile(f, g) { - response.Roots = append(response.Roots, id) + response.addRoot(id) } } } } } - return response, nil + return response.dr, nil } -func addNeededOverlayPackages(cfg *Config, driver driver, addPkg func(*Package), pkgs []string) error { - response, err := driver(cfg, pkgs...) +func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDeduper, pkgs []string) error { + dr, err := driver(cfg, pkgs...) if err != nil { return err } - for _, pkg := range response.Packages { - addPkg(pkg) + for _, pkg := range dr.Packages { + response.addPackage(pkg) } return nil } -func runContainsQueries(cfg *Config, driver driver, isFallback bool, addPkg func(*Package), queries []string) ([]string, error) { - var results []string +func runContainsQueries(cfg *Config, driver driver, isFallback bool, response *responseDeduper, queries []string) error { for _, query := range queries { // TODO(matloob): Do only one query per directory. fdir := filepath.Dir(query) @@ -200,7 +223,7 @@ func runContainsQueries(cfg *Config, driver driver, isFallback bool, addPkg func // not a package path. pattern, err := filepath.Abs(fdir) if err != nil { - return nil, fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err) + return fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err) } if isFallback { pattern = "." @@ -209,7 +232,7 @@ func runContainsQueries(cfg *Config, driver driver, isFallback bool, addPkg func dirResponse, err := driver(cfg, pattern) if err != nil { - return nil, err + return err } isRoot := make(map[string]bool, len(dirResponse.Roots)) for _, root := range dirResponse.Roots { @@ -220,34 +243,34 @@ func runContainsQueries(cfg *Config, driver driver, isFallback bool, addPkg func // We don't bother to filter packages that will be dropped by the changes of roots, // that will happen anyway during graph construction outside this function. // Over-reporting packages is not a problem. - addPkg(pkg) + response.addPackage(pkg) // if the package was not a root one, it cannot have the file if !isRoot[pkg.ID] { continue } for _, pkgFile := range pkg.GoFiles { if filepath.Base(query) == filepath.Base(pkgFile) { - results = append(results, pkg.ID) + response.addRoot(pkg.ID) break } } } } - return results, nil + return nil } // modCacheRegexp splits a path in a module cache into module, module version, and package. var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`) -func runNamedQueries(cfg *Config, driver driver, addPkg func(*Package), queries []string) ([]string, error) { +func runNamedQueries(cfg *Config, driver driver, response *responseDeduper, queries []string) error { // calling `go env` isn't free; bail out if there's nothing to do. if len(queries) == 0 { - return nil, nil + return nil } // Determine which directories are relevant to scan. roots, modRoot, err := roots(cfg) if err != nil { - return nil, err + return err } // Scan the selected directories. Simple matches, from GOPATH/GOROOT @@ -260,7 +283,10 @@ func runNamedQueries(cfg *Config, driver driver, addPkg func(*Package), queries matchesMu.Lock() defer matchesMu.Unlock() - path := dir[len(root.Path)+1:] + path := dir + if dir != root.Path { + path = dir[len(root.Path)+1:] + } if pathMatchesQueries(path, queries) { switch root.Type { case gopathwalk.RootModuleCache: @@ -306,13 +332,12 @@ func runNamedQueries(cfg *Config, driver driver, addPkg func(*Package), queries } } - var results []string addResponse := func(r *driverResponse) { for _, pkg := range r.Packages { - addPkg(pkg) + response.addPackage(pkg) for _, name := range queries { if pkg.Name == name { - results = append(results, pkg.ID) + response.addRoot(pkg.ID) break } } @@ -322,7 +347,7 @@ func runNamedQueries(cfg *Config, driver driver, addPkg func(*Package), queries if len(simpleMatches) != 0 { resp, err := driver(cfg, simpleMatches...) if err != nil { - return nil, err + return err } addResponse(resp) } @@ -371,23 +396,23 @@ func runNamedQueries(cfg *Config, driver driver, addPkg func(*Package), queries var err error tmpCfg.Dir, err = ioutil.TempDir("", "gopackages-modquery") if err != nil { - return nil, err + return err } defer os.RemoveAll(tmpCfg.Dir) if err := ioutil.WriteFile(filepath.Join(tmpCfg.Dir, "go.mod"), gomod.Bytes(), 0777); err != nil { - return nil, fmt.Errorf("writing go.mod for module cache query: %v", err) + return fmt.Errorf("writing go.mod for module cache query: %v", err) } // Run the query, using the import paths calculated from the matches above. resp, err := driver(&tmpCfg, imports...) if err != nil { - return nil, fmt.Errorf("querying module cache matches: %v", err) + return fmt.Errorf("querying module cache matches: %v", err) } addResponse(resp) } - return results, nil + return nil } func getSizes(cfg *Config) (types.Sizes, error) { @@ -691,6 +716,9 @@ func golistargs(cfg *Config, words []string) []string { fmt.Sprintf("-test=%t", cfg.Tests), fmt.Sprintf("-export=%t", usesExportData(cfg)), fmt.Sprintf("-deps=%t", cfg.Mode >= LoadImports), + // go list doesn't let you pass -test and -find together, + // probably because you'd just get the TestMain. + fmt.Sprintf("-find=%t", cfg.Mode < LoadImports && !cfg.Tests), } fullargs = append(fullargs, cfg.BuildFlags...) fullargs = append(fullargs, "--") diff --git a/vendor/golang.org/x/tools/go/packages/golist_fallback.go b/vendor/golang.org/x/tools/go/packages/golist_fallback.go index 8e88607a0ec8..141fa19ac19e 100644 --- a/vendor/golang.org/x/tools/go/packages/golist_fallback.go +++ b/vendor/golang.org/x/tools/go/packages/golist_fallback.go @@ -21,7 +21,7 @@ import ( // TODO(matloob): Delete this file once Go 1.12 is released. // This file provides backwards compatibility support for -// loading for versions of Go earlier than 1.10.4. This support is meant to +// loading for versions of Go earlier than 1.11. This support is meant to // assist with migration to the Package API until there's // widespread adoption of these newer Go versions. // This support will be removed once Go 1.12 is released @@ -89,11 +89,7 @@ func golistDriverFallback(cfg *Config, words ...string) (*driverResponse, error) return "", err } } - // Add a "go-build" component to the path to make the tests think the files are in the cache. - // This allows the same test to test the pre- and post-Go 1.11 go list logic because the Go 1.11 - // go list generates test mains in the cache, and the test code knows not to rely on paths in the - // cache to stay stable. - outdir = filepath.Join(tmpdir, "go-build", strings.Replace(p.ImportPath, "/", "_", -1)) + outdir = filepath.Join(tmpdir, strings.Replace(p.ImportPath, "/", "_", -1)) if err := os.MkdirAll(outdir, 0755); err != nil { outdir = "" return "", err @@ -200,7 +196,11 @@ func golistDriverFallback(cfg *Config, words ...string) (*driverResponse, error) }) return } - testmain := filepath.Join(outdir, "testmain.go") + // Don't use a .go extension on the file, so that the tests think the file is inside GOCACHE. + // This allows the same test to test the pre- and post-Go 1.11 go list logic because the Go 1.11 + // go list generates test mains in the cache, and the test code knows not to rely on paths in the + // cache to stay stable. + testmain := filepath.Join(outdir, "testmain-go") extraimports, extradeps, err := generateTestmain(testmain, testPkg, xtestPkg) if err != nil { testmainPkg.Errors = append(testmainPkg.Errors, Error{ diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go index 15927f1d3804..f4aac56e97b4 100644 --- a/vendor/golang.org/x/tools/go/packages/packages.go +++ b/vendor/golang.org/x/tools/go/packages/packages.go @@ -52,7 +52,7 @@ const ( // LoadAllSyntax adds typed syntax trees for the packages matching the patterns // and all dependencies. - // Package fields added: Types, Fset, Illtyped, Syntax, and TypesInfo, + // Package fields added: Types, Fset, IllTyped, Syntax, and TypesInfo, // for all packages in the import graph. LoadAllSyntax ) @@ -250,6 +250,9 @@ type Package struct { // TypesInfo provides type information about the package's syntax trees. // It is set only when Syntax is set. TypesInfo *types.Info + + // TypesSizes provides the effective size function for types in TypesInfo. + TypesSizes types.Sizes } // An Error describes a problem with a package's metadata, syntax, or types. @@ -582,6 +585,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { lpkg.Fset = ld.Fset lpkg.Syntax = []*ast.File{} lpkg.TypesInfo = new(types.Info) + lpkg.TypesSizes = ld.sizes return } @@ -669,6 +673,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { Scopes: make(map[ast.Node]*types.Scope), Selections: make(map[*ast.SelectorExpr]*types.Selection), } + lpkg.TypesSizes = ld.sizes importer := importerFunc(func(path string) (*types.Package, error) { if path == "unsafe" { @@ -768,6 +773,11 @@ func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) { parsed := make([]*ast.File, n) errors := make([]error, n) for i, file := range filenames { + if ld.Config.Context.Err() != nil { + parsed[i] = nil + errors[i] = ld.Config.Context.Err() + continue + } wg.Add(1) go func(i int, filename string) { ioLimit <- true // wait diff --git a/vendor/golang.org/x/tools/imports/fix.go b/vendor/golang.org/x/tools/imports/fix.go index 8267f087c5ff..4c0339305db6 100644 --- a/vendor/golang.org/x/tools/imports/fix.go +++ b/vendor/golang.org/x/tools/imports/fix.go @@ -22,6 +22,9 @@ import ( "strconv" "strings" "sync" + "time" + "unicode" + "unicode/utf8" "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/packages" @@ -85,7 +88,7 @@ type importInfo struct { // A packageInfo represents what's known about a package. type packageInfo struct { - name string // discovered package name. + name string // real package name, if known. exports map[string]bool // known exports. } @@ -142,8 +145,8 @@ func addGlobals(f *ast.File, globals map[string]bool) { // collectReferences builds a map of selector expressions, from // left hand side (X) to a set of right hand sides (Sel). -func collectReferences(f *ast.File) map[string]map[string]bool { - refs := map[string]map[string]bool{} +func collectReferences(f *ast.File) references { + refs := references{} var visitor visitFn visitor = func(node ast.Node) ast.Visitor { @@ -157,7 +160,11 @@ func collectReferences(f *ast.File) map[string]map[string]bool { break } if xident.Obj != nil { - // if the parser can resolve it, it's not a package ref + // If the parser can resolve it, it's not a package ref. + break + } + if !ast.IsExported(v.Sel.Name) { + // Whatever this is, it's not exported from a package. break } pkgName := xident.Name @@ -166,9 +173,7 @@ func collectReferences(f *ast.File) map[string]map[string]bool { r = make(map[string]bool) refs[pkgName] = r } - if ast.IsExported(v.Sel.Name) { - r[v.Sel.Name] = true - } + r[v.Sel.Name] = true } return visitor } @@ -205,12 +210,7 @@ func (p *pass) findMissingImport(pkg string, syms map[string]bool) *importInfo { if !ok { continue } - // If the candidate import has a name, it must match pkg. - if candidate.name != "" && candidate.name != pkg { - continue - } - // Otherwise, the real name of the package must match. - if candidate.name == "" && pkgInfo.name != pkg { + if p.importIdentifier(candidate) != pkg { continue } @@ -229,6 +229,11 @@ func (p *pass) findMissingImport(pkg string, syms map[string]bool) *importInfo { return nil } +// references is set of references found in a Go file. The first map key is the +// left hand side of a selector expression, the second key is the right hand +// side, and the value should always be true. +type references map[string]map[string]bool + // A pass contains all the inputs and state necessary to fix a file's imports. // It can be modified in some ways during use; see comments below. type pass struct { @@ -236,14 +241,14 @@ type pass struct { fset *token.FileSet // fset used to parse f and its siblings. f *ast.File // the file being fixed. srcDir string // the directory containing f. - useGoPackages bool // use go/packages to load package information. + fixEnv *fixEnv // the environment to use for go commands, etc. loadRealPackageNames bool // if true, load package names from disk rather than guessing them. otherFiles []*ast.File // sibling files. // Intermediate state, generated by load. existingImports map[string]*importInfo - allRefs map[string]map[string]bool - missingRefs map[string]map[string]bool + allRefs references + missingRefs references // Inputs to fix. These can be augmented between successive fix calls. lastTry bool // indicates that this is the last call and fix should clean up as best it can. @@ -261,50 +266,32 @@ func (p *pass) loadPackageNames(imports []*importInfo) error { unknown = append(unknown, imp.importPath) } - if !p.useGoPackages || !p.loadRealPackageNames { - pathToName := importPathToNameBasic - if p.loadRealPackageNames { - pathToName = importPathToName - } - for _, path := range unknown { - p.knownPackages[path] = &packageInfo{ - name: pathToName(path, p.srcDir), - exports: map[string]bool{}, - } - } - return nil - } - - cfg := newPackagesConfig(packages.LoadFiles) - pkgs, err := packages.Load(cfg, unknown...) + names, err := p.fixEnv.getResolver().loadPackageNames(unknown, p.srcDir) if err != nil { return err } - for _, pkg := range pkgs { - p.knownPackages[VendorlessPath(pkg.PkgPath)] = &packageInfo{ - name: pkg.Name, - exports: map[string]bool{}, - } - } - // We may not have found all the packages. Guess the rest. - for _, path := range unknown { - if _, ok := p.knownPackages[path]; ok { - continue - } + + for path, name := range names { p.knownPackages[path] = &packageInfo{ - name: importPathToNameBasic(path, p.srcDir), + name: name, exports: map[string]bool{}, } } return nil } -// importIdentifier returns the indentifier that imp will introduce. +// importIdentifier returns the identifier that imp will introduce. It will +// guess if the package name has not been loaded, e.g. because the source +// is not available. func (p *pass) importIdentifier(imp *importInfo) string { if imp.name != "" { return imp.name } - return p.knownPackages[imp.importPath].name + known := p.knownPackages[imp.importPath] + if known != nil && known.name != "" { + return known.name + } + return importPathToAssumedName(imp.importPath) } // load reads in everything necessary to run a pass, and reports whether the @@ -312,7 +299,7 @@ func (p *pass) importIdentifier(imp *importInfo) string { // file's missing symbols, if any, or removes unused imports if not. func (p *pass) load() bool { p.knownPackages = map[string]*packageInfo{} - p.missingRefs = map[string]map[string]bool{} + p.missingRefs = references{} p.existingImports = map[string]*importInfo{} // Load basic information about the file in question. @@ -334,7 +321,15 @@ func (p *pass) load() bool { // Resolve all the import paths we've seen to package names, and store // f's imports by the identifier they introduce. imports := collectImports(p.f) - p.loadPackageNames(append(imports, p.candidates...)) + if p.loadRealPackageNames { + err := p.loadPackageNames(append(imports, p.candidates...)) + if err != nil { + if Debug { + log.Printf("loading package names: %v", err) + } + return false + } + } for _, imp := range imports { p.existingImports[p.importIdentifier(imp)] = imp } @@ -395,12 +390,9 @@ func (p *pass) fix() bool { continue } path := strings.Trim(imp.Path.Value, `""`) - pkg, ok := p.knownPackages[path] - if !ok { - continue - } - if pkg.name != importPathToNameBasic(path, p.srcDir) { - imp.Name = &ast.Ident{Name: pkg.name, NamePos: imp.Pos()} + ident := p.importIdentifier(&importInfo{importPath: path}) + if ident != importPathToAssumedName(path) { + imp.Name = &ast.Ident{Name: ident, NamePos: imp.Pos()} } } } @@ -456,7 +448,7 @@ func (p *pass) addCandidate(imp *importInfo, pkg *packageInfo) { // easily be extended by adding a file with an init function. var fixImports = fixImportsDefault -func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string) error { +func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string, env *fixEnv) error { abs, err := filepath.Abs(filename) if err != nil { return err @@ -479,7 +471,6 @@ func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string) error // Second pass: add information from other files in the same package, // like their package vars and imports. - p = &pass{fset: fset, f: f, srcDir: srcDir} p.otherFiles = otherFiles if p.load() { return nil @@ -492,13 +483,10 @@ func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string) error return nil } - // The only things that use go/packages happen in the third pass, - // so we can delay calling go env until this point. - useGoPackages := shouldUseGoPackages() - // Third pass: get real package names where we had previously used - // the naive algorithm. - p = &pass{fset: fset, f: f, srcDir: srcDir, useGoPackages: useGoPackages} + // the naive algorithm. This is the first step that will use the + // environment, so we provide it here for the first time. + p = &pass{fset: fset, f: f, srcDir: srcDir, fixEnv: env} p.loadRealPackageNames = true p.otherFiles = otherFiles if p.load() { @@ -522,38 +510,98 @@ func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string) error return nil } -// Values controlling the use of go/packages, for testing only. -var forceGoPackages, _ = strconv.ParseBool(os.Getenv("GOIMPORTSFORCEGOPACKAGES")) -var goPackagesDir string -var go111ModuleEnv string +// fixEnv contains environment variables and settings that affect the use of +// the go command, the go/build package, etc. +type fixEnv struct { + // If non-empty, these will be used instead of the + // process-wide values. + GOPATH, GOROOT, GO111MODULE, GOPROXY, GOFLAGS string + WorkingDir string -func shouldUseGoPackages() bool { - if forceGoPackages { - return true + // If true, use go/packages regardless of the environment. + ForceGoPackages bool + + resolver resolver +} + +func (e *fixEnv) env() []string { + env := os.Environ() + add := func(k, v string) { + if v != "" { + env = append(env, k+"="+v) + } + } + add("GOPATH", e.GOPATH) + add("GOROOT", e.GOROOT) + add("GO111MODULE", e.GO111MODULE) + add("GOPROXY", e.GOPROXY) + add("GOFLAGS", e.GOFLAGS) + if e.WorkingDir != "" { + add("PWD", e.WorkingDir) } + return env +} - cmd := exec.Command("go", "env", "GOMOD") - cmd.Dir = goPackagesDir - out, err := cmd.Output() - if err != nil { - return false +func (e *fixEnv) getResolver() resolver { + if e.resolver != nil { + return e.resolver + } + if e.ForceGoPackages { + return &goPackagesResolver{env: e} + } + + out, err := e.invokeGo("env", "GOMOD") + if err != nil || len(bytes.TrimSpace(out.Bytes())) == 0 { + return &gopathResolver{env: e} } - return len(bytes.TrimSpace(out)) > 0 + return &moduleResolver{env: e} } -func newPackagesConfig(mode packages.LoadMode) *packages.Config { - cfg := &packages.Config{ +func (e *fixEnv) newPackagesConfig(mode packages.LoadMode) *packages.Config { + return &packages.Config{ Mode: mode, - Dir: goPackagesDir, - Env: append(os.Environ(), "GOROOT="+build.Default.GOROOT, "GOPATH="+build.Default.GOPATH), + Dir: e.WorkingDir, + Env: e.env(), + } +} + +func (e *fixEnv) buildContext() *build.Context { + ctx := build.Default + ctx.GOROOT = e.GOROOT + ctx.GOPATH = e.GOPATH + return &ctx +} + +func (e *fixEnv) invokeGo(args ...string) (*bytes.Buffer, error) { + cmd := exec.Command("go", args...) + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + cmd.Stdout = stdout + cmd.Stderr = stderr + cmd.Env = e.env() + cmd.Dir = e.WorkingDir + + if Debug { + defer func(start time.Time) { log.Printf("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now()) } - if go111ModuleEnv != "" { - cfg.Env = append(cfg.Env, "GO111MODULE="+go111ModuleEnv) + if err := cmd.Run(); err != nil { + return nil, fmt.Errorf("running go: %v (stderr:\n%s)", err, stderr) } - return cfg + return stdout, nil +} + +func cmdDebugStr(cmd *exec.Cmd) string { + env := make(map[string]string) + for _, kv := range cmd.Env { + split := strings.Split(kv, "=") + k, v := split[0], split[1] + env[k] = v + } + + return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v go %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], cmd.Args) } -func addStdlibCandidates(pass *pass, refs map[string]map[string]bool) { +func addStdlibCandidates(pass *pass, refs references) { add := func(pkg string) { pass.addCandidate( &importInfo{importPath: pkg}, @@ -574,13 +622,47 @@ func addStdlibCandidates(pass *pass, refs map[string]map[string]bool) { } } -func scanGoPackages(refs map[string]map[string]bool) ([]*pkg, error) { +// A resolver does the build-system-specific parts of goimports. +type resolver interface { + // loadPackageNames loads the package names in importPaths. + loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) + // scan finds (at least) the packages satisfying refs. The returned slice is unordered. + scan(refs references) ([]*pkg, error) +} + +// gopathResolver implements resolver for GOPATH and module workspaces using go/packages. +type goPackagesResolver struct { + env *fixEnv +} + +func (r *goPackagesResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) { + cfg := r.env.newPackagesConfig(packages.LoadFiles) + pkgs, err := packages.Load(cfg, importPaths...) + if err != nil { + return nil, err + } + names := map[string]string{} + for _, pkg := range pkgs { + names[VendorlessPath(pkg.PkgPath)] = pkg.Name + } + // We may not have found all the packages. Guess the rest. + for _, path := range importPaths { + if _, ok := names[path]; ok { + continue + } + names[path] = importPathToAssumedName(path) + } + return names, nil + +} + +func (r *goPackagesResolver) scan(refs references) ([]*pkg, error) { var loadQueries []string for pkgName := range refs { - loadQueries = append(loadQueries, "name="+pkgName) + loadQueries = append(loadQueries, "iamashamedtousethedisabledqueryname="+pkgName) } sort.Strings(loadQueries) - cfg := newPackagesConfig(packages.LoadTypes) + cfg := r.env.newPackagesConfig(packages.LoadFiles) goPackages, err := packages.Load(cfg, loadQueries...) if err != nil { return nil, err @@ -597,18 +679,10 @@ func scanGoPackages(refs map[string]map[string]bool) ([]*pkg, error) { return scan, nil } -var addExternalCandidates = addExternalCandidatesDefault - -func addExternalCandidatesDefault(pass *pass, refs map[string]map[string]bool, filename string) error { - var dirScan []*pkg - if pass.useGoPackages { - var err error - dirScan, err = scanGoPackages(refs) - if err != nil { - return err - } - } else { - dirScan = scanGoDirs() +func addExternalCandidates(pass *pass, refs references, filename string) error { + dirScan, err := pass.fixEnv.getResolver().scan(refs) + if err != nil { + return err } // Search for imports matching potential package references. @@ -633,7 +707,7 @@ func addExternalCandidatesDefault(pass *pass, refs map[string]map[string]bool, f go func(pkgName string, symbols map[string]bool) { defer wg.Done() - found, err := findImport(ctx, dirScan, pkgName, symbols, filename) + found, err := findImport(ctx, pass.fixEnv, dirScan, pkgName, symbols, filename) if err != nil { firstErrOnce.Do(func() { @@ -669,49 +743,77 @@ func addExternalCandidatesDefault(pass *pass, refs map[string]map[string]bool, f return firstErr } -// importPathToNameBasic assumes the package name is the base of import path, -// except that if the path ends in foo/vN, it assumes the package name is foo. -func importPathToNameBasic(importPath, srcDir string) (packageName string) { +// notIdentifier reports whether ch is an invalid identifier character. +func notIdentifier(ch rune) bool { + return !('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || + '0' <= ch && ch <= '9' || + ch == '_' || + ch >= utf8.RuneSelf && (unicode.IsLetter(ch) || unicode.IsDigit(ch))) +} + +// importPathToAssumedName returns the assumed package name of an import path. +// It does this using only string parsing of the import path. +// It picks the last element of the path that does not look like a major +// version, and then picks the valid identifier off the start of that element. +// It is used to determine if a local rename should be added to an import for +// clarity. +// This function could be moved to a standard package and exported if we want +// for use in other tools. +func importPathToAssumedName(importPath string) string { base := path.Base(importPath) if strings.HasPrefix(base, "v") { if _, err := strconv.Atoi(base[1:]); err == nil { dir := path.Dir(importPath) if dir != "." { - return path.Base(dir) + base = path.Base(dir) } } } + base = strings.TrimPrefix(base, "go-") + if i := strings.IndexFunc(base, notIdentifier); i >= 0 { + base = base[:i] + } return base } +// gopathResolver implements resolver for GOPATH workspaces. +type gopathResolver struct { + env *fixEnv +} + +func (r *gopathResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) { + names := map[string]string{} + for _, path := range importPaths { + names[path] = importPathToName(r.env, path, srcDir) + } + return names, nil +} + // importPathToNameGoPath finds out the actual package name, as declared in its .go files. -// If there's a problem, it falls back to using importPathToNameBasic. -func importPathToName(importPath, srcDir string) (packageName string) { +// If there's a problem, it returns "". +func importPathToName(env *fixEnv, importPath, srcDir string) (packageName string) { // Fast path for standard library without going to disk. if _, ok := stdlib[importPath]; ok { return path.Base(importPath) // stdlib packages always match their paths. } - pkgName, err := importPathToNameGoPathParse(importPath, srcDir) - if Debug { - log.Printf("importPathToNameGoPathParse(%q, srcDir=%q) = %q, %v", importPath, srcDir, pkgName, err) + buildPkg, err := env.buildContext().Import(importPath, srcDir, build.FindOnly) + if err != nil { + return "" } - if err == nil { - return pkgName + pkgName, err := packageDirToName(buildPkg.Dir) + if err != nil { + return "" } - return importPathToNameBasic(importPath, srcDir) + return pkgName } -// importPathToNameGoPathParse is a faster version of build.Import if +// packageDirToName is a faster version of build.Import if // the only thing desired is the package name. It uses build.FindOnly // to find the directory and then only parses one file in the package, // trusting that the files in the directory are consistent. -func importPathToNameGoPathParse(importPath, srcDir string) (packageName string, err error) { - buildPkg, err := build.Import(importPath, srcDir, build.FindOnly) - if err != nil { - return "", err - } - d, err := os.Open(buildPkg.Dir) +func packageDirToName(dir string) (packageName string, err error) { + d, err := os.Open(dir) if err != nil { return "", err } @@ -731,7 +833,7 @@ func importPathToNameGoPathParse(importPath, srcDir string) (packageName string, continue } nfile++ - fullFile := filepath.Join(buildPkg.Dir, name) + fullFile := filepath.Join(dir, name) fset := token.NewFileSet() f, err := parser.ParseFile(fset, fullFile, nil, parser.PackageClauseOnly) @@ -805,8 +907,7 @@ func distance(basepath, targetpath string) int { return strings.Count(p, string(filepath.Separator)) + 1 } -// scanGoDirs populates the dirScan map for GOPATH and GOROOT. -func scanGoDirs() []*pkg { +func (r *gopathResolver) scan(_ references) ([]*pkg, error) { dupCheck := make(map[string]bool) var result []*pkg @@ -826,8 +927,8 @@ func scanGoDirs() []*pkg { dir: dir, }) } - gopathwalk.Walk(gopathwalk.SrcDirsRoots(), add, gopathwalk.Options{Debug: Debug, ModulesEnabled: false}) - return result + gopathwalk.Walk(gopathwalk.SrcDirsRoots(r.env.buildContext()), add, gopathwalk.Options{Debug: Debug, ModulesEnabled: false}) + return result, nil } // VendorlessPath returns the devendorized version of the import path ipath. @@ -845,50 +946,52 @@ func VendorlessPath(ipath string) string { // loadExports returns the set of exported symbols in the package at dir. // It returns nil on error or if the package name in dir does not match expectPackage. -func loadExports(ctx context.Context, expectPackage string, pkg *pkg) (map[string]bool, error) { +func loadExports(ctx context.Context, env *fixEnv, expectPackage string, pkg *pkg) (map[string]bool, error) { + if Debug { + log.Printf("loading exports in dir %s (seeking package %s)", pkg.dir, expectPackage) + } if pkg.goPackage != nil { exports := map[string]bool{} - for _, name := range pkg.goPackage.Types.Scope().Names() { - if ast.IsExported(name) { - exports[name] = true + fset := token.NewFileSet() + for _, fname := range pkg.goPackage.CompiledGoFiles { + f, err := parser.ParseFile(fset, fname, nil, 0) + if err != nil { + return nil, fmt.Errorf("parsing %s: %v", fname, err) + } + for name := range f.Scope.Objects { + if ast.IsExported(name) { + exports[name] = true + } } } return exports, nil } - if Debug { - log.Printf("loading exports in dir %s (seeking package %s)", pkg.dir, expectPackage) - } exports := make(map[string]bool) - buildCtx := build.Default - - // ReadDir is like ioutil.ReadDir, but only returns *.go files - // and filters out _test.go files since they're not relevant - // and only slow things down. - buildCtx.ReadDir = func(dir string) (notTests []os.FileInfo, err error) { - all, err := ioutil.ReadDir(dir) - if err != nil { - return nil, err + // Look for non-test, buildable .go files which could provide exports. + all, err := ioutil.ReadDir(pkg.dir) + if err != nil { + return nil, err + } + var files []os.FileInfo + for _, fi := range all { + name := fi.Name() + if !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") { + continue } - notTests = all[:0] - for _, fi := range all { - name := fi.Name() - if strings.HasSuffix(name, ".go") && !strings.HasSuffix(name, "_test.go") { - notTests = append(notTests, fi) - } + match, err := env.buildContext().MatchFile(pkg.dir, fi.Name()) + if err != nil || !match { + continue } - return notTests, nil + files = append(files, fi) } - files, err := buildCtx.ReadDir(pkg.dir) - if err != nil { - log.Print(err) - return nil, err + if len(files) == 0 { + return nil, fmt.Errorf("dir %v contains no buildable, non-test .go files", pkg.dir) } fset := token.NewFileSet() - for _, fi := range files { select { case <-ctx.Done(): @@ -896,30 +999,19 @@ func loadExports(ctx context.Context, expectPackage string, pkg *pkg) (map[strin default: } - match, err := buildCtx.MatchFile(pkg.dir, fi.Name()) - if err != nil || !match { - continue - } fullFile := filepath.Join(pkg.dir, fi.Name()) f, err := parser.ParseFile(fset, fullFile, nil, 0) if err != nil { - if Debug { - log.Printf("Parsing %s: %v", fullFile, err) - } - return nil, err + return nil, fmt.Errorf("parsing %s: %v", fullFile, err) } pkgName := f.Name.Name if pkgName == "documentation" { // Special case from go/build.ImportDir, not - // handled by buildCtx.MatchFile. + // handled by MatchFile above. continue } if pkgName != expectPackage { - err := fmt.Errorf("scan of dir %v is not expected package %v (actually %v)", pkg.dir, expectPackage, pkgName) - if Debug { - log.Print(err) - } - return nil, err + return nil, fmt.Errorf("scan of dir %v is not expected package %v (actually %v)", pkg.dir, expectPackage, pkgName) } for name := range f.Scope.Objects { if ast.IsExported(name) { @@ -941,7 +1033,7 @@ func loadExports(ctx context.Context, expectPackage string, pkg *pkg) (map[strin // findImport searches for a package with the given symbols. // If no package is found, findImport returns ("", false, nil) -func findImport(ctx context.Context, dirScan []*pkg, pkgName string, symbols map[string]bool, filename string) (*pkg, error) { +func findImport(ctx context.Context, env *fixEnv, dirScan []*pkg, pkgName string, symbols map[string]bool, filename string) (*pkg, error) { pkgDir, err := filepath.Abs(filename) if err != nil { return nil, err @@ -1003,8 +1095,11 @@ func findImport(ctx context.Context, dirScan []*pkg, pkgName string, symbols map wg.Done() }() - exports, err := loadExports(ctx, pkgName, c.pkg) + exports, err := loadExports(ctx, env, pkgName, c.pkg) if err != nil { + if Debug { + log.Printf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err) + } resc <- nil return } diff --git a/vendor/golang.org/x/tools/imports/imports.go b/vendor/golang.org/x/tools/imports/imports.go index 717a6f3aa0da..07101cb8048d 100644 --- a/vendor/golang.org/x/tools/imports/imports.go +++ b/vendor/golang.org/x/tools/imports/imports.go @@ -13,6 +13,7 @@ import ( "bytes" "fmt" "go/ast" + "go/build" "go/format" "go/parser" "go/printer" @@ -45,6 +46,11 @@ type Options struct { // so it is important that filename be accurate. // To process data ``as if'' it were in filename, pass the data as a non-nil src. func Process(filename string, src []byte, opt *Options) ([]byte, error) { + env := &fixEnv{GOPATH: build.Default.GOPATH, GOROOT: build.Default.GOROOT} + return process(filename, src, opt, env) +} + +func process(filename string, src []byte, opt *Options, env *fixEnv) ([]byte, error) { if opt == nil { opt = &Options{Comments: true, TabIndent: true, TabWidth: 8} } @@ -63,7 +69,7 @@ func Process(filename string, src []byte, opt *Options) ([]byte, error) { } if !opt.FormatOnly { - if err := fixImports(fileSet, file, filename); err != nil { + if err := fixImports(fileSet, file, filename, env); err != nil { return nil, err } } diff --git a/vendor/golang.org/x/tools/imports/mkstdlib.go b/vendor/golang.org/x/tools/imports/mkstdlib.go index 5e53378fc8ba..5059ad4d7d3f 100644 --- a/vendor/golang.org/x/tools/imports/mkstdlib.go +++ b/vendor/golang.org/x/tools/imports/mkstdlib.go @@ -58,6 +58,7 @@ func main() { mustOpen(api("go1.9.txt")), mustOpen(api("go1.10.txt")), mustOpen(api("go1.11.txt")), + mustOpen(api("go1.12.txt")), ) sc := bufio.NewScanner(f) diff --git a/vendor/golang.org/x/tools/imports/mod.go b/vendor/golang.org/x/tools/imports/mod.go new file mode 100644 index 000000000000..ec769145cce0 --- /dev/null +++ b/vendor/golang.org/x/tools/imports/mod.go @@ -0,0 +1,351 @@ +package imports + +import ( + "bytes" + "encoding/json" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "regexp" + "sort" + "strconv" + "strings" + "sync" + "time" + + "golang.org/x/tools/internal/gopathwalk" + "golang.org/x/tools/internal/module" +) + +// moduleResolver implements resolver for modules using the go command as little +// as feasible. +type moduleResolver struct { + env *fixEnv + + main *moduleJSON + modsByModPath []*moduleJSON // All modules, ordered by # of path components in module Path... + modsByDir []*moduleJSON // ...or Dir. +} + +type moduleJSON struct { + Path string // module path + Version string // module version + Versions []string // available module versions (with -versions) + Replace *moduleJSON // replaced by this module + Time *time.Time // time version was created + Update *moduleJSON // available update, if any (with -u) + Main bool // is this the main module? + Indirect bool // is this module only an indirect dependency of main module? + Dir string // directory holding files for this module, if any + GoMod string // path to go.mod file for this module, if any + Error *moduleErrorJSON // error loading module +} + +type moduleErrorJSON struct { + Err string // the error itself +} + +func (r *moduleResolver) init() error { + if r.main != nil { + return nil + } + stdout, err := r.env.invokeGo("list", "-m", "-json", "...") + if err != nil { + return err + } + for dec := json.NewDecoder(stdout); dec.More(); { + mod := &moduleJSON{} + if err := dec.Decode(mod); err != nil { + return err + } + if mod.Dir == "" { + if Debug { + log.Printf("module %v has not been downloaded and will be ignored", mod.Path) + } + // Can't do anything with a module that's not downloaded. + continue + } + r.modsByModPath = append(r.modsByModPath, mod) + r.modsByDir = append(r.modsByDir, mod) + if mod.Main { + r.main = 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 + }) + + return nil +} + +// 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) (*moduleJSON, string) { + for _, m := range r.modsByModPath { + if !strings.HasPrefix(importPath, m.Path) { + continue + } + pathInModule := importPath[len(m.Path):] + pkgDir := filepath.Join(m.Dir, pathInModule) + if dirIsNestedModule(pkgDir, m) { + continue + } + + 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.buildContext().MatchFile(pkgDir, fi.Name()); ok { + return m, pkgDir + } + } + } + return nil, "" +} + +// findModuleByDir returns the module that contains dir, or nil if no such +// module is in scope. +func (r *moduleResolver) findModuleByDir(dir string) *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 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 dirIsNestedModule(dir string, mod *moduleJSON) bool { + if !strings.HasPrefix(dir, mod.Dir) { + return false + } + mf := findModFile(dir) + if mf == "" { + return false + } + return filepath.Dir(mf) != mod.Dir +} + +func findModFile(dir string) string { + for { + f := filepath.Join(dir, "go.mod") + info, err := os.Stat(f) + if err == nil && !info.IsDir() { + return f + } + d := filepath.Dir(dir) + if len(d) >= len(dir) { + return "" // reached top of file system, no go.mod + } + dir = d + } +} + +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(_ references) ([]*pkg, error) { + if err := r.init(); err != nil { + return nil, err + } + + // Walk GOROOT, GOPATH/pkg/mod, and the main module. + roots := []gopathwalk.Root{ + {filepath.Join(r.env.GOROOT, "/src"), gopathwalk.RootGOROOT}, + {r.main.Dir, gopathwalk.RootCurrentModule}, + } + for _, p := range filepath.SplitList(r.env.GOPATH) { + roots = append(roots, gopathwalk.Root{filepath.Join(p, "/pkg/mod"), gopathwalk.RootModuleCache}) + } + + // Walk replace targets, just in case they're not in any of the above. + for _, mod := range r.modsByModPath { + if mod.Replace != nil { + roots = append(roots, gopathwalk.Root{mod.Dir, gopathwalk.RootOther}) + } + } + + var result []*pkg + dupCheck := make(map[string]bool) + var mu sync.Mutex + + gopathwalk.Walk(roots, func(root gopathwalk.Root, dir string) { + mu.Lock() + defer mu.Unlock() + + if _, dup := dupCheck[dir]; dup { + return + } + + dupCheck[dir] = true + + subdir := "" + if dir != root.Path { + subdir = dir[len(root.Path)+len("/"):] + } + importPath := filepath.ToSlash(subdir) + if strings.HasPrefix(importPath, "vendor/") { + // Ignore vendor dirs. If -mod=vendor is on, then things + // should mostly just work, but when it's not vendor/ + // is a mess. There's no easy way to tell if it's on. + // We can still find things in the mod cache and + // map them into /vendor when -mod=vendor is on. + return + } + switch root.Type { + case gopathwalk.RootCurrentModule: + importPath = path.Join(r.main.Path, filepath.ToSlash(subdir)) + case gopathwalk.RootModuleCache: + matches := modCacheRegexp.FindStringSubmatch(subdir) + modPath, err := module.DecodePath(filepath.ToSlash(matches[1])) + if err != nil { + if Debug { + log.Printf("decoding module cache path %q: %v", subdir, err) + } + return + } + importPath = path.Join(modPath, filepath.ToSlash(matches[3])) + case gopathwalk.RootGOROOT: + importPath = subdir + } + + // Check if the directory is underneath a module that's in scope. + if mod := r.findModuleByDir(dir); 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 == dir { + importPath = mod.Path + } else { + dirInMod := dir[len(mod.Dir)+len("/"):] + importPath = path.Join(mod.Path, filepath.ToSlash(dirInMod)) + } + } else { + // The package is in an unknown module. Check that it's + // not obviously impossible to import. + var modFile string + switch root.Type { + case gopathwalk.RootModuleCache: + matches := modCacheRegexp.FindStringSubmatch(subdir) + modFile = filepath.Join(matches[1], "@", matches[2], "go.mod") + default: + modFile = findModFile(dir) + } + + modBytes, err := ioutil.ReadFile(modFile) + if err == nil && !strings.HasPrefix(importPath, modulePath(modBytes)) { + // The module's declared path does not match + // its expected path. It probably needs a + // replace directive we don't have. + return + } + } + // 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 != "" { + dir = canonicalDir + } + + result = append(result, &pkg{ + importPathShort: VendorlessPath(importPath), + dir: dir, + }) + }, gopathwalk.Options{Debug: Debug, ModulesEnabled: true}) + return result, nil +} + +// 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 +} diff --git a/vendor/golang.org/x/tools/imports/zstdlib.go b/vendor/golang.org/x/tools/imports/zstdlib.go index ca4f0b2f2190..c18a0095b202 100644 --- a/vendor/golang.org/x/tools/imports/zstdlib.go +++ b/vendor/golang.org/x/tools/imports/zstdlib.go @@ -112,6 +112,7 @@ var stdlib = map[string]map[string]bool{ "Reader": true, "Repeat": true, "Replace": true, + "ReplaceAll": true, "Runes": true, "Split": true, "SplitAfter": true, @@ -441,6 +442,9 @@ var stdlib = map[string]map[string]bool{ "RequireAnyClientCert": true, "Server": true, "SignatureScheme": true, + "TLS_AES_128_GCM_SHA256": true, + "TLS_AES_256_GCM_SHA384": true, + "TLS_CHACHA20_POLY1305_SHA256": true, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": true, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": true, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": true, @@ -469,6 +473,7 @@ var stdlib = map[string]map[string]bool{ "VersionTLS10": true, "VersionTLS11": true, "VersionTLS12": true, + "VersionTLS13": true, "X25519": true, "X509KeyPair": true, }, @@ -1835,6 +1840,7 @@ var stdlib = map[string]map[string]bool{ "R_PPC_UADDR32": true, "R_RISCV": true, "R_RISCV_32": true, + "R_RISCV_32_PCREL": true, "R_RISCV_64": true, "R_RISCV_ADD16": true, "R_RISCV_ADD32": true, @@ -2260,6 +2266,7 @@ var stdlib = map[string]map[string]bool{ "IMAGE_FILE_MACHINE_AMD64": true, "IMAGE_FILE_MACHINE_ARM": true, "IMAGE_FILE_MACHINE_ARM64": true, + "IMAGE_FILE_MACHINE_ARMNT": true, "IMAGE_FILE_MACHINE_EBC": true, "IMAGE_FILE_MACHINE_I386": true, "IMAGE_FILE_MACHINE_IA64": true, @@ -2753,6 +2760,7 @@ var stdlib = map[string]map[string]bool{ "New": true, "Note": true, "Package": true, + "PreserveAST": true, "Synopsis": true, "ToHTML": true, "ToText": true, @@ -2764,9 +2772,10 @@ var stdlib = map[string]map[string]bool{ "Source": true, }, "go/importer": map[string]bool{ - "Default": true, - "For": true, - "Lookup": true, + "Default": true, + "For": true, + "ForCompiler": true, + "Lookup": true, }, "go/parser": map[string]bool{ "AllErrors": true, @@ -3296,6 +3305,7 @@ var stdlib = map[string]map[string]bool{ "SeekEnd": true, "SeekStart": true, "Seeker": true, + "StringWriter": true, "TeeReader": true, "WriteCloser": true, "WriteSeeker": true, @@ -3498,6 +3508,12 @@ var stdlib = map[string]map[string]bool{ "Word": true, }, "math/bits": map[string]bool{ + "Add": true, + "Add32": true, + "Add64": true, + "Div": true, + "Div32": true, + "Div64": true, "LeadingZeros": true, "LeadingZeros16": true, "LeadingZeros32": true, @@ -3508,6 +3524,9 @@ var stdlib = map[string]map[string]bool{ "Len32": true, "Len64": true, "Len8": true, + "Mul": true, + "Mul32": true, + "Mul64": true, "OnesCount": true, "OnesCount16": true, "OnesCount32": true, @@ -3527,6 +3546,9 @@ var stdlib = map[string]map[string]bool{ "RotateLeft32": true, "RotateLeft64": true, "RotateLeft8": true, + "Sub": true, + "Sub32": true, + "Sub64": true, "TrailingZeros": true, "TrailingZeros16": true, "TrailingZeros32": true, @@ -3870,6 +3892,7 @@ var stdlib = map[string]map[string]bool{ "StatusTeapot": true, "StatusTemporaryRedirect": true, "StatusText": true, + "StatusTooEarly": true, "StatusTooManyRequests": true, "StatusUnauthorized": true, "StatusUnavailableForLegalReasons": true, @@ -4140,6 +4163,7 @@ var stdlib = map[string]map[string]bool{ "Truncate": true, "Unsetenv": true, "UserCacheDir": true, + "UserHomeDir": true, }, "os/exec": map[string]bool{ "Cmd": true, @@ -4244,6 +4268,7 @@ var stdlib = map[string]map[string]bool{ "MakeMapWithSize": true, "MakeSlice": true, "Map": true, + "MapIter": true, "MapOf": true, "Method": true, "New": true, @@ -4419,9 +4444,12 @@ var stdlib = map[string]map[string]bool{ "Version": true, }, "runtime/debug": map[string]bool{ + "BuildInfo": true, "FreeOSMemory": true, "GCStats": true, + "Module": true, "PrintStack": true, + "ReadBuildInfo": true, "ReadGCStats": true, "SetGCPercent": true, "SetMaxStack": true, @@ -4547,6 +4575,7 @@ var stdlib = map[string]map[string]bool{ "Reader": true, "Repeat": true, "Replace": true, + "ReplaceAll": true, "Replacer": true, "Split": true, "SplitAfter": true, @@ -6105,6 +6134,7 @@ var stdlib = map[string]map[string]bool{ "FreeLibrary": true, "Fsid": true, "Fstat": true, + "Fstatat": true, "Fstatfs": true, "Fstore_t": true, "Fsync": true, @@ -9362,6 +9392,7 @@ var stdlib = map[string]map[string]bool{ "Syscall": true, "Syscall12": true, "Syscall15": true, + "Syscall18": true, "Syscall6": true, "Syscall9": true, "Sysctl": true, @@ -9630,6 +9661,7 @@ var stdlib = map[string]map[string]bool{ "TransmitFile": true, "TransmitFileBuffers": true, "Truncate": true, + "UNIX_PATH_MAX": true, "USAGE_MATCH_TYPE_AND": true, "USAGE_MATCH_TYPE_OR": true, "UTF16FromString": true, diff --git a/vendor/golang.org/x/tools/internal/gopathwalk/walk.go b/vendor/golang.org/x/tools/internal/gopathwalk/walk.go index a561f9f4148e..04bb96a3627c 100644 --- a/vendor/golang.org/x/tools/internal/gopathwalk/walk.go +++ b/vendor/golang.org/x/tools/internal/gopathwalk/walk.go @@ -35,6 +35,7 @@ const ( RootGOPATH RootCurrentModule RootModuleCache + RootOther ) // A Root is a starting point for a Walk. @@ -44,10 +45,10 @@ type Root struct { } // SrcDirsRoots returns the roots from build.Default.SrcDirs(). Not modules-compatible. -func SrcDirsRoots() []Root { +func SrcDirsRoots(ctx *build.Context) []Root { var roots []Root - roots = append(roots, Root{filepath.Join(build.Default.GOROOT, "src"), RootGOROOT}) - for _, p := range filepath.SplitList(build.Default.GOPATH) { + roots = append(roots, Root{filepath.Join(ctx.GOROOT, "src"), RootGOROOT}) + for _, p := range filepath.SplitList(ctx.GOPATH) { roots = append(roots, Root{filepath.Join(p, "src"), RootGOPATH}) } return roots @@ -162,7 +163,7 @@ func (w *walker) shouldSkipDir(fi os.FileInfo) bool { func (w *walker) walk(path string, typ os.FileMode) error { dir := filepath.Dir(path) if typ.IsRegular() { - if dir == w.root.Path { + if dir == w.root.Path && (w.root.Type == RootGOROOT || w.root.Type == RootGOPATH) { // Doesn't make sense to have regular files // directly in your $GOPATH/src or $GOROOT/src. return fastwalk.SkipFiles diff --git a/vendor/golang.org/x/tools/internal/module/module.go b/vendor/golang.org/x/tools/internal/module/module.go new file mode 100644 index 000000000000..9a4edb9dec15 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/module/module.go @@ -0,0 +1,540 @@ +// Copyright 2018 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 module defines the module.Version type +// along with support code. +package module + +// IMPORTANT NOTE +// +// This file essentially defines the set of valid import paths for the go command. +// There are many subtle considerations, including Unicode ambiguity, +// security, network, and file system representations. +// +// This file also defines the set of valid module path and version combinations, +// another topic with many subtle considerations. +// +// Changes to the semantics in this file require approval from rsc. + +import ( + "fmt" + "sort" + "strings" + "unicode" + "unicode/utf8" + + "golang.org/x/tools/internal/semver" +) + +// A Version is defined by a module path and version pair. +type Version struct { + Path string + + // Version is usually a semantic version in canonical form. + // There are two exceptions to this general rule. + // First, the top-level target of a build has no specific version + // and uses Version = "". + // Second, during MVS calculations the version "none" is used + // to represent the decision to take no version of a given module. + Version string `json:",omitempty"` +} + +// Check checks that a given module path, version pair is valid. +// In addition to the path being a valid module path +// and the version being a valid semantic version, +// the two must correspond. +// For example, the path "yaml/v2" only corresponds to +// semantic versions beginning with "v2.". +func Check(path, version string) error { + if err := CheckPath(path); err != nil { + return err + } + if !semver.IsValid(version) { + return fmt.Errorf("malformed semantic version %v", version) + } + _, pathMajor, _ := SplitPathVersion(path) + if !MatchPathMajor(version, pathMajor) { + if pathMajor == "" { + pathMajor = "v0 or v1" + } + if pathMajor[0] == '.' { // .v1 + pathMajor = pathMajor[1:] + } + return fmt.Errorf("mismatched module path %v and version %v (want %v)", path, version, pathMajor) + } + return nil +} + +// firstPathOK reports whether r can appear in the first element of a module path. +// The first element of the path must be an LDH domain name, at least for now. +// To avoid case ambiguity, the domain name must be entirely lower case. +func firstPathOK(r rune) bool { + return r == '-' || r == '.' || + '0' <= r && r <= '9' || + 'a' <= r && r <= 'z' +} + +// pathOK reports whether r can appear in an import path element. +// Paths can be ASCII letters, ASCII digits, and limited ASCII punctuation: + - . _ and ~. +// This matches what "go get" has historically recognized in import paths. +// TODO(rsc): We would like to allow Unicode letters, but that requires additional +// care in the safe encoding (see note below). +func pathOK(r rune) bool { + if r < utf8.RuneSelf { + return r == '+' || r == '-' || r == '.' || r == '_' || r == '~' || + '0' <= r && r <= '9' || + 'A' <= r && r <= 'Z' || + 'a' <= r && r <= 'z' + } + return false +} + +// fileNameOK reports whether r can appear in a file name. +// For now we allow all Unicode letters but otherwise limit to pathOK plus a few more punctuation characters. +// If we expand the set of allowed characters here, we have to +// work harder at detecting potential case-folding and normalization collisions. +// See note about "safe encoding" below. +func fileNameOK(r rune) bool { + if r < utf8.RuneSelf { + // Entire set of ASCII punctuation, from which we remove characters: + // ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~ + // We disallow some shell special characters: " ' * < > ? ` | + // (Note that some of those are disallowed by the Windows file system as well.) + // We also disallow path separators / : and \ (fileNameOK is only called on path element characters). + // We allow spaces (U+0020) in file names. + const allowed = "!#$%&()+,-.=@[]^_{}~ " + if '0' <= r && r <= '9' || 'A' <= r && r <= 'Z' || 'a' <= r && r <= 'z' { + return true + } + for i := 0; i < len(allowed); i++ { + if rune(allowed[i]) == r { + return true + } + } + return false + } + // It may be OK to add more ASCII punctuation here, but only carefully. + // For example Windows disallows < > \, and macOS disallows :, so we must not allow those. + return unicode.IsLetter(r) +} + +// CheckPath checks that a module path is valid. +func CheckPath(path string) error { + if err := checkPath(path, false); err != nil { + return fmt.Errorf("malformed module path %q: %v", path, err) + } + i := strings.Index(path, "/") + if i < 0 { + i = len(path) + } + if i == 0 { + return fmt.Errorf("malformed module path %q: leading slash", path) + } + if !strings.Contains(path[:i], ".") { + return fmt.Errorf("malformed module path %q: missing dot in first path element", path) + } + if path[0] == '-' { + return fmt.Errorf("malformed module path %q: leading dash in first path element", path) + } + for _, r := range path[:i] { + if !firstPathOK(r) { + return fmt.Errorf("malformed module path %q: invalid char %q in first path element", path, r) + } + } + if _, _, ok := SplitPathVersion(path); !ok { + return fmt.Errorf("malformed module path %q: invalid version", path) + } + return nil +} + +// CheckImportPath checks that an import path is valid. +func CheckImportPath(path string) error { + if err := checkPath(path, false); err != nil { + return fmt.Errorf("malformed import path %q: %v", path, err) + } + return nil +} + +// checkPath checks that a general path is valid. +// It returns an error describing why but not mentioning path. +// Because these checks apply to both module paths and import paths, +// the caller is expected to add the "malformed ___ path %q: " prefix. +// fileName indicates whether the final element of the path is a file name +// (as opposed to a directory name). +func checkPath(path string, fileName bool) error { + if !utf8.ValidString(path) { + return fmt.Errorf("invalid UTF-8") + } + if path == "" { + return fmt.Errorf("empty string") + } + if strings.Contains(path, "..") { + return fmt.Errorf("double dot") + } + if strings.Contains(path, "//") { + return fmt.Errorf("double slash") + } + if path[len(path)-1] == '/' { + return fmt.Errorf("trailing slash") + } + elemStart := 0 + for i, r := range path { + if r == '/' { + if err := checkElem(path[elemStart:i], fileName); err != nil { + return err + } + elemStart = i + 1 + } + } + if err := checkElem(path[elemStart:], fileName); err != nil { + return err + } + return nil +} + +// checkElem checks whether an individual path element is valid. +// fileName indicates whether the element is a file name (not a directory name). +func checkElem(elem string, fileName bool) error { + if elem == "" { + return fmt.Errorf("empty path element") + } + if strings.Count(elem, ".") == len(elem) { + return fmt.Errorf("invalid path element %q", elem) + } + if elem[0] == '.' && !fileName { + return fmt.Errorf("leading dot in path element") + } + if elem[len(elem)-1] == '.' { + return fmt.Errorf("trailing dot in path element") + } + charOK := pathOK + if fileName { + charOK = fileNameOK + } + for _, r := range elem { + if !charOK(r) { + return fmt.Errorf("invalid char %q", r) + } + } + + // Windows disallows a bunch of path elements, sadly. + // See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file + short := elem + if i := strings.Index(short, "."); i >= 0 { + short = short[:i] + } + for _, bad := range badWindowsNames { + if strings.EqualFold(bad, short) { + return fmt.Errorf("disallowed path element %q", elem) + } + } + return nil +} + +// CheckFilePath checks whether a slash-separated file path is valid. +func CheckFilePath(path string) error { + if err := checkPath(path, true); err != nil { + return fmt.Errorf("malformed file path %q: %v", path, err) + } + return nil +} + +// badWindowsNames are the reserved file path elements on Windows. +// See https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file +var badWindowsNames = []string{ + "CON", + "PRN", + "AUX", + "NUL", + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9", + "LPT1", + "LPT2", + "LPT3", + "LPT4", + "LPT5", + "LPT6", + "LPT7", + "LPT8", + "LPT9", +} + +// SplitPathVersion returns prefix and major version such that prefix+pathMajor == path +// and version is either empty or "/vN" for N >= 2. +// As a special case, gopkg.in paths are recognized directly; +// they require ".vN" instead of "/vN", and for all N, not just N >= 2. +func SplitPathVersion(path string) (prefix, pathMajor string, ok bool) { + if strings.HasPrefix(path, "gopkg.in/") { + return splitGopkgIn(path) + } + + i := len(path) + dot := false + for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9' || path[i-1] == '.') { + if path[i-1] == '.' { + dot = true + } + i-- + } + if i <= 1 || i == len(path) || path[i-1] != 'v' || path[i-2] != '/' { + return path, "", true + } + prefix, pathMajor = path[:i-2], path[i-2:] + if dot || len(pathMajor) <= 2 || pathMajor[2] == '0' || pathMajor == "/v1" { + return path, "", false + } + return prefix, pathMajor, true +} + +// splitGopkgIn is like SplitPathVersion but only for gopkg.in paths. +func splitGopkgIn(path string) (prefix, pathMajor string, ok bool) { + if !strings.HasPrefix(path, "gopkg.in/") { + return path, "", false + } + i := len(path) + if strings.HasSuffix(path, "-unstable") { + i -= len("-unstable") + } + for i > 0 && ('0' <= path[i-1] && path[i-1] <= '9') { + i-- + } + if i <= 1 || path[i-1] != 'v' || path[i-2] != '.' { + // All gopkg.in paths must end in vN for some N. + return path, "", false + } + prefix, pathMajor = path[:i-2], path[i-2:] + if len(pathMajor) <= 2 || pathMajor[2] == '0' && pathMajor != ".v0" { + return path, "", false + } + return prefix, pathMajor, true +} + +// MatchPathMajor reports whether the semantic version v +// matches the path major version pathMajor. +func MatchPathMajor(v, pathMajor string) bool { + if strings.HasPrefix(pathMajor, ".v") && strings.HasSuffix(pathMajor, "-unstable") { + pathMajor = strings.TrimSuffix(pathMajor, "-unstable") + } + if strings.HasPrefix(v, "v0.0.0-") && pathMajor == ".v1" { + // Allow old bug in pseudo-versions that generated v0.0.0- pseudoversion for gopkg .v1. + // For example, gopkg.in/yaml.v2@v2.2.1's go.mod requires gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405. + return true + } + m := semver.Major(v) + if pathMajor == "" { + return m == "v0" || m == "v1" || semver.Build(v) == "+incompatible" + } + return (pathMajor[0] == '/' || pathMajor[0] == '.') && m == pathMajor[1:] +} + +// CanonicalVersion returns the canonical form of the version string v. +// It is the same as semver.Canonical(v) except that it preserves the special build suffix "+incompatible". +func CanonicalVersion(v string) string { + cv := semver.Canonical(v) + if semver.Build(v) == "+incompatible" { + cv += "+incompatible" + } + return cv +} + +// Sort sorts the list by Path, breaking ties by comparing Versions. +func Sort(list []Version) { + sort.Slice(list, func(i, j int) bool { + mi := list[i] + mj := list[j] + if mi.Path != mj.Path { + return mi.Path < mj.Path + } + // To help go.sum formatting, allow version/file. + // Compare semver prefix by semver rules, + // file by string order. + vi := mi.Version + vj := mj.Version + var fi, fj string + if k := strings.Index(vi, "/"); k >= 0 { + vi, fi = vi[:k], vi[k:] + } + if k := strings.Index(vj, "/"); k >= 0 { + vj, fj = vj[:k], vj[k:] + } + if vi != vj { + return semver.Compare(vi, vj) < 0 + } + return fi < fj + }) +} + +// Safe encodings +// +// Module paths appear as substrings of file system paths +// (in the download cache) and of web server URLs in the proxy protocol. +// In general we cannot rely on file systems to be case-sensitive, +// nor can we rely on web servers, since they read from file systems. +// That is, we cannot rely on the file system to keep rsc.io/QUOTE +// and rsc.io/quote separate. Windows and macOS don't. +// Instead, we must never require two different casings of a file path. +// Because we want the download cache to match the proxy protocol, +// and because we want the proxy protocol to be possible to serve +// from a tree of static files (which might be stored on a case-insensitive +// file system), the proxy protocol must never require two different casings +// of a URL path either. +// +// One possibility would be to make the safe encoding be the lowercase +// hexadecimal encoding of the actual path bytes. This would avoid ever +// needing different casings of a file path, but it would be fairly illegible +// to most programmers when those paths appeared in the file system +// (including in file paths in compiler errors and stack traces) +// in web server logs, and so on. Instead, we want a safe encoding that +// leaves most paths unaltered. +// +// The safe encoding is this: +// replace every uppercase letter with an exclamation mark +// followed by the letter's lowercase equivalent. +// +// For example, +// github.com/Azure/azure-sdk-for-go -> github.com/!azure/azure-sdk-for-go. +// github.com/GoogleCloudPlatform/cloudsql-proxy -> github.com/!google!cloud!platform/cloudsql-proxy +// github.com/Sirupsen/logrus -> github.com/!sirupsen/logrus. +// +// Import paths that avoid upper-case letters are left unchanged. +// Note that because import paths are ASCII-only and avoid various +// problematic punctuation (like : < and >), the safe encoding is also ASCII-only +// and avoids the same problematic punctuation. +// +// Import paths have never allowed exclamation marks, so there is no +// need to define how to encode a literal !. +// +// Although paths are disallowed from using Unicode (see pathOK above), +// the eventual plan is to allow Unicode letters as well, to assume that +// file systems and URLs are Unicode-safe (storing UTF-8), and apply +// the !-for-uppercase convention. Note however that not all runes that +// are different but case-fold equivalent are an upper/lower pair. +// For example, U+004B ('K'), U+006B ('k'), and U+212A ('K' for Kelvin) +// are considered to case-fold to each other. When we do add Unicode +// letters, we must not assume that upper/lower are the only case-equivalent pairs. +// Perhaps the Kelvin symbol would be disallowed entirely, for example. +// Or perhaps it would encode as "!!k", or perhaps as "(212A)". +// +// Also, it would be nice to allow Unicode marks as well as letters, +// but marks include combining marks, and then we must deal not +// only with case folding but also normalization: both U+00E9 ('é') +// and U+0065 U+0301 ('e' followed by combining acute accent) +// look the same on the page and are treated by some file systems +// as the same path. If we do allow Unicode marks in paths, there +// must be some kind of normalization to allow only one canonical +// encoding of any character used in an import path. + +// EncodePath returns the safe encoding of the given module path. +// It fails if the module path is invalid. +func EncodePath(path string) (encoding string, err error) { + if err := CheckPath(path); err != nil { + return "", err + } + + return encodeString(path) +} + +// EncodeVersion returns the safe encoding of the given module version. +// Versions are allowed to be in non-semver form but must be valid file names +// and not contain exclamation marks. +func EncodeVersion(v string) (encoding string, err error) { + if err := checkElem(v, true); err != nil || strings.Contains(v, "!") { + return "", fmt.Errorf("disallowed version string %q", v) + } + return encodeString(v) +} + +func encodeString(s string) (encoding string, err error) { + haveUpper := false + for _, r := range s { + if r == '!' || r >= utf8.RuneSelf { + // This should be disallowed by CheckPath, but diagnose anyway. + // The correctness of the encoding loop below depends on it. + return "", fmt.Errorf("internal error: inconsistency in EncodePath") + } + if 'A' <= r && r <= 'Z' { + haveUpper = true + } + } + + if !haveUpper { + return s, nil + } + + var buf []byte + for _, r := range s { + if 'A' <= r && r <= 'Z' { + buf = append(buf, '!', byte(r+'a'-'A')) + } else { + buf = append(buf, byte(r)) + } + } + return string(buf), nil +} + +// DecodePath returns the module path of the given safe encoding. +// It fails if the encoding is invalid or encodes an invalid path. +func DecodePath(encoding string) (path string, err error) { + path, ok := decodeString(encoding) + if !ok { + return "", fmt.Errorf("invalid module path encoding %q", encoding) + } + if err := CheckPath(path); err != nil { + return "", fmt.Errorf("invalid module path encoding %q: %v", encoding, err) + } + return path, nil +} + +// DecodeVersion returns the version string for the given safe encoding. +// It fails if the encoding is invalid or encodes an invalid version. +// Versions are allowed to be in non-semver form but must be valid file names +// and not contain exclamation marks. +func DecodeVersion(encoding string) (v string, err error) { + v, ok := decodeString(encoding) + if !ok { + return "", fmt.Errorf("invalid version encoding %q", encoding) + } + if err := checkElem(v, true); err != nil { + return "", fmt.Errorf("disallowed version string %q", v) + } + return v, nil +} + +func decodeString(encoding string) (string, bool) { + var buf []byte + + bang := false + for _, r := range encoding { + if r >= utf8.RuneSelf { + return "", false + } + if bang { + bang = false + if r < 'a' || 'z' < r { + return "", false + } + buf = append(buf, byte(r+'A'-'a')) + continue + } + if r == '!' { + bang = true + continue + } + if 'A' <= r && r <= 'Z' { + return "", false + } + buf = append(buf, byte(r)) + } + if bang { + return "", false + } + return string(buf), true +} diff --git a/vendor/modules.txt b/vendor/modules.txt index a09608b0d55d..c7475a4ca506 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -192,7 +192,7 @@ golang.org/x/sys/windows golang.org/x/text/width golang.org/x/text/transform golang.org/x/text/unicode/norm -# golang.org/x/tools v0.0.0-20181220024903-92cdcd90bf52 +# golang.org/x/tools v0.0.0-20190205201329-379209517ffe golang.org/x/tools/go/loader golang.org/x/tools/go/packages golang.org/x/tools/imports @@ -206,6 +206,7 @@ golang.org/x/tools/go/internal/cgo golang.org/x/tools/go/internal/packagesdriver golang.org/x/tools/internal/gopathwalk golang.org/x/tools/internal/semver +golang.org/x/tools/internal/module golang.org/x/tools/go/callgraph golang.org/x/tools/go/callgraph/cha golang.org/x/tools/go/callgraph/rta