.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.1-0.20210319172145-bda8f5cee399 / internal / lsp / analysis / fillstruct / fillstruct.go
1 // Copyright 2020 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 fillstruct defines an Analyzer that automatically
6 // fills in a struct declaration with zero value elements for each field.
7 package fillstruct
8
9 import (
10         "bytes"
11         "fmt"
12         "go/ast"
13         "go/format"
14         "go/token"
15         "go/types"
16         "unicode"
17
18         "golang.org/x/tools/go/analysis"
19         "golang.org/x/tools/go/analysis/passes/inspect"
20         "golang.org/x/tools/go/ast/astutil"
21         "golang.org/x/tools/go/ast/inspector"
22         "golang.org/x/tools/internal/analysisinternal"
23         "golang.org/x/tools/internal/span"
24 )
25
26 const Doc = `note incomplete struct initializations
27
28 This analyzer provides diagnostics for any struct literals that do not have
29 any fields initialized. Because the suggested fix for this analysis is
30 expensive to compute, callers should compute it separately, using the
31 SuggestedFix function below.
32 `
33
34 var Analyzer = &analysis.Analyzer{
35         Name:             "fillstruct",
36         Doc:              Doc,
37         Requires:         []*analysis.Analyzer{inspect.Analyzer},
38         Run:              run,
39         RunDespiteErrors: true,
40 }
41
42 func run(pass *analysis.Pass) (interface{}, error) {
43         inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
44         nodeFilter := []ast.Node{(*ast.CompositeLit)(nil)}
45         inspect.Preorder(nodeFilter, func(n ast.Node) {
46                 info := pass.TypesInfo
47                 if info == nil {
48                         return
49                 }
50                 expr := n.(*ast.CompositeLit)
51
52                 var file *ast.File
53                 for _, f := range pass.Files {
54                         if f.Pos() <= expr.Pos() && expr.Pos() <= f.End() {
55                                 file = f
56                                 break
57                         }
58                 }
59                 if file == nil {
60                         return
61                 }
62
63                 typ := info.TypeOf(expr)
64                 if typ == nil {
65                         return
66                 }
67
68                 // Find reference to the type declaration of the struct being initialized.
69                 for {
70                         p, ok := typ.Underlying().(*types.Pointer)
71                         if !ok {
72                                 break
73                         }
74                         typ = p.Elem()
75                 }
76                 typ = typ.Underlying()
77
78                 obj, ok := typ.(*types.Struct)
79                 if !ok {
80                         return
81                 }
82                 fieldCount := obj.NumFields()
83
84                 // Skip any struct that is already populated or that has no fields.
85                 if fieldCount == 0 || fieldCount == len(expr.Elts) {
86                         return
87                 }
88
89                 var fillable bool
90                 for i := 0; i < fieldCount; i++ {
91                         field := obj.Field(i)
92                         // Ignore fields that are not accessible in the current package.
93                         if field.Pkg() != nil && field.Pkg() != pass.Pkg && !field.Exported() {
94                                 continue
95                         }
96                         fillable = true
97                 }
98                 if !fillable {
99                         return
100                 }
101                 var name string
102                 switch typ := expr.Type.(type) {
103                 case *ast.Ident:
104                         name = typ.Name
105                 case *ast.SelectorExpr:
106                         name = fmt.Sprintf("%s.%s", typ.X, typ.Sel.Name)
107                 default:
108                         name = "anonymous struct"
109                 }
110                 pass.Report(analysis.Diagnostic{
111                         Message: fmt.Sprintf("Fill %s", name),
112                         Pos:     expr.Pos(),
113                         End:     expr.End(),
114                 })
115         })
116         return nil, nil
117 }
118
119 func SuggestedFix(fset *token.FileSet, rng span.Range, content []byte, file *ast.File, pkg *types.Package, info *types.Info) (*analysis.SuggestedFix, error) {
120         pos := rng.Start // don't use the end
121
122         // TODO(rstambler): Using ast.Inspect would probably be more efficient than
123         // calling PathEnclosingInterval. Switch this approach.
124         path, _ := astutil.PathEnclosingInterval(file, pos, pos)
125         if len(path) == 0 {
126                 return nil, fmt.Errorf("no enclosing ast.Node")
127         }
128         var expr *ast.CompositeLit
129         for _, n := range path {
130                 if node, ok := n.(*ast.CompositeLit); ok {
131                         expr = node
132                         break
133                 }
134         }
135
136         if info == nil {
137                 return nil, fmt.Errorf("nil types.Info")
138         }
139         typ := info.TypeOf(expr)
140         if typ == nil {
141                 return nil, fmt.Errorf("no composite literal")
142         }
143
144         // Find reference to the type declaration of the struct being initialized.
145         for {
146                 p, ok := typ.Underlying().(*types.Pointer)
147                 if !ok {
148                         break
149                 }
150                 typ = p.Elem()
151         }
152         typ = typ.Underlying()
153
154         obj, ok := typ.(*types.Struct)
155         if !ok {
156                 return nil, fmt.Errorf("unexpected type %v (%T), expected *types.Struct", typ, typ)
157         }
158         fieldCount := obj.NumFields()
159
160         // Check which types have already been filled in. (we only want to fill in
161         // the unfilled types, or else we'll blat user-supplied details)
162         prefilledTypes := map[string]ast.Expr{}
163         for _, e := range expr.Elts {
164                 if kv, ok := e.(*ast.KeyValueExpr); ok {
165                         if key, ok := kv.Key.(*ast.Ident); ok {
166                                 prefilledTypes[key.Name] = kv.Value
167                         }
168                 }
169         }
170
171         // Use a new fileset to build up a token.File for the new composite
172         // literal. We need one line for foo{, one line for }, and one line for
173         // each field we're going to set. format.Node only cares about line
174         // numbers, so we don't need to set columns, and each line can be
175         // 1 byte long.
176         fakeFset := token.NewFileSet()
177         tok := fakeFset.AddFile("", -1, fieldCount+2)
178
179         line := 2 // account for 1-based lines and the left brace
180         var elts []ast.Expr
181         var fieldTyps []types.Type
182         for i := 0; i < fieldCount; i++ {
183                 field := obj.Field(i)
184                 // Ignore fields that are not accessible in the current package.
185                 if field.Pkg() != nil && field.Pkg() != pkg && !field.Exported() {
186                         fieldTyps = append(fieldTyps, nil)
187                         continue
188                 }
189                 fieldTyps = append(fieldTyps, field.Type())
190         }
191         matches := analysisinternal.FindMatchingIdents(fieldTyps, file, rng.Start, info, pkg)
192         for i, fieldTyp := range fieldTyps {
193                 if fieldTyp == nil {
194                         continue
195                 }
196
197                 tok.AddLine(line - 1) // add 1 byte per line
198                 if line > tok.LineCount() {
199                         panic(fmt.Sprintf("invalid line number %v (of %v) for fillstruct", line, tok.LineCount()))
200                 }
201                 pos := tok.LineStart(line)
202
203                 kv := &ast.KeyValueExpr{
204                         Key: &ast.Ident{
205                                 NamePos: pos,
206                                 Name:    obj.Field(i).Name(),
207                         },
208                         Colon: pos,
209                 }
210                 if expr, ok := prefilledTypes[obj.Field(i).Name()]; ok {
211                         kv.Value = expr
212                 } else {
213                         idents, ok := matches[fieldTyp]
214                         if !ok {
215                                 return nil, fmt.Errorf("invalid struct field type: %v", fieldTyp)
216                         }
217
218                         // Find the identifer whose name is most similar to the name of the field's key.
219                         // If we do not find any identifer that matches the pattern, generate a new value.
220                         // NOTE: We currently match on the name of the field key rather than the field type.
221                         value := analysisinternal.FindBestMatch(obj.Field(i).Name(), idents)
222                         if value == nil {
223                                 value = populateValue(fset, file, pkg, fieldTyp)
224                         }
225                         if value == nil {
226                                 return nil, nil
227                         }
228
229                         kv.Value = value
230                 }
231                 elts = append(elts, kv)
232                 line++
233         }
234
235         // If all of the struct's fields are unexported, we have nothing to do.
236         if len(elts) == 0 {
237                 return nil, fmt.Errorf("no elements to fill")
238         }
239
240         // Add the final line for the right brace. Offset is the number of
241         // bytes already added plus 1.
242         tok.AddLine(len(elts) + 1)
243         line = len(elts) + 2
244         if line > tok.LineCount() {
245                 panic(fmt.Sprintf("invalid line number %v (of %v) for fillstruct", line, tok.LineCount()))
246         }
247
248         cl := &ast.CompositeLit{
249                 Type:   expr.Type,
250                 Lbrace: tok.LineStart(1),
251                 Elts:   elts,
252                 Rbrace: tok.LineStart(line),
253         }
254
255         // Find the line on which the composite literal is declared.
256         split := bytes.Split(content, []byte("\n"))
257         lineNumber := fset.Position(expr.Lbrace).Line
258         firstLine := split[lineNumber-1] // lines are 1-indexed
259
260         // Trim the whitespace from the left of the line, and use the index
261         // to get the amount of whitespace on the left.
262         trimmed := bytes.TrimLeftFunc(firstLine, unicode.IsSpace)
263         index := bytes.Index(firstLine, trimmed)
264         whitespace := firstLine[:index]
265
266         // First pass through the formatter: turn the expr into a string.
267         var formatBuf bytes.Buffer
268         if err := format.Node(&formatBuf, fakeFset, cl); err != nil {
269                 return nil, fmt.Errorf("failed to run first format on:\n%s\ngot err: %v", cl.Type, err)
270         }
271         sug := indent(formatBuf.Bytes(), whitespace)
272
273         if len(prefilledTypes) > 0 {
274                 // Attempt a second pass through the formatter to line up columns.
275                 sourced, err := format.Source(sug)
276                 if err == nil {
277                         sug = indent(sourced, whitespace)
278                 }
279         }
280
281         return &analysis.SuggestedFix{
282                 TextEdits: []analysis.TextEdit{
283                         {
284                                 Pos:     expr.Pos(),
285                                 End:     expr.End(),
286                                 NewText: sug,
287                         },
288                 },
289         }, nil
290 }
291
292 // indent works line by line through str, indenting (prefixing) each line with
293 // ind.
294 func indent(str, ind []byte) []byte {
295         split := bytes.Split(str, []byte("\n"))
296         newText := bytes.NewBuffer(nil)
297         for i, s := range split {
298                 if len(s) == 0 {
299                         continue
300                 }
301                 // Don't add the extra indentation to the first line.
302                 if i != 0 {
303                         newText.Write(ind)
304                 }
305                 newText.Write(s)
306                 if i < len(split)-1 {
307                         newText.WriteByte('\n')
308                 }
309         }
310         return newText.Bytes()
311 }
312
313 // populateValue constructs an expression to fill the value of a struct field.
314 //
315 // When the type of a struct field is a basic literal or interface, we return
316 // default values. For other types, such as maps, slices, and channels, we create
317 // expressions rather than using default values.
318 //
319 // The reasoning here is that users will call fillstruct with the intention of
320 // initializing the struct, in which case setting these fields to nil has no effect.
321 func populateValue(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
322         under := typ
323         if n, ok := typ.(*types.Named); ok {
324                 under = n.Underlying()
325         }
326         switch u := under.(type) {
327         case *types.Basic:
328                 switch {
329                 case u.Info()&types.IsNumeric != 0:
330                         return &ast.BasicLit{Kind: token.INT, Value: "0"}
331                 case u.Info()&types.IsBoolean != 0:
332                         return &ast.Ident{Name: "false"}
333                 case u.Info()&types.IsString != 0:
334                         return &ast.BasicLit{Kind: token.STRING, Value: `""`}
335                 default:
336                         panic("unknown basic type")
337                 }
338         case *types.Map:
339                 k := analysisinternal.TypeExpr(fset, f, pkg, u.Key())
340                 v := analysisinternal.TypeExpr(fset, f, pkg, u.Elem())
341                 if k == nil || v == nil {
342                         return nil
343                 }
344                 return &ast.CompositeLit{
345                         Type: &ast.MapType{
346                                 Key:   k,
347                                 Value: v,
348                         },
349                 }
350         case *types.Slice:
351                 s := analysisinternal.TypeExpr(fset, f, pkg, u.Elem())
352                 if s == nil {
353                         return nil
354                 }
355                 return &ast.CompositeLit{
356                         Type: &ast.ArrayType{
357                                 Elt: s,
358                         },
359                 }
360         case *types.Array:
361                 a := analysisinternal.TypeExpr(fset, f, pkg, u.Elem())
362                 if a == nil {
363                         return nil
364                 }
365                 return &ast.CompositeLit{
366                         Type: &ast.ArrayType{
367                                 Elt: a,
368                                 Len: &ast.BasicLit{
369                                         Kind: token.INT, Value: fmt.Sprintf("%v", u.Len()),
370                                 },
371                         },
372                 }
373         case *types.Chan:
374                 v := analysisinternal.TypeExpr(fset, f, pkg, u.Elem())
375                 if v == nil {
376                         return nil
377                 }
378                 dir := ast.ChanDir(u.Dir())
379                 if u.Dir() == types.SendRecv {
380                         dir = ast.SEND | ast.RECV
381                 }
382                 return &ast.CallExpr{
383                         Fun: ast.NewIdent("make"),
384                         Args: []ast.Expr{
385                                 &ast.ChanType{
386                                         Dir:   dir,
387                                         Value: v,
388                                 },
389                         },
390                 }
391         case *types.Struct:
392                 s := analysisinternal.TypeExpr(fset, f, pkg, typ)
393                 if s == nil {
394                         return nil
395                 }
396                 return &ast.CompositeLit{
397                         Type: s,
398                 }
399         case *types.Signature:
400                 var params []*ast.Field
401                 for i := 0; i < u.Params().Len(); i++ {
402                         p := analysisinternal.TypeExpr(fset, f, pkg, u.Params().At(i).Type())
403                         if p == nil {
404                                 return nil
405                         }
406                         params = append(params, &ast.Field{
407                                 Type: p,
408                                 Names: []*ast.Ident{
409                                         {
410                                                 Name: u.Params().At(i).Name(),
411                                         },
412                                 },
413                         })
414                 }
415                 var returns []*ast.Field
416                 for i := 0; i < u.Results().Len(); i++ {
417                         r := analysisinternal.TypeExpr(fset, f, pkg, u.Results().At(i).Type())
418                         if r == nil {
419                                 return nil
420                         }
421                         returns = append(returns, &ast.Field{
422                                 Type: r,
423                         })
424                 }
425                 return &ast.FuncLit{
426                         Type: &ast.FuncType{
427                                 Params: &ast.FieldList{
428                                         List: params,
429                                 },
430                                 Results: &ast.FieldList{
431                                         List: returns,
432                                 },
433                         },
434                         Body: &ast.BlockStmt{},
435                 }
436         case *types.Pointer:
437                 switch u.Elem().(type) {
438                 case *types.Basic:
439                         return &ast.CallExpr{
440                                 Fun: &ast.Ident{
441                                         Name: "new",
442                                 },
443                                 Args: []ast.Expr{
444                                         &ast.Ident{
445                                                 Name: u.Elem().String(),
446                                         },
447                                 },
448                         }
449                 default:
450                         return &ast.UnaryExpr{
451                                 Op: token.AND,
452                                 X:  populateValue(fset, f, pkg, u.Elem()),
453                         }
454                 }
455         case *types.Interface:
456                 return ast.NewIdent("nil")
457         }
458         return nil
459 }