Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / mvdan.cc / gofumpt@v0.0.0-20200802201014-ab5a8192947d / gen.go
1 // Copyright (c) 2019, Daniel Martí <mvdan@mvdan.cc>
2 // See LICENSE for licensing information
3
4 // +build ignore
5
6 package main
7
8 import (
9         "bytes"
10         "context"
11         "encoding/json"
12         "fmt"
13         "io/ioutil"
14         "os"
15         "os/exec"
16         "path/filepath"
17         "strings"
18         "time"
19 )
20
21 func main() {
22         pkgs, err := listPackages(context.TODO(), nil,
23                 "cmd/gofmt",
24
25                 // These are internal cmd dependencies. Copy them.
26                 "cmd/internal/diff",
27
28                 "golang.org/x/tools/cmd/goimports",
29
30                 // These are internal goimports dependencies. Copy them.
31                 "golang.org/x/tools/internal/event",
32                 "golang.org/x/tools/internal/event/core",
33                 "golang.org/x/tools/internal/event/keys",
34                 "golang.org/x/tools/internal/event/label",
35                 "golang.org/x/tools/internal/fastwalk",
36                 "golang.org/x/tools/internal/gocommand",
37                 "golang.org/x/tools/internal/gopathwalk",
38                 "golang.org/x/tools/internal/imports",
39                 "golang.org/x/tools/internal/module",
40                 "golang.org/x/tools/internal/semver",
41                 "golang.org/x/tools/internal/telemetry/event",
42         )
43         if err != nil {
44                 panic(err)
45         }
46         for _, pkg := range pkgs {
47                 switch pkg.ImportPath {
48                 case "cmd/gofmt":
49                         copyGofmt(pkg)
50                 case "golang.org/x/tools/cmd/goimports":
51                         copyGoimports(pkg)
52                 default:
53                         parts := strings.Split(pkg.ImportPath, "/")
54                         if parts[0] == "cmd" {
55                                 copyInternal(pkg, filepath.Join(parts[1:]...))
56                         } else {
57                                 dir := filepath.Join(append([]string{"gofumports"}, parts[3:]...)...)
58                                 copyInternal(pkg, dir)
59                         }
60                 }
61         }
62 }
63
64 type Module struct {
65         Path      string       // module path
66         Version   string       // module version
67         Versions  []string     // available module versions (with -versions)
68         Replace   *Module      // replaced by this module
69         Time      *time.Time   // time version was created
70         Update    *Module      // available update, if any (with -u)
71         Main      bool         // is this the main module?
72         Indirect  bool         // is this module only an indirect dependency of main module?
73         Dir       string       // directory holding files for this module, if any
74         GoMod     string       // path to go.mod file used when loading this module, if any
75         GoVersion string       // go version used in module
76         Error     *ModuleError // error loading module
77 }
78
79 type ModuleError struct {
80         Err string // the error itself
81 }
82
83 type Package struct {
84         Dir           string   // directory containing package sources
85         ImportPath    string   // import path of package in dir
86         ImportComment string   // path in import comment on package statement
87         Name          string   // package name
88         Doc           string   // package documentation string
89         Target        string   // install path
90         Shlib         string   // the shared library that contains this package (only set when -linkshared)
91         Goroot        bool     // is this package in the Go root?
92         Standard      bool     // is this package part of the standard Go library?
93         Stale         bool     // would 'go install' do anything for this package?
94         StaleReason   string   // explanation for Stale==true
95         Root          string   // Go root or Go path dir containing this package
96         ConflictDir   string   // this directory shadows Dir in $GOPATH
97         BinaryOnly    bool     // binary-only package (no longer supported)
98         ForTest       string   // package is only for use in named test
99         Export        string   // file containing export data (when using -export)
100         Module        *Module  // info about package's containing module, if any (can be nil)
101         Match         []string // command-line patterns matching this package
102         DepOnly       bool     // package is only a dependency, not explicitly listed
103
104         // Source files
105         GoFiles         []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
106         CgoFiles        []string // .go source files that import "C"
107         CompiledGoFiles []string // .go files presented to compiler (when using -compiled)
108         IgnoredGoFiles  []string // .go source files ignored due to build constraints
109         CFiles          []string // .c source files
110         CXXFiles        []string // .cc, .cxx and .cpp source files
111         MFiles          []string // .m source files
112         HFiles          []string // .h, .hh, .hpp and .hxx source files
113         FFiles          []string // .f, .F, .for and .f90 Fortran source files
114         SFiles          []string // .s source files
115         SwigFiles       []string // .swig files
116         SwigCXXFiles    []string // .swigcxx files
117         SysoFiles       []string // .syso object files to add to archive
118         TestGoFiles     []string // _test.go files in package
119         XTestGoFiles    []string // _test.go files outside package
120
121         // Cgo directives
122         CgoCFLAGS    []string // cgo: flags for C compiler
123         CgoCPPFLAGS  []string // cgo: flags for C preprocessor
124         CgoCXXFLAGS  []string // cgo: flags for C++ compiler
125         CgoFFLAGS    []string // cgo: flags for Fortran compiler
126         CgoLDFLAGS   []string // cgo: flags for linker
127         CgoPkgConfig []string // cgo: pkg-config names
128
129         // Dependency information
130         Imports      []string          // import paths used by this package
131         ImportMap    map[string]string // map from source import to ImportPath (identity entries omitted)
132         Deps         []string          // all (recursively) imported dependencies
133         TestImports  []string          // imports from TestGoFiles
134         XTestImports []string          // imports from XTestGoFiles
135
136         // Error information
137         Incomplete bool            // this package or a dependency has an error
138         Error      *PackageError   // error loading package
139         DepsErrors []*PackageError // errors loading dependencies
140 }
141
142 type PackageError struct {
143         ImportStack []string // shortest path from package named on command line to this one
144         Pos         string   // position of error (if present, file:line:col)
145         Err         string   // the error itself
146 }
147
148 func getEnv(env []string, name string) string {
149         for _, kv := range env {
150                 if i := strings.IndexByte(kv, '='); i > 0 && name == kv[:i] {
151                         return kv[i+1:]
152                 }
153         }
154         return ""
155 }
156
157 // listPackages is a wrapper for 'go list -json -e', which can take arbitrary
158 // environment variables and arguments as input. The working directory can be
159 // fed by adding $PWD to env; otherwise, it will default to the current
160 // directory.
161 //
162 // Since -e is used, the returned error will only be non-nil if a JSON result
163 // could not be obtained. Such examples are if the Go command is not installed,
164 // or if invalid flags are used as arguments.
165 //
166 // Errors encountered when loading packages will be returned for each package,
167 // in the form of PackageError. See 'go help list'.
168 func listPackages(ctx context.Context, env []string, args ...string) (pkgs []*Package, finalErr error) {
169         goArgs := append([]string{"list", "-json", "-e"}, args...)
170         cmd := exec.CommandContext(ctx, "go", goArgs...)
171         cmd.Env = env
172         cmd.Dir = getEnv(env, "PWD")
173
174         stdout, err := cmd.StdoutPipe()
175         if err != nil {
176                 return nil, err
177         }
178         var stderrBuf bytes.Buffer
179         cmd.Stderr = &stderrBuf
180         defer func() {
181                 if finalErr != nil && stderrBuf.Len() > 0 {
182                         // TODO: wrap? but the format is backwards, given that
183                         // stderr is likely multi-line
184                         finalErr = fmt.Errorf("%v\n%s", finalErr, stderrBuf.Bytes())
185                 }
186         }()
187
188         if err := cmd.Start(); err != nil {
189                 return nil, err
190         }
191         dec := json.NewDecoder(stdout)
192         for dec.More() {
193                 var pkg Package
194                 if err := dec.Decode(&pkg); err != nil {
195                         return nil, err
196                 }
197                 pkgs = append(pkgs, &pkg)
198         }
199         if err := cmd.Wait(); err != nil {
200                 return nil, err
201         }
202         return pkgs, nil
203 }
204
205 func readFile(path string) string {
206         body, err := ioutil.ReadFile(path)
207         if err != nil {
208                 panic(err)
209         }
210         return string(body)
211 }
212
213 func writeFile(path, body string) {
214         if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
215                 panic(err)
216         }
217         if err := ioutil.WriteFile(path, []byte(body), 0o644); err != nil {
218                 panic(err)
219         }
220 }
221
222 func sourceFiles(pkg *Package) (paths []string) {
223         var combined []string
224         for _, list := range [...][]string{
225                 pkg.GoFiles,
226                 pkg.IgnoredGoFiles,
227         } {
228                 for _, name := range list {
229                         if strings.HasSuffix(name, "_test.go") {
230                                 // IgnoredGoFiles can contain test files too.
231                                 continue
232                         }
233                         combined = append(combined, filepath.Join(pkg.Dir, name))
234                 }
235         }
236         return combined
237 }
238
239 const extraImport = `gformat "mvdan.cc/gofumpt/format"; `
240
241 const extraSrcLangVersion = `` +
242         `if *langVersion == "" {
243                 out, err := exec.Command("go", "list", "-m", "-f", "{{.GoVersion}}").Output()
244                 out = bytes.TrimSpace(out)
245                 if err == nil && len(out) > 0 {
246                         *langVersion = string(out)
247                 }
248         }`
249
250 func copyGofmt(pkg *Package) {
251         const extraSrc = `
252                 // This is the only gofumpt change on gofmt's codebase, besides changing
253                 // the name in the usage text.
254                 ` + extraSrcLangVersion + `
255                 gformat.File(fileSet, file, gformat.Options{
256                         LangVersion: *langVersion,
257                         ExtraRules:  *extraRules,
258                 })
259                 `
260         for _, path := range sourceFiles(pkg) {
261                 body := readFile(path)
262                 body = fixImports(body)
263                 name := filepath.Base(path)
264                 switch name {
265                 case "doc.go":
266                         continue // we have our own
267                 case "gofmt.go":
268                         if i := strings.Index(body, "\t\"mvdan.cc/gofumpt"); i > 0 {
269                                 body = body[:i] + "\n" + extraImport + "\n" + body[i:]
270                         }
271                         if i := strings.Index(body, "res, err := format("); i > 0 {
272                                 body = body[:i] + "\n" + extraSrc + "\n" + body[i:]
273                         }
274                 }
275                 body = strings.Replace(body, "gofmt", "gofumpt", -1)
276                 writeFile(name, body)
277         }
278 }
279
280 func copyGoimports(pkg *Package) {
281         const extraSrc = `
282                 // This is the only gofumpt change on goimports's codebase, besides changing
283                 // the name in the usage text.
284                 ` + extraSrcLangVersion + `
285                 res, err = gformat.Source(res, gformat.Options{LangVersion: *langVersion})
286                 if err != nil {
287                         return err
288                 }
289                 `
290         for _, path := range sourceFiles(pkg) {
291                 body := readFile(path)
292                 body = fixImports(body)
293                 name := filepath.Base(path)
294                 switch name {
295                 case "doc.go":
296                         continue // we have our own
297                 case "goimports.go":
298                         if i := strings.Index(body, "\t\"mvdan.cc/gofumpt"); i > 0 {
299                                 body = body[:i] + "\n" + extraImport + "\n" + body[i:]
300                         }
301                         if i := strings.Index(body, "if !bytes.Equal"); i > 0 {
302                                 body = body[:i] + "\n" + extraSrc + "\n" + body[i:]
303                         }
304                 }
305                 body = strings.Replace(body, "goimports", "gofumports", -1)
306
307                 writeFile(filepath.Join("gofumports", name), body)
308         }
309 }
310
311 func copyInternal(pkg *Package, dir string) {
312         for _, path := range sourceFiles(pkg) {
313                 body := readFile(path)
314                 body = fixImports(body)
315                 name := filepath.Base(path)
316                 writeFile(filepath.Join(dir, name), body)
317         }
318 }
319
320 func fixImports(body string) string {
321         body = strings.Replace(body,
322                 "golang.org/x/tools/internal/",
323                 "mvdan.cc/gofumpt/gofumports/internal/",
324                 -1)
325         body = strings.Replace(body,
326                 "cmd/internal/",
327                 "mvdan.cc/gofumpt/internal/",
328                 -1)
329         return body
330 }