Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / cmd / godex / godex.go
1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package main
6
7 import (
8         "errors"
9         "flag"
10         "fmt"
11         "go/build"
12         "go/types"
13         "io/ioutil"
14         "os"
15         "path/filepath"
16         "strings"
17 )
18
19 var (
20         source  = flag.String("s", "", "only consider packages from src, where src is one of the supported compilers")
21         verbose = flag.Bool("v", false, "verbose mode")
22 )
23
24 // lists of registered sources and corresponding importers
25 var (
26         sources         []string
27         importers       []types.Importer
28         errImportFailed = errors.New("import failed")
29 )
30
31 func usage() {
32         fmt.Fprintln(os.Stderr, "usage: godex [flags] {path|qualifiedIdent}")
33         flag.PrintDefaults()
34         os.Exit(2)
35 }
36
37 func report(msg string) {
38         fmt.Fprintln(os.Stderr, "error: "+msg)
39         os.Exit(2)
40 }
41
42 func main() {
43         flag.Usage = usage
44         flag.Parse()
45
46         if flag.NArg() == 0 {
47                 report("no package name, path, or file provided")
48         }
49
50         var imp types.Importer = new(tryImporters)
51         if *source != "" {
52                 imp = lookup(*source)
53                 if imp == nil {
54                         report("source (-s argument) must be one of: " + strings.Join(sources, ", "))
55                 }
56         }
57
58         for _, arg := range flag.Args() {
59                 path, name := splitPathIdent(arg)
60                 logf("\tprocessing %q: path = %q, name = %s\n", arg, path, name)
61
62                 // generate possible package path prefixes
63                 // (at the moment we do this for each argument - should probably cache the generated prefixes)
64                 prefixes := make(chan string)
65                 go genPrefixes(prefixes, !filepath.IsAbs(path) && !build.IsLocalImport(path))
66
67                 // import package
68                 pkg, err := tryPrefixes(prefixes, path, imp)
69                 if err != nil {
70                         logf("\t=> ignoring %q: %s\n", path, err)
71                         continue
72                 }
73
74                 // filter objects if needed
75                 var filter func(types.Object) bool
76                 if name != "" {
77                         filter = func(obj types.Object) bool {
78                                 // TODO(gri) perhaps use regular expression matching here?
79                                 return obj.Name() == name
80                         }
81                 }
82
83                 // print contents
84                 print(os.Stdout, pkg, filter)
85         }
86 }
87
88 func logf(format string, args ...interface{}) {
89         if *verbose {
90                 fmt.Fprintf(os.Stderr, format, args...)
91         }
92 }
93
94 // splitPathIdent splits a path.name argument into its components.
95 // All but the last path element may contain dots.
96 func splitPathIdent(arg string) (path, name string) {
97         if i := strings.LastIndex(arg, "."); i >= 0 {
98                 if j := strings.LastIndex(arg, "/"); j < i {
99                         // '.' is not part of path
100                         path = arg[:i]
101                         name = arg[i+1:]
102                         return
103                 }
104         }
105         path = arg
106         return
107 }
108
109 // tryPrefixes tries to import the package given by (the possibly partial) path using the given importer imp
110 // by prepending all possible prefixes to path. It returns with the first package that it could import, or
111 // with an error.
112 func tryPrefixes(prefixes chan string, path string, imp types.Importer) (pkg *types.Package, err error) {
113         for prefix := range prefixes {
114                 actual := path
115                 if prefix == "" {
116                         // don't use filepath.Join as it will sanitize the path and remove
117                         // a leading dot and then the path is not recognized as a relative
118                         // package path by the importers anymore
119                         logf("\ttrying no prefix\n")
120                 } else {
121                         actual = filepath.Join(prefix, path)
122                         logf("\ttrying prefix %q\n", prefix)
123                 }
124                 pkg, err = imp.Import(actual)
125                 if err == nil {
126                         break
127                 }
128                 logf("\t=> importing %q failed: %s\n", actual, err)
129         }
130         return
131 }
132
133 // tryImporters is an importer that tries all registered importers
134 // successively until one of them succeeds or all of them failed.
135 type tryImporters struct{}
136
137 func (t *tryImporters) Import(path string) (pkg *types.Package, err error) {
138         for i, imp := range importers {
139                 logf("\t\ttrying %s import\n", sources[i])
140                 pkg, err = imp.Import(path)
141                 if err == nil {
142                         break
143                 }
144                 logf("\t\t=> %s import failed: %s\n", sources[i], err)
145         }
146         return
147 }
148
149 type protector struct {
150         imp types.Importer
151 }
152
153 func (p *protector) Import(path string) (pkg *types.Package, err error) {
154         defer func() {
155                 if recover() != nil {
156                         pkg = nil
157                         err = errImportFailed
158                 }
159         }()
160         return p.imp.Import(path)
161 }
162
163 // protect protects an importer imp from panics and returns the protected importer.
164 func protect(imp types.Importer) types.Importer {
165         return &protector{imp}
166 }
167
168 // register registers an importer imp for a given source src.
169 func register(src string, imp types.Importer) {
170         if lookup(src) != nil {
171                 panic(src + " importer already registered")
172         }
173         sources = append(sources, src)
174         importers = append(importers, protect(imp))
175 }
176
177 // lookup returns the importer imp for a given source src.
178 func lookup(src string) types.Importer {
179         for i, s := range sources {
180                 if s == src {
181                         return importers[i]
182                 }
183         }
184         return nil
185 }
186
187 func genPrefixes(out chan string, all bool) {
188         out <- ""
189         if all {
190                 platform := build.Default.GOOS + "_" + build.Default.GOARCH
191                 dirnames := append([]string{build.Default.GOROOT}, filepath.SplitList(build.Default.GOPATH)...)
192                 for _, dirname := range dirnames {
193                         walkDir(filepath.Join(dirname, "pkg", platform), "", out)
194                 }
195         }
196         close(out)
197 }
198
199 func walkDir(dirname, prefix string, out chan string) {
200         fiList, err := ioutil.ReadDir(dirname)
201         if err != nil {
202                 return
203         }
204         for _, fi := range fiList {
205                 if fi.IsDir() && !strings.HasPrefix(fi.Name(), ".") {
206                         prefix := filepath.Join(prefix, fi.Name())
207                         out <- prefix
208                         walkDir(filepath.Join(dirname, fi.Name()), prefix, out)
209                 }
210         }
211 }