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