diff --git a/go.mod b/go.mod index 72e1ed02778d..059eb3be342e 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/go-critic/go-critic v0.0.0-20181204210945-0af0999fabfb github.com/go-lintpack/lintpack v0.5.2 github.com/go-ole/go-ole v1.2.1 // indirect - github.com/go-toolsmith/pkgload v0.0.0-20181120203407-5122569a890b // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.0.0 // indirect github.com/golang/mock v1.1.1 @@ -56,13 +55,13 @@ 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-20190125232054-d66bd3c5d5a6 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 mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect - mvdan.cc/unparam v0.0.0-20181201214637-68701730a1d7 + mvdan.cc/unparam v0.0.0-20190124213536-cc9d2fb52971 sourcegraph.com/sourcegraph/go-diff v0.0.0-20171119081133-3f415a150aec sourcegraph.com/sqs/pbtypes v0.0.0-20160107090929-4d1b9dc7ffc3 // indirect ) diff --git a/go.sum b/go.sum index 7f840c92e2b6..ab76f95c85e1 100644 --- a/go.sum +++ b/go.sum @@ -26,9 +26,8 @@ github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086 h1:EIMuvbE9fbt github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg= github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30 h1:zRJPftZJNLPDiOtvYbFRwjSbaJAcVOf80TeEmWGe2kQ= github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk= +github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8 h1:vVouagbdmqTVlCIAxpyYsNNTbkKZ3V66VpKOLU/s6W4= github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks= -github.com/go-toolsmith/pkgload v0.0.0-20181120203407-5122569a890b h1:M3arPs33ilAwsEZcEzBcZGj5XjNLo1Qohmg0IkSmrnA= -github.com/go-toolsmith/pkgload v0.0.0-20181120203407-5122569a890b/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks= github.com/go-toolsmith/strparse v0.0.0-20180903215201-830b6daa1241 h1:ZRDeQioMGTBLeJxcPxXfFifEUgYxzR7fXw7w2WR+1bo= github.com/go-toolsmith/strparse v0.0.0-20180903215201-830b6daa1241/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= github.com/go-toolsmith/typep v0.0.0-20181030061450-d63dc7650676 h1:6Qrsp0+25KEkaS2bB26UE0giFgRrIc8mYXboDL5OVMA= @@ -85,6 +84,11 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/magiconair/properties v1.7.6 h1:U+1DqNen04MdEPgFiIwdOUiqZ8qPa37xgogX/sd3+54= github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -110,6 +114,7 @@ github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7 h1:80VN+vGkqM773Br/uNNTSheo3KatTgV8IpjIKjvVLng= github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U= @@ -146,14 +151,16 @@ golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181201035826-d0ca3933b724/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6 h1:iZgcI2DDp6zW5v9Z/5+f0NuqoxNdmzg4hivjk2WLXpY= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/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= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0= @@ -166,8 +173,8 @@ mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wp mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20181201214637-68701730a1d7 h1:AHc3hAmhXTHwSA40I4CJW2w2iirqwLzZuFgC6LdbtJk= -mvdan.cc/unparam v0.0.0-20181201214637-68701730a1d7/go.mod h1:N4YHaPPCFjRU1vNrla2C9eoyqLxMaHmrsI8Th4iuQMY= +mvdan.cc/unparam v0.0.0-20190124213536-cc9d2fb52971 h1:INXoeng6RAblgAds8fdt2cAu1Bs/bgr5zq1fdYcuxMI= +mvdan.cc/unparam v0.0.0-20190124213536-cc9d2fb52971/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY= sourcegraph.com/sourcegraph/go-diff v0.0.0-20171119081133-3f415a150aec h1:wAAdENPXC7bE1oxY4VqSDdhaA+XQ8TgQHsZMMnrXjEk= sourcegraph.com/sourcegraph/go-diff v0.0.0-20171119081133-3f415a150aec/go.mod h1:R09mWeb9JcPbO+A3cYDc11xjz0wp6r9+KnqdqROAoRU= sourcegraph.com/sqs/pbtypes v0.0.0-20160107090929-4d1b9dc7ffc3 h1:hXy8YsgVLDz5mlngKhNHQhAsAGrSp3dlXZN4b0/4UUI= diff --git a/pkg/golinters/unparam.go b/pkg/golinters/unparam.go index 6dd4970d2e09..2b67cc7c67c1 100644 --- a/pkg/golinters/unparam.go +++ b/pkg/golinters/unparam.go @@ -23,7 +23,6 @@ func (lint Unparam) Run(ctx context.Context, lintCtx *linter.Context) ([]result. us := &lintCtx.Settings().Unparam c := &check.Checker{} - c.CallgraphAlgorithm(us.Algo) c.CheckExportedFuncs(us.CheckExported) c.Packages(lintCtx.Packages) c.ProgramSSA(lintCtx.SSAProgram) diff --git a/vendor/golang.org/x/tools/go/callgraph/rta/rta.go b/vendor/golang.org/x/tools/go/callgraph/rta/rta.go deleted file mode 100644 index e6b44606ae85..000000000000 --- a/vendor/golang.org/x/tools/go/callgraph/rta/rta.go +++ /dev/null @@ -1,459 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This package provides Rapid Type Analysis (RTA) for Go, a fast -// algorithm for call graph construction and discovery of reachable code -// (and hence dead code) and runtime types. The algorithm was first -// described in: -// -// David F. Bacon and Peter F. Sweeney. 1996. -// Fast static analysis of C++ virtual function calls. (OOPSLA '96) -// http://doi.acm.org/10.1145/236337.236371 -// -// The algorithm uses dynamic programming to tabulate the cross-product -// of the set of known "address taken" functions with the set of known -// dynamic calls of the same type. As each new address-taken function -// is discovered, call graph edges are added from each known callsite, -// and as each new call site is discovered, call graph edges are added -// from it to each known address-taken function. -// -// A similar approach is used for dynamic calls via interfaces: it -// tabulates the cross-product of the set of known "runtime types", -// i.e. types that may appear in an interface value, or be derived from -// one via reflection, with the set of known "invoke"-mode dynamic -// calls. As each new "runtime type" is discovered, call edges are -// added from the known call sites, and as each new call site is -// discovered, call graph edges are added to each compatible -// method. -// -// In addition, we must consider all exported methods of any runtime type -// as reachable, since they may be called via reflection. -// -// Each time a newly added call edge causes a new function to become -// reachable, the code of that function is analyzed for more call sites, -// address-taken functions, and runtime types. The process continues -// until a fixed point is achieved. -// -// The resulting call graph is less precise than one produced by pointer -// analysis, but the algorithm is much faster. For example, running the -// cmd/callgraph tool on its own source takes ~2.1s for RTA and ~5.4s -// for points-to analysis. -// -package rta // import "golang.org/x/tools/go/callgraph/rta" - -// TODO(adonovan): test it by connecting it to the interpreter and -// replacing all "unreachable" functions by a special intrinsic, and -// ensure that that intrinsic is never called. - -import ( - "fmt" - "go/types" - - "golang.org/x/tools/go/callgraph" - "golang.org/x/tools/go/ssa" - "golang.org/x/tools/go/types/typeutil" -) - -// A Result holds the results of Rapid Type Analysis, which includes the -// set of reachable functions/methods, runtime types, and the call graph. -// -type Result struct { - // CallGraph is the discovered callgraph. - // It does not include edges for calls made via reflection. - CallGraph *callgraph.Graph - - // Reachable contains the set of reachable functions and methods. - // This includes exported methods of runtime types, since - // they may be accessed via reflection. - // The value indicates whether the function is address-taken. - // - // (We wrap the bool in a struct to avoid inadvertent use of - // "if Reachable[f] {" to test for set membership.) - Reachable map[*ssa.Function]struct{ AddrTaken bool } - - // RuntimeTypes contains the set of types that are needed at - // runtime, for interfaces or reflection. - // - // The value indicates whether the type is inaccessible to reflection. - // Consider: - // type A struct{B} - // fmt.Println(new(A)) - // Types *A, A and B are accessible to reflection, but the unnamed - // type struct{B} is not. - RuntimeTypes typeutil.Map -} - -// Working state of the RTA algorithm. -type rta struct { - result *Result - - prog *ssa.Program - - worklist []*ssa.Function // list of functions to visit - - // addrTakenFuncsBySig contains all address-taken *Functions, grouped by signature. - // Keys are *types.Signature, values are map[*ssa.Function]bool sets. - addrTakenFuncsBySig typeutil.Map - - // dynCallSites contains all dynamic "call"-mode call sites, grouped by signature. - // Keys are *types.Signature, values are unordered []ssa.CallInstruction. - dynCallSites typeutil.Map - - // invokeSites contains all "invoke"-mode call sites, grouped by interface. - // Keys are *types.Interface (never *types.Named), - // Values are unordered []ssa.CallInstruction sets. - invokeSites typeutil.Map - - // The following two maps together define the subset of the - // m:n "implements" relation needed by the algorithm. - - // concreteTypes maps each concrete type to the set of interfaces that it implements. - // Keys are types.Type, values are unordered []*types.Interface. - // Only concrete types used as MakeInterface operands are included. - concreteTypes typeutil.Map - - // interfaceTypes maps each interface type to - // the set of concrete types that implement it. - // Keys are *types.Interface, values are unordered []types.Type. - // Only interfaces used in "invoke"-mode CallInstructions are included. - interfaceTypes typeutil.Map -} - -// addReachable marks a function as potentially callable at run-time, -// and ensures that it gets processed. -func (r *rta) addReachable(f *ssa.Function, addrTaken bool) { - reachable := r.result.Reachable - n := len(reachable) - v := reachable[f] - if addrTaken { - v.AddrTaken = true - } - reachable[f] = v - if len(reachable) > n { - // First time seeing f. Add it to the worklist. - r.worklist = append(r.worklist, f) - } -} - -// addEdge adds the specified call graph edge, and marks it reachable. -// addrTaken indicates whether to mark the callee as "address-taken". -func (r *rta) addEdge(site ssa.CallInstruction, callee *ssa.Function, addrTaken bool) { - r.addReachable(callee, addrTaken) - - if g := r.result.CallGraph; g != nil { - if site.Parent() == nil { - panic(site) - } - from := g.CreateNode(site.Parent()) - to := g.CreateNode(callee) - callgraph.AddEdge(from, site, to) - } -} - -// ---------- addrTakenFuncs × dynCallSites ---------- - -// visitAddrTakenFunc is called each time we encounter an address-taken function f. -func (r *rta) visitAddrTakenFunc(f *ssa.Function) { - // Create two-level map (Signature -> Function -> bool). - S := f.Signature - funcs, _ := r.addrTakenFuncsBySig.At(S).(map[*ssa.Function]bool) - if funcs == nil { - funcs = make(map[*ssa.Function]bool) - r.addrTakenFuncsBySig.Set(S, funcs) - } - if !funcs[f] { - // First time seeing f. - funcs[f] = true - - // If we've seen any dyncalls of this type, mark it reachable, - // and add call graph edges. - sites, _ := r.dynCallSites.At(S).([]ssa.CallInstruction) - for _, site := range sites { - r.addEdge(site, f, true) - } - } -} - -// visitDynCall is called each time we encounter a dynamic "call"-mode call. -func (r *rta) visitDynCall(site ssa.CallInstruction) { - S := site.Common().Signature() - - // Record the call site. - sites, _ := r.dynCallSites.At(S).([]ssa.CallInstruction) - r.dynCallSites.Set(S, append(sites, site)) - - // For each function of signature S that we know is address-taken, - // add an edge and mark it reachable. - funcs, _ := r.addrTakenFuncsBySig.At(S).(map[*ssa.Function]bool) - for g := range funcs { - r.addEdge(site, g, true) - } -} - -// ---------- concrete types × invoke sites ---------- - -// addInvokeEdge is called for each new pair (site, C) in the matrix. -func (r *rta) addInvokeEdge(site ssa.CallInstruction, C types.Type) { - // Ascertain the concrete method of C to be called. - imethod := site.Common().Method - cmethod := r.prog.MethodValue(r.prog.MethodSets.MethodSet(C).Lookup(imethod.Pkg(), imethod.Name())) - r.addEdge(site, cmethod, true) -} - -// visitInvoke is called each time the algorithm encounters an "invoke"-mode call. -func (r *rta) visitInvoke(site ssa.CallInstruction) { - I := site.Common().Value.Type().Underlying().(*types.Interface) - - // Record the invoke site. - sites, _ := r.invokeSites.At(I).([]ssa.CallInstruction) - r.invokeSites.Set(I, append(sites, site)) - - // Add callgraph edge for each existing - // address-taken concrete type implementing I. - for _, C := range r.implementations(I) { - r.addInvokeEdge(site, C) - } -} - -// ---------- main algorithm ---------- - -// visitFunc processes function f. -func (r *rta) visitFunc(f *ssa.Function) { - var space [32]*ssa.Value // preallocate space for common case - - for _, b := range f.Blocks { - for _, instr := range b.Instrs { - rands := instr.Operands(space[:0]) - - switch instr := instr.(type) { - case ssa.CallInstruction: - call := instr.Common() - if call.IsInvoke() { - r.visitInvoke(instr) - } else if g := call.StaticCallee(); g != nil { - r.addEdge(instr, g, false) - } else if _, ok := call.Value.(*ssa.Builtin); !ok { - r.visitDynCall(instr) - } - - // Ignore the call-position operand when - // looking for address-taken Functions. - // Hack: assume this is rands[0]. - rands = rands[1:] - - case *ssa.MakeInterface: - r.addRuntimeType(instr.X.Type(), false) - } - - // Process all address-taken functions. - for _, op := range rands { - if g, ok := (*op).(*ssa.Function); ok { - r.visitAddrTakenFunc(g) - } - } - } - } -} - -// Analyze performs Rapid Type Analysis, starting at the specified root -// functions. It returns nil if no roots were specified. -// -// If buildCallGraph is true, Result.CallGraph will contain a call -// graph; otherwise, only the other fields (reachable functions) are -// populated. -// -func Analyze(roots []*ssa.Function, buildCallGraph bool) *Result { - if len(roots) == 0 { - return nil - } - - r := &rta{ - result: &Result{Reachable: make(map[*ssa.Function]struct{ AddrTaken bool })}, - prog: roots[0].Prog, - } - - if buildCallGraph { - // TODO(adonovan): change callgraph API to eliminate the - // notion of a distinguished root node. Some callgraphs - // have many roots, or none. - r.result.CallGraph = callgraph.New(roots[0]) - } - - hasher := typeutil.MakeHasher() - r.result.RuntimeTypes.SetHasher(hasher) - r.addrTakenFuncsBySig.SetHasher(hasher) - r.dynCallSites.SetHasher(hasher) - r.invokeSites.SetHasher(hasher) - r.concreteTypes.SetHasher(hasher) - r.interfaceTypes.SetHasher(hasher) - - // Visit functions, processing their instructions, and adding - // new functions to the worklist, until a fixed point is - // reached. - var shadow []*ssa.Function // for efficiency, we double-buffer the worklist - r.worklist = append(r.worklist, roots...) - for len(r.worklist) > 0 { - shadow, r.worklist = r.worklist, shadow[:0] - for _, f := range shadow { - r.visitFunc(f) - } - } - return r.result -} - -// interfaces(C) returns all currently known interfaces implemented by C. -func (r *rta) interfaces(C types.Type) []*types.Interface { - // Ascertain set of interfaces C implements - // and update 'implements' relation. - var ifaces []*types.Interface - r.interfaceTypes.Iterate(func(I types.Type, concs interface{}) { - if I := I.(*types.Interface); types.Implements(C, I) { - concs, _ := concs.([]types.Type) - r.interfaceTypes.Set(I, append(concs, C)) - ifaces = append(ifaces, I) - } - }) - r.concreteTypes.Set(C, ifaces) - return ifaces -} - -// implementations(I) returns all currently known concrete types that implement I. -func (r *rta) implementations(I *types.Interface) []types.Type { - var concs []types.Type - if v := r.interfaceTypes.At(I); v != nil { - concs = v.([]types.Type) - } else { - // First time seeing this interface. - // Update the 'implements' relation. - r.concreteTypes.Iterate(func(C types.Type, ifaces interface{}) { - if types.Implements(C, I) { - ifaces, _ := ifaces.([]*types.Interface) - r.concreteTypes.Set(C, append(ifaces, I)) - concs = append(concs, C) - } - }) - r.interfaceTypes.Set(I, concs) - } - return concs -} - -// addRuntimeType is called for each concrete type that can be the -// dynamic type of some interface or reflect.Value. -// Adapted from needMethods in go/ssa/builder.go -// -func (r *rta) addRuntimeType(T types.Type, skip bool) { - if prev, ok := r.result.RuntimeTypes.At(T).(bool); ok { - if skip && !prev { - r.result.RuntimeTypes.Set(T, skip) - } - return - } - r.result.RuntimeTypes.Set(T, skip) - - mset := r.prog.MethodSets.MethodSet(T) - - if _, ok := T.Underlying().(*types.Interface); !ok { - // T is a new concrete type. - for i, n := 0, mset.Len(); i < n; i++ { - sel := mset.At(i) - m := sel.Obj() - - if m.Exported() { - // Exported methods are always potentially callable via reflection. - r.addReachable(r.prog.MethodValue(sel), true) - } - } - - // Add callgraph edge for each existing dynamic - // "invoke"-mode call via that interface. - for _, I := range r.interfaces(T) { - sites, _ := r.invokeSites.At(I).([]ssa.CallInstruction) - for _, site := range sites { - r.addInvokeEdge(site, T) - } - } - } - - // Precondition: T is not a method signature (*Signature with Recv()!=nil). - // Recursive case: skip => don't call makeMethods(T). - // Each package maintains its own set of types it has visited. - - var n *types.Named - switch T := T.(type) { - case *types.Named: - n = T - case *types.Pointer: - n, _ = T.Elem().(*types.Named) - } - if n != nil { - owner := n.Obj().Pkg() - if owner == nil { - return // built-in error type - } - } - - // Recursion over signatures of each exported method. - for i := 0; i < mset.Len(); i++ { - if mset.At(i).Obj().Exported() { - sig := mset.At(i).Type().(*types.Signature) - r.addRuntimeType(sig.Params(), true) // skip the Tuple itself - r.addRuntimeType(sig.Results(), true) // skip the Tuple itself - } - } - - switch t := T.(type) { - case *types.Basic: - // nop - - case *types.Interface: - // nop---handled by recursion over method set. - - case *types.Pointer: - r.addRuntimeType(t.Elem(), false) - - case *types.Slice: - r.addRuntimeType(t.Elem(), false) - - case *types.Chan: - r.addRuntimeType(t.Elem(), false) - - case *types.Map: - r.addRuntimeType(t.Key(), false) - r.addRuntimeType(t.Elem(), false) - - case *types.Signature: - if t.Recv() != nil { - panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv())) - } - r.addRuntimeType(t.Params(), true) // skip the Tuple itself - r.addRuntimeType(t.Results(), true) // skip the Tuple itself - - case *types.Named: - // A pointer-to-named type can be derived from a named - // type via reflection. It may have methods too. - r.addRuntimeType(types.NewPointer(T), false) - - // Consider 'type T struct{S}' where S has methods. - // Reflection provides no way to get from T to struct{S}, - // only to S, so the method set of struct{S} is unwanted, - // so set 'skip' flag during recursion. - r.addRuntimeType(t.Underlying(), true) - - case *types.Array: - r.addRuntimeType(t.Elem(), false) - - case *types.Struct: - for i, n := 0, t.NumFields(); i < n; i++ { - r.addRuntimeType(t.Field(i).Type(), false) - } - - case *types.Tuple: - for i, n := 0, t.Len(); i < n; i++ { - r.addRuntimeType(t.At(i).Type(), false) - } - - default: - panic(T) - } -} 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..0ec0fab24bb7 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,21 +14,20 @@ 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". +Three query operators are currently supported: "file", "pattern", and "name". 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. +The query "name=identifier" matches 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..7ca5ebe459a7 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. @@ -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..f18c41351453 100644 --- a/vendor/golang.org/x/tools/imports/fix.go +++ b/vendor/golang.org/x/tools/imports/fix.go @@ -22,6 +22,7 @@ import ( "strconv" "strings" "sync" + "time" "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/packages" @@ -85,7 +86,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 +143,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 +158,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 +171,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 +208,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 +227,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 +239,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 +264,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 importPathToNameBasic(imp.importPath, p.srcDir) } // load reads in everything necessary to run a pass, and reports whether the @@ -312,7 +297,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 +319,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 +388,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 != importPathToNameBasic(path, p.srcDir) { + imp.Name = &ast.Ident{Name: ident, NamePos: imp.Pos()} } } } @@ -456,7 +446,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 +469,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 +481,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 +508,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 } - return len(bytes.TrimSpace(out)) > 0 + 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 &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 addStdlibCandidates(pass *pass, refs map[string]map[string]bool) { +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 references) { add := func(pkg string) { pass.addCandidate( &importInfo{importPath: pkg}, @@ -574,13 +620,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] = importPathToNameBasic(path, srcDir) + } + return names, nil + +} + +func (r *goPackagesResolver) scan(refs references) ([]*pkg, error) { var loadQueries []string for pkgName := range refs { loadQueries = append(loadQueries, "name="+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 +677,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 +705,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() { @@ -684,34 +756,44 @@ func importPathToNameBasic(importPath, srcDir string) (packageName string) { 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 +813,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 +887,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 +907,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 +926,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 +979,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 +1013,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 +1075,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/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/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..98dcc8951e4f 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-20190125232054-d66bd3c5d5a6 golang.org/x/tools/go/loader golang.org/x/tools/go/packages golang.org/x/tools/imports @@ -206,9 +206,9 @@ 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 golang.org/x/tools/go/internal/gcimporter golang.org/x/tools/internal/fastwalk # gopkg.in/yaml.v2 v2.2.1 @@ -217,7 +217,7 @@ gopkg.in/yaml.v2 mvdan.cc/interfacer/check # mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b mvdan.cc/lint -# mvdan.cc/unparam v0.0.0-20181201214637-68701730a1d7 +# mvdan.cc/unparam v0.0.0-20190124213536-cc9d2fb52971 mvdan.cc/unparam/check # sourcegraph.com/sourcegraph/go-diff v0.0.0-20171119081133-3f415a150aec sourcegraph.com/sourcegraph/go-diff/diff diff --git a/vendor/mvdan.cc/unparam/check/check.go b/vendor/mvdan.cc/unparam/check/check.go index fc22b4041eba..c52cccfd2eaf 100644 --- a/vendor/mvdan.cc/unparam/check/check.go +++ b/vendor/mvdan.cc/unparam/check/check.go @@ -23,7 +23,6 @@ import ( "golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/callgraph/cha" - "golang.org/x/tools/go/callgraph/rta" "golang.org/x/tools/go/packages" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" @@ -31,7 +30,7 @@ import ( // UnusedParams returns a list of human-readable issues that point out unused // function parameters. -func UnusedParams(tests bool, algo string, exported, debug bool, args ...string) ([]string, error) { +func UnusedParams(tests bool, exported, debug bool, args ...string) ([]string, error) { wd, err := os.Getwd() if err != nil { return nil, err @@ -39,7 +38,6 @@ func UnusedParams(tests bool, algo string, exported, debug bool, args ...string) c := &Checker{ wd: wd, tests: tests, - algo: algo, exported: exported, } if debug { @@ -59,7 +57,6 @@ type Checker struct { wd string tests bool - algo string exported bool debugLog io.Writer @@ -73,6 +70,12 @@ type Checker struct { // funcBodyByPos maps from a function position to its body. We can't map // to the declaration, as that could be either a FuncDecl or FuncLit. funcBodyByPos map[token.Pos]*ast.BlockStmt + + typesImplementing map[*types.Named]bool + + // Funcs used as a struct field or a func call argument. These are very + // often signatures which cannot be changed. + funcUsedAs map[*ssa.Function]string } var errorType = types.Universe.Lookup("error").Type() @@ -139,11 +142,6 @@ func (c *Checker) ProgramSSA(prog *ssa.Program) { c.prog = prog } -// CallgraphAlgorithm supplies Checker with the call graph construction algorithm. -func (c *Checker) CallgraphAlgorithm(algo string) { - c.algo = algo -} - // CheckExportedFuncs sets whether to inspect exported functions func (c *Checker) CheckExportedFuncs(exported bool) { c.exported = exported @@ -184,6 +182,8 @@ func (c *Checker) Check() ([]Issue, error) { c.cachedDeclCounts = make(map[string]map[string]int) c.callByPos = make(map[token.Pos]*ast.CallExpr) c.funcBodyByPos = make(map[token.Pos]*ast.BlockStmt) + c.typesImplementing = make(map[*types.Named]bool) + wantPkg := make(map[*types.Package]*packages.Package) genFiles := make(map[string]bool) for _, pkg := range c.pkgs { @@ -195,6 +195,21 @@ func (c *Checker) Check() ([]Issue, error) { } ast.Inspect(f, func(node ast.Node) bool { switch node := node.(type) { + case *ast.ValueSpec: + if len(node.Values) == 0 || node.Type == nil || + len(node.Names) != 1 || node.Names[0].Name != "_" { + break + } + _, ok := pkg.TypesInfo.TypeOf(node.Type).Underlying().(*types.Interface) + if !ok { + break + } + valTyp := pkg.TypesInfo.Types[node.Values[0]].Type + named := findNamed(valTyp) + if named == nil { + break + } + c.typesImplementing[named] = true case *ast.CallExpr: c.callByPos[node.Lparen] = node // ssa.Function.Pos returns the declaring @@ -209,26 +224,47 @@ func (c *Checker) Check() ([]Issue, error) { }) } } - switch c.algo { - case "cha": - c.graph = cha.CallGraph(c.prog) - case "rta": - mains, err := mainPackages(c.prog, wantPkg) - if err != nil { - return nil, err - } - var roots []*ssa.Function - for _, main := range mains { - roots = append(roots, main.Func("init"), main.Func("main")) + c.graph = cha.CallGraph(c.prog) + c.graph.DeleteSyntheticNodes() + + allFuncs := ssautil.AllFunctions(c.prog) + + c.funcUsedAs = make(map[*ssa.Function]string) + for fn := range allFuncs { + for _, b := range fn.Blocks { + for _, instr := range b.Instrs { + switch instr := instr.(type) { + case *ssa.Call: + for _, arg := range instr.Call.Args { + if fn := findFunction(arg); fn != nil { + // someFunc(fn) + c.funcUsedAs[fn] = "param" + } + } + case *ssa.Store: + if _, ok := instr.Addr.(*ssa.FieldAddr); !ok { + break + } + if fn := findFunction(instr.Val); fn != nil { + // x.someField = fn + c.funcUsedAs[fn] = "field" + } + case *ssa.MakeInterface: + if fn := findFunction(instr.X); fn != nil { + // emptyIface = fn + c.funcUsedAs[fn] = "interface" + } + case *ssa.ChangeType: + if fn := findFunction(instr.X); fn != nil { + // someType(fn) + c.funcUsedAs[fn] = "type conversion" + } + } + } } - result := rta.Analyze(roots, true) - c.graph = result.CallGraph - default: - return nil, fmt.Errorf("unknown call graph construction algorithm: %q", c.algo) } - c.graph.DeleteSyntheticNodes() - for fn := range ssautil.AllFunctions(c.prog) { + for fn := range allFuncs { switch { case fn.Pkg == nil: // builtin? continue @@ -267,6 +303,28 @@ func (c *Checker) Check() ([]Issue, error) { return c.issues, nil } +func findNamed(typ types.Type) *types.Named { + switch typ := typ.(type) { + case *types.Pointer: + return findNamed(typ.Elem()) + case *types.Named: + return typ + } + return nil +} + +// findFunction returns the function that is behind a value, if any. +func findFunction(value ssa.Value) *ssa.Function { + switch value := value.(type) { + case *ssa.Function: + return value + case *ssa.MakeClosure: + // closure of a func + return value.Fn.(*ssa.Function) + } + return nil +} + // addIssue records a newly found unused parameter. func (c *Checker) addIssue(fn *ssa.Function, pos token.Pos, format string, args ...interface{}) { c.issues = append(c.issues, Issue{ @@ -299,6 +357,17 @@ func (c *Checker) checkFunc(fn *ssa.Function, pkg *packages.Package) { c.debug(" skip - type is required via call\n") return } + if usedAs := c.funcUsedAs[fn]; usedAs != "" { + c.debug(" skip - func is used as a %s\n", usedAs) + return + } + if recv := fn.Signature.Recv(); recv != nil { + named := findNamed(recv.Type()) + if c.typesImplementing[named] { + c.debug(" skip - receiver must implement an interface\n") + return + } + } if c.multipleImpls(pkg, fn) { c.debug(" skip - multiple implementations via build tags\n") return @@ -410,21 +479,6 @@ resLoop: } } -// mainPackages returns the subset of main packages within pkgSet. -func mainPackages(prog *ssa.Program, pkgSet map[*types.Package]*packages.Package) ([]*ssa.Package, error) { - mains := make([]*ssa.Package, 0, len(pkgSet)) - for tpkg := range pkgSet { - pkg := prog.Package(tpkg) - if tpkg.Name() == "main" && pkg.Func("main") != nil { - mains = append(mains, pkg) - } - } - if len(mains) == 0 { - return nil, fmt.Errorf("no main packages") - } - return mains, nil -} - // calledInReturn reports whether any of a function's inbound calls happened // directly as a return statement. That is, if function "foo" was used via // "return foo()". This means that the result parameters of the function cannot @@ -500,6 +554,12 @@ func (c *Checker) alwaysReceivedConst(in []*callgraph.Edge, par *ssa.Parameter, seenOrig := "" for _, edge := range in { call := edge.Site.Common() + if pos >= len(call.Args) { + // TODO: investigate? Weird crash in + // internal/x/net/http2/hpack/hpack_test.go, where we + // roughly do: "at := d.mustAt; at(3)". + return "" + } cnst := constValue(call.Args[pos]) if cnst == nil { return "" // not a constant @@ -731,16 +791,8 @@ func (c *Checker) multipleImpls(pkg *packages.Package, fn *ssa.Function) bool { path := c.prog.Fset.Position(fn.Pos()).Filename count := c.declCounts(filepath.Dir(path), pkg.Types.Name()) name := fn.Name() - if fn.Signature.Recv() != nil { - tp := fn.Params[0].Type() - for { - ptr, ok := tp.(*types.Pointer) - if !ok { - break - } - tp = ptr.Elem() - } - named := tp.(*types.Named) + if recv := fn.Signature.Recv(); recv != nil { + named := findNamed(recv.Type()) name = named.Obj().Name() + "." + name } return count[name] > 1