Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.0.1-2020.1.5 / cmd / keyify / keyify.go
1 // keyify transforms unkeyed struct literals into a keyed ones.
2 package main
3
4 import (
5         "bytes"
6         "encoding/json"
7         "flag"
8         "fmt"
9         "go/ast"
10         "go/build"
11         "go/constant"
12         "go/printer"
13         "go/token"
14         "go/types"
15         "log"
16         "os"
17         "path/filepath"
18
19         "honnef.co/go/tools/version"
20
21         "golang.org/x/tools/go/ast/astutil"
22         "golang.org/x/tools/go/buildutil"
23         "golang.org/x/tools/go/loader"
24 )
25
26 var (
27         fRecursive bool
28         fOneLine   bool
29         fJSON      bool
30         fMinify    bool
31         fModified  bool
32         fVersion   bool
33 )
34
35 func init() {
36         flag.BoolVar(&fRecursive, "r", false, "keyify struct initializers recursively")
37         flag.BoolVar(&fOneLine, "o", false, "print new struct initializer on a single line")
38         flag.BoolVar(&fJSON, "json", false, "print new struct initializer as JSON")
39         flag.BoolVar(&fMinify, "m", false, "omit fields that are set to their zero value")
40         flag.BoolVar(&fModified, "modified", false, "read an archive of modified files from standard input")
41         flag.BoolVar(&fVersion, "version", false, "Print version and exit")
42 }
43
44 func usage() {
45         fmt.Printf("Usage: %s [flags] <position>\n\n", os.Args[0])
46         flag.PrintDefaults()
47 }
48
49 func main() {
50         log.SetFlags(0)
51         flag.Usage = usage
52         flag.Parse()
53
54         if fVersion {
55                 version.Print()
56                 os.Exit(0)
57         }
58
59         if flag.NArg() != 1 {
60                 flag.Usage()
61                 os.Exit(2)
62         }
63         pos := flag.Args()[0]
64         name, start, _, err := parsePos(pos)
65         if err != nil {
66                 log.Fatal(err)
67         }
68         eval, err := filepath.EvalSymlinks(name)
69         if err != nil {
70                 log.Fatal(err)
71         }
72         name, err = filepath.Abs(eval)
73         if err != nil {
74                 log.Fatal(err)
75         }
76         cwd, err := os.Getwd()
77         if err != nil {
78                 log.Fatal(err)
79         }
80         ctx := &build.Default
81         if fModified {
82                 overlay, err := buildutil.ParseOverlayArchive(os.Stdin)
83                 if err != nil {
84                         log.Fatal(err)
85                 }
86                 ctx = buildutil.OverlayContext(ctx, overlay)
87         }
88         bpkg, err := buildutil.ContainingPackage(ctx, cwd, name)
89         if err != nil {
90                 log.Fatal(err)
91         }
92         conf := &loader.Config{
93                 Build: ctx,
94         }
95         conf.TypeCheckFuncBodies = func(s string) bool {
96                 return s == bpkg.ImportPath || s == bpkg.ImportPath+"_test"
97         }
98         conf.ImportWithTests(bpkg.ImportPath)
99         lprog, err := conf.Load()
100         if err != nil {
101                 log.Fatal(err)
102         }
103         var tf *token.File
104         var af *ast.File
105         var pkg *loader.PackageInfo
106 outer:
107         for _, pkg = range lprog.InitialPackages() {
108                 for _, ff := range pkg.Files {
109                         file := lprog.Fset.File(ff.Pos())
110                         if file.Name() == name {
111                                 af = ff
112                                 tf = file
113                                 break outer
114                         }
115                 }
116         }
117         if tf == nil {
118                 log.Fatalf("couldn't find file %s", name)
119         }
120         tstart, tend, err := fileOffsetToPos(tf, start, start)
121         if err != nil {
122                 log.Fatal(err)
123         }
124         path, _ := astutil.PathEnclosingInterval(af, tstart, tend)
125         var complit *ast.CompositeLit
126         for _, p := range path {
127                 if p, ok := p.(*ast.CompositeLit); ok {
128                         complit = p
129                         break
130                 }
131         }
132         if complit == nil {
133                 log.Fatal("no composite literal found near point")
134         }
135         if len(complit.Elts) == 0 {
136                 printComplit(complit, complit, lprog.Fset, lprog.Fset)
137                 return
138         }
139         if _, ok := complit.Elts[0].(*ast.KeyValueExpr); ok {
140                 lit := complit
141                 if fOneLine {
142                         lit = copyExpr(complit, 1).(*ast.CompositeLit)
143                 }
144                 printComplit(complit, lit, lprog.Fset, lprog.Fset)
145                 return
146         }
147         _, ok := pkg.TypeOf(complit).Underlying().(*types.Struct)
148         if !ok {
149                 log.Fatal("not a struct initialiser")
150                 return
151         }
152
153         newComplit, lines := keyify(pkg, complit)
154         newFset := token.NewFileSet()
155         newFile := newFset.AddFile("", -1, lines)
156         for i := 1; i <= lines; i++ {
157                 newFile.AddLine(i)
158         }
159         printComplit(complit, newComplit, lprog.Fset, newFset)
160 }
161
162 func keyify(
163         pkg *loader.PackageInfo,
164         complit *ast.CompositeLit,
165 ) (*ast.CompositeLit, int) {
166         var calcPos func(int) token.Pos
167         if fOneLine {
168                 calcPos = func(int) token.Pos { return token.Pos(1) }
169         } else {
170                 calcPos = func(i int) token.Pos { return token.Pos(2 + i) }
171         }
172
173         st, _ := pkg.TypeOf(complit).Underlying().(*types.Struct)
174         newComplit := &ast.CompositeLit{
175                 Type:   complit.Type,
176                 Lbrace: 1,
177                 Rbrace: token.Pos(st.NumFields() + 2),
178         }
179         if fOneLine {
180                 newComplit.Rbrace = 1
181         }
182         numLines := 2 + st.NumFields()
183         n := 0
184         for i := 0; i < st.NumFields(); i++ {
185                 field := st.Field(i)
186                 val := complit.Elts[i]
187                 if fRecursive {
188                         if val2, ok := val.(*ast.CompositeLit); ok {
189                                 if _, ok := pkg.TypeOf(val2.Type).Underlying().(*types.Struct); ok {
190                                         // FIXME(dh): this code is obviously wrong. But
191                                         // what were we intending to do here?
192                                         var lines int
193                                         numLines += lines
194                                         //lint:ignore SA4006 See FIXME above.
195                                         val, lines = keyify(pkg, val2)
196                                 }
197                         }
198                 }
199                 _, isIface := st.Field(i).Type().Underlying().(*types.Interface)
200                 if fMinify && (isNil(val, pkg) || (!isIface && isZero(val, pkg))) {
201                         continue
202                 }
203                 elt := &ast.KeyValueExpr{
204                         Key:   &ast.Ident{NamePos: calcPos(n), Name: field.Name()},
205                         Value: copyExpr(val, calcPos(n)),
206                 }
207                 newComplit.Elts = append(newComplit.Elts, elt)
208                 n++
209         }
210         return newComplit, numLines
211 }
212
213 func isNil(val ast.Expr, pkg *loader.PackageInfo) bool {
214         ident, ok := val.(*ast.Ident)
215         if !ok {
216                 return false
217         }
218         if _, ok := pkg.ObjectOf(ident).(*types.Nil); ok {
219                 return true
220         }
221         if c, ok := pkg.ObjectOf(ident).(*types.Const); ok {
222                 if c.Val().Kind() != constant.Bool {
223                         return false
224                 }
225                 return !constant.BoolVal(c.Val())
226         }
227         return false
228 }
229
230 func isZero(val ast.Expr, pkg *loader.PackageInfo) bool {
231         switch val := val.(type) {
232         case *ast.BasicLit:
233                 switch val.Value {
234                 case `""`, "``", "0", "0.0", "0i", "0.":
235                         return true
236                 default:
237                         return false
238                 }
239         case *ast.Ident:
240                 return isNil(val, pkg)
241         case *ast.CompositeLit:
242                 typ := pkg.TypeOf(val.Type)
243                 if typ == nil {
244                         return false
245                 }
246                 isIface := false
247                 switch typ := typ.Underlying().(type) {
248                 case *types.Struct:
249                 case *types.Array:
250                         _, isIface = typ.Elem().Underlying().(*types.Interface)
251                 default:
252                         return false
253                 }
254                 for _, elt := range val.Elts {
255                         if isNil(elt, pkg) || (!isIface && !isZero(elt, pkg)) {
256                                 return false
257                         }
258                 }
259                 return true
260         }
261         return false
262 }
263
264 func printComplit(oldlit, newlit *ast.CompositeLit, oldfset, newfset *token.FileSet) {
265         buf := &bytes.Buffer{}
266         cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8}
267         _ = cfg.Fprint(buf, newfset, newlit)
268         if fJSON {
269                 output := struct {
270                         Start       int    `json:"start"`
271                         End         int    `json:"end"`
272                         Replacement string `json:"replacement"`
273                 }{
274                         oldfset.Position(oldlit.Pos()).Offset,
275                         oldfset.Position(oldlit.End()).Offset,
276                         buf.String(),
277                 }
278                 _ = json.NewEncoder(os.Stdout).Encode(output)
279         } else {
280                 fmt.Println(buf.String())
281         }
282 }
283
284 func copyExpr(expr ast.Expr, line token.Pos) ast.Expr {
285         switch expr := expr.(type) {
286         case *ast.BasicLit:
287                 cp := *expr
288                 cp.ValuePos = 0
289                 return &cp
290         case *ast.BinaryExpr:
291                 cp := *expr
292                 cp.X = copyExpr(cp.X, line)
293                 cp.OpPos = 0
294                 cp.Y = copyExpr(cp.Y, line)
295                 return &cp
296         case *ast.CallExpr:
297                 cp := *expr
298                 cp.Fun = copyExpr(cp.Fun, line)
299                 cp.Lparen = 0
300                 for i, v := range cp.Args {
301                         cp.Args[i] = copyExpr(v, line)
302                 }
303                 if cp.Ellipsis != 0 {
304                         cp.Ellipsis = line
305                 }
306                 cp.Rparen = 0
307                 return &cp
308         case *ast.CompositeLit:
309                 cp := *expr
310                 cp.Type = copyExpr(cp.Type, line)
311                 cp.Lbrace = 0
312                 for i, v := range cp.Elts {
313                         cp.Elts[i] = copyExpr(v, line)
314                 }
315                 cp.Rbrace = 0
316                 return &cp
317         case *ast.Ident:
318                 cp := *expr
319                 cp.NamePos = 0
320                 return &cp
321         case *ast.IndexExpr:
322                 cp := *expr
323                 cp.X = copyExpr(cp.X, line)
324                 cp.Lbrack = 0
325                 cp.Index = copyExpr(cp.Index, line)
326                 cp.Rbrack = 0
327                 return &cp
328         case *ast.KeyValueExpr:
329                 cp := *expr
330                 cp.Key = copyExpr(cp.Key, line)
331                 cp.Colon = 0
332                 cp.Value = copyExpr(cp.Value, line)
333                 return &cp
334         case *ast.ParenExpr:
335                 cp := *expr
336                 cp.Lparen = 0
337                 cp.X = copyExpr(cp.X, line)
338                 cp.Rparen = 0
339                 return &cp
340         case *ast.SelectorExpr:
341                 cp := *expr
342                 cp.X = copyExpr(cp.X, line)
343                 cp.Sel = copyExpr(cp.Sel, line).(*ast.Ident)
344                 return &cp
345         case *ast.SliceExpr:
346                 cp := *expr
347                 cp.X = copyExpr(cp.X, line)
348                 cp.Lbrack = 0
349                 cp.Low = copyExpr(cp.Low, line)
350                 cp.High = copyExpr(cp.High, line)
351                 cp.Max = copyExpr(cp.Max, line)
352                 cp.Rbrack = 0
353                 return &cp
354         case *ast.StarExpr:
355                 cp := *expr
356                 cp.Star = 0
357                 cp.X = copyExpr(cp.X, line)
358                 return &cp
359         case *ast.TypeAssertExpr:
360                 cp := *expr
361                 cp.X = copyExpr(cp.X, line)
362                 cp.Lparen = 0
363                 cp.Type = copyExpr(cp.Type, line)
364                 cp.Rparen = 0
365                 return &cp
366         case *ast.UnaryExpr:
367                 cp := *expr
368                 cp.OpPos = 0
369                 cp.X = copyExpr(cp.X, line)
370                 return &cp
371         case *ast.MapType:
372                 cp := *expr
373                 cp.Map = 0
374                 cp.Key = copyExpr(cp.Key, line)
375                 cp.Value = copyExpr(cp.Value, line)
376                 return &cp
377         case *ast.ArrayType:
378                 cp := *expr
379                 cp.Lbrack = 0
380                 cp.Len = copyExpr(cp.Len, line)
381                 cp.Elt = copyExpr(cp.Elt, line)
382                 return &cp
383         case *ast.Ellipsis:
384                 cp := *expr
385                 cp.Elt = copyExpr(cp.Elt, line)
386                 cp.Ellipsis = line
387                 return &cp
388         case *ast.InterfaceType:
389                 cp := *expr
390                 cp.Interface = 0
391                 return &cp
392         case *ast.StructType:
393                 cp := *expr
394                 cp.Struct = 0
395                 return &cp
396         case *ast.FuncLit:
397                 return expr
398         case *ast.ChanType:
399                 cp := *expr
400                 cp.Arrow = 0
401                 cp.Begin = 0
402                 cp.Value = copyExpr(cp.Value, line)
403                 return &cp
404         case nil:
405                 return nil
406         default:
407                 panic(fmt.Sprintf("shouldn't happen: unknown ast.Expr of type %T", expr))
408         }
409 }