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 / bundle / main.go
1 // Copyright 2015 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 // Bundle creates a single-source-file version of a source package
6 // suitable for inclusion in a particular target package.
7 //
8 // Usage:
9 //
10 //      bundle [-o file] [-dst path] [-pkg name] [-prefix p] [-import old=new] [-tags build_constraints] <src>
11 //
12 // The src argument specifies the import path of the package to bundle.
13 // The bundling of a directory of source files into a single source file
14 // necessarily imposes a number of constraints.
15 // The package being bundled must not use cgo; must not use conditional
16 // file compilation, whether with build tags or system-specific file names
17 // like code_amd64.go; must not depend on any special comments, which
18 // may not be preserved; must not use any assembly sources;
19 // must not use renaming imports; and must not use reflection-based APIs
20 // that depend on the specific names of types or struct fields.
21 //
22 // By default, bundle writes the bundled code to standard output.
23 // If the -o argument is given, bundle writes to the named file
24 // and also includes a ``//go:generate'' comment giving the exact
25 // command line used, for regenerating the file with ``go generate.''
26 //
27 // Bundle customizes its output for inclusion in a particular package, the destination package.
28 // By default bundle assumes the destination is the package in the current directory,
29 // but the destination package can be specified explicitly using the -dst option,
30 // which takes an import path as its argument.
31 // If the source package imports the destination package, bundle will remove
32 // those imports and rewrite any references to use direct references to the
33 // corresponding symbols.
34 // Bundle also must write a package declaration in the output and must
35 // choose a name to use in that declaration.
36 // If the -pkg option is given, bundle uses that name.
37 // Otherwise, the name of the destination package is used.
38 // Build constraints for the generated file can be specified using the -tags option.
39 //
40 // To avoid collisions, bundle inserts a prefix at the beginning of
41 // every package-level const, func, type, and var identifier in src's code,
42 // updating references accordingly. The default prefix is the package name
43 // of the source package followed by an underscore. The -prefix option
44 // specifies an alternate prefix.
45 //
46 // Occasionally it is necessary to rewrite imports during the bundling
47 // process. The -import option, which may be repeated, specifies that
48 // an import of "old" should be rewritten to import "new" instead.
49 //
50 // Example
51 //
52 // Bundle archive/zip for inclusion in cmd/dist:
53 //
54 //      cd $GOROOT/src/cmd/dist
55 //      bundle -o zip.go archive/zip
56 //
57 // Bundle golang.org/x/net/http2 for inclusion in net/http,
58 // prefixing all identifiers by "http2" instead of "http2_", and
59 // including a "!nethttpomithttp2" build constraint:
60 //
61 //      cd $GOROOT/src/net/http
62 //      bundle -o h2_bundle.go -prefix http2 -tags '!nethttpomithttp2' golang.org/x/net/http2
63 //
64 // Update the http2 bundle in net/http:
65 //
66 //      go generate net/http
67 //
68 // Update all bundles in the standard library:
69 //
70 //      go generate -run bundle std
71 //
72 package main
73
74 import (
75         "bytes"
76         "flag"
77         "fmt"
78         "go/ast"
79         "go/format"
80         "go/printer"
81         "go/token"
82         "go/types"
83         "io/ioutil"
84         "log"
85         "os"
86         "strconv"
87         "strings"
88
89         "golang.org/x/tools/go/packages"
90 )
91
92 var (
93         outputFile = flag.String("o", "", "write output to `file` (default standard output)")
94         dstPath    = flag.String("dst", ".", "set destination import `path`")
95         pkgName    = flag.String("pkg", "", "set destination package `name`")
96         prefix     = flag.String("prefix", "&_", "set bundled identifier prefix to `p` (default is \"&_\", where & stands for the original name)")
97         buildTags  = flag.String("tags", "", "the build constraints to be inserted into the generated file")
98
99         importMap = map[string]string{}
100 )
101
102 func init() {
103         flag.Var(flagFunc(addImportMap), "import", "rewrite import using `map`, of form old=new (can be repeated)")
104 }
105
106 func addImportMap(s string) {
107         if strings.Count(s, "=") != 1 {
108                 log.Fatal("-import argument must be of the form old=new")
109         }
110         i := strings.Index(s, "=")
111         old, new := s[:i], s[i+1:]
112         if old == "" || new == "" {
113                 log.Fatal("-import argument must be of the form old=new; old and new must be non-empty")
114         }
115         importMap[old] = new
116 }
117
118 func usage() {
119         fmt.Fprintf(os.Stderr, "Usage: bundle [options] <src>\n")
120         flag.PrintDefaults()
121 }
122
123 func main() {
124         log.SetPrefix("bundle: ")
125         log.SetFlags(0)
126
127         flag.Usage = usage
128         flag.Parse()
129         args := flag.Args()
130         if len(args) != 1 {
131                 usage()
132                 os.Exit(2)
133         }
134
135         cfg := &packages.Config{Mode: packages.NeedName}
136         pkgs, err := packages.Load(cfg, *dstPath)
137         if err != nil {
138                 log.Fatalf("cannot load destination package: %v", err)
139         }
140         if packages.PrintErrors(pkgs) > 0 || len(pkgs) != 1 {
141                 log.Fatalf("failed to load destination package")
142         }
143         if *pkgName == "" {
144                 *pkgName = pkgs[0].Name
145         }
146
147         code, err := bundle(args[0], pkgs[0].PkgPath, *pkgName, *prefix, *buildTags)
148         if err != nil {
149                 log.Fatal(err)
150         }
151         if *outputFile != "" {
152                 err := ioutil.WriteFile(*outputFile, code, 0666)
153                 if err != nil {
154                         log.Fatal(err)
155                 }
156         } else {
157                 _, err := os.Stdout.Write(code)
158                 if err != nil {
159                         log.Fatal(err)
160                 }
161         }
162 }
163
164 // isStandardImportPath is copied from cmd/go in the standard library.
165 func isStandardImportPath(path string) bool {
166         i := strings.Index(path, "/")
167         if i < 0 {
168                 i = len(path)
169         }
170         elem := path[:i]
171         return !strings.Contains(elem, ".")
172 }
173
174 var testingOnlyPackagesConfig *packages.Config
175
176 func bundle(src, dst, dstpkg, prefix, buildTags string) ([]byte, error) {
177         // Load the initial package.
178         cfg := &packages.Config{}
179         if testingOnlyPackagesConfig != nil {
180                 *cfg = *testingOnlyPackagesConfig
181         } else {
182                 // Bypass default vendor mode, as we need a package not available in the
183                 // std module vendor folder.
184                 cfg.Env = append(os.Environ(), "GOFLAGS=-mod=mod")
185         }
186         cfg.Mode = packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo
187         pkgs, err := packages.Load(cfg, src)
188         if err != nil {
189                 return nil, err
190         }
191         if packages.PrintErrors(pkgs) > 0 || len(pkgs) != 1 {
192                 return nil, fmt.Errorf("failed to load source package")
193         }
194         pkg := pkgs[0]
195
196         if strings.Contains(prefix, "&") {
197                 prefix = strings.Replace(prefix, "&", pkg.Syntax[0].Name.Name, -1)
198         }
199
200         objsToUpdate := make(map[types.Object]bool)
201         var rename func(from types.Object)
202         rename = func(from types.Object) {
203                 if !objsToUpdate[from] {
204                         objsToUpdate[from] = true
205
206                         // Renaming a type that is used as an embedded field
207                         // requires renaming the field too. e.g.
208                         //      type T int // if we rename this to U..
209                         //      var s struct {T}
210                         //      print(s.T) // ...this must change too
211                         if _, ok := from.(*types.TypeName); ok {
212                                 for id, obj := range pkg.TypesInfo.Uses {
213                                         if obj == from {
214                                                 if field := pkg.TypesInfo.Defs[id]; field != nil {
215                                                         rename(field)
216                                                 }
217                                         }
218                                 }
219                         }
220                 }
221         }
222
223         // Rename each package-level object.
224         scope := pkg.Types.Scope()
225         for _, name := range scope.Names() {
226                 rename(scope.Lookup(name))
227         }
228
229         var out bytes.Buffer
230         if buildTags != "" {
231                 fmt.Fprintf(&out, "// +build %s\n\n", buildTags)
232         }
233
234         fmt.Fprintf(&out, "// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.\n")
235         if *outputFile != "" && buildTags == "" {
236                 fmt.Fprintf(&out, "//go:generate bundle %s\n", strings.Join(os.Args[1:], " "))
237         } else {
238                 fmt.Fprintf(&out, "//   $ bundle %s\n", strings.Join(os.Args[1:], " "))
239         }
240         fmt.Fprintf(&out, "\n")
241
242         // Concatenate package comments from all files...
243         for _, f := range pkg.Syntax {
244                 if doc := f.Doc.Text(); strings.TrimSpace(doc) != "" {
245                         for _, line := range strings.Split(doc, "\n") {
246                                 fmt.Fprintf(&out, "// %s\n", line)
247                         }
248                 }
249         }
250         // ...but don't let them become the actual package comment.
251         fmt.Fprintln(&out)
252
253         fmt.Fprintf(&out, "package %s\n\n", dstpkg)
254
255         // BUG(adonovan,shurcooL): bundle may generate incorrect code
256         // due to shadowing between identifiers and imported package names.
257         //
258         // The generated code will either fail to compile or
259         // (unlikely) compile successfully but have different behavior
260         // than the original package. The risk of this happening is higher
261         // when the original package has renamed imports (they're typically
262         // renamed in order to resolve a shadow inside that particular .go file).
263
264         // TODO(adonovan,shurcooL):
265         // - detect shadowing issues, and either return error or resolve them
266         // - preserve comments from the original import declarations.
267
268         // pkgStd and pkgExt are sets of printed import specs. This is done
269         // to deduplicate instances of the same import name and path.
270         var pkgStd = make(map[string]bool)
271         var pkgExt = make(map[string]bool)
272         for _, f := range pkg.Syntax {
273                 for _, imp := range f.Imports {
274                         path, err := strconv.Unquote(imp.Path.Value)
275                         if err != nil {
276                                 log.Fatalf("invalid import path string: %v", err) // Shouldn't happen here since packages.Load succeeded.
277                         }
278                         if path == dst {
279                                 continue
280                         }
281                         if newPath, ok := importMap[path]; ok {
282                                 path = newPath
283                         }
284
285                         var name string
286                         if imp.Name != nil {
287                                 name = imp.Name.Name
288                         }
289                         spec := fmt.Sprintf("%s %q", name, path)
290                         if isStandardImportPath(path) {
291                                 pkgStd[spec] = true
292                         } else {
293                                 pkgExt[spec] = true
294                         }
295                 }
296         }
297
298         // Print a single declaration that imports all necessary packages.
299         fmt.Fprintln(&out, "import (")
300         for p := range pkgStd {
301                 fmt.Fprintf(&out, "\t%s\n", p)
302         }
303         if len(pkgExt) > 0 {
304                 fmt.Fprintln(&out)
305         }
306         for p := range pkgExt {
307                 fmt.Fprintf(&out, "\t%s\n", p)
308         }
309         fmt.Fprint(&out, ")\n\n")
310
311         // Modify and print each file.
312         for _, f := range pkg.Syntax {
313                 // Update renamed identifiers.
314                 for id, obj := range pkg.TypesInfo.Defs {
315                         if objsToUpdate[obj] {
316                                 id.Name = prefix + obj.Name()
317                         }
318                 }
319                 for id, obj := range pkg.TypesInfo.Uses {
320                         if objsToUpdate[obj] {
321                                 id.Name = prefix + obj.Name()
322                         }
323                 }
324
325                 // For each qualified identifier that refers to the
326                 // destination package, remove the qualifier.
327                 // The "@@@." strings are removed in postprocessing.
328                 ast.Inspect(f, func(n ast.Node) bool {
329                         if sel, ok := n.(*ast.SelectorExpr); ok {
330                                 if id, ok := sel.X.(*ast.Ident); ok {
331                                         if obj, ok := pkg.TypesInfo.Uses[id].(*types.PkgName); ok {
332                                                 if obj.Imported().Path() == dst {
333                                                         id.Name = "@@@"
334                                                 }
335                                         }
336                                 }
337                         }
338                         return true
339                 })
340
341                 last := f.Package
342                 if len(f.Imports) > 0 {
343                         imp := f.Imports[len(f.Imports)-1]
344                         last = imp.End()
345                         if imp.Comment != nil {
346                                 if e := imp.Comment.End(); e > last {
347                                         last = e
348                                 }
349                         }
350                 }
351
352                 // Pretty-print package-level declarations.
353                 // but no package or import declarations.
354                 var buf bytes.Buffer
355                 for _, decl := range f.Decls {
356                         if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT {
357                                 continue
358                         }
359
360                         beg, end := sourceRange(decl)
361
362                         printComments(&out, f.Comments, last, beg)
363
364                         buf.Reset()
365                         format.Node(&buf, pkg.Fset, &printer.CommentedNode{Node: decl, Comments: f.Comments})
366                         // Remove each "@@@." in the output.
367                         // TODO(adonovan): not hygienic.
368                         out.Write(bytes.Replace(buf.Bytes(), []byte("@@@."), nil, -1))
369
370                         last = printSameLineComment(&out, f.Comments, pkg.Fset, end)
371
372                         out.WriteString("\n\n")
373                 }
374
375                 printLastComments(&out, f.Comments, last)
376         }
377
378         // Now format the entire thing.
379         result, err := format.Source(out.Bytes())
380         if err != nil {
381                 log.Fatalf("formatting failed: %v", err)
382         }
383
384         return result, nil
385 }
386
387 // sourceRange returns the [beg, end) interval of source code
388 // belonging to decl (incl. associated comments).
389 func sourceRange(decl ast.Decl) (beg, end token.Pos) {
390         beg = decl.Pos()
391         end = decl.End()
392
393         var doc, com *ast.CommentGroup
394
395         switch d := decl.(type) {
396         case *ast.GenDecl:
397                 doc = d.Doc
398                 if len(d.Specs) > 0 {
399                         switch spec := d.Specs[len(d.Specs)-1].(type) {
400                         case *ast.ValueSpec:
401                                 com = spec.Comment
402                         case *ast.TypeSpec:
403                                 com = spec.Comment
404                         }
405                 }
406         case *ast.FuncDecl:
407                 doc = d.Doc
408         }
409
410         if doc != nil {
411                 beg = doc.Pos()
412         }
413         if com != nil && com.End() > end {
414                 end = com.End()
415         }
416
417         return beg, end
418 }
419
420 func printComments(out *bytes.Buffer, comments []*ast.CommentGroup, pos, end token.Pos) {
421         for _, cg := range comments {
422                 if pos <= cg.Pos() && cg.Pos() < end {
423                         for _, c := range cg.List {
424                                 fmt.Fprintln(out, c.Text)
425                         }
426                         fmt.Fprintln(out)
427                 }
428         }
429 }
430
431 const infinity = 1 << 30
432
433 func printLastComments(out *bytes.Buffer, comments []*ast.CommentGroup, pos token.Pos) {
434         printComments(out, comments, pos, infinity)
435 }
436
437 func printSameLineComment(out *bytes.Buffer, comments []*ast.CommentGroup, fset *token.FileSet, pos token.Pos) token.Pos {
438         tf := fset.File(pos)
439         for _, cg := range comments {
440                 if pos <= cg.Pos() && tf.Line(cg.Pos()) == tf.Line(pos) {
441                         for _, c := range cg.List {
442                                 fmt.Fprintln(out, c.Text)
443                         }
444                         return cg.End()
445                 }
446         }
447         return pos
448 }
449
450 type flagFunc func(string)
451
452 func (f flagFunc) Set(s string) error {
453         f(s)
454         return nil
455 }
456
457 func (f flagFunc) String() string { return "" }