.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.0 / internal / lsp / analysis / fillstruct / fillstruct.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/internal/lsp/analysis/fillstruct/fillstruct.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.0/internal/lsp/analysis/fillstruct/fillstruct.go
new file mode 100644 (file)
index 0000000..36a63a1
--- /dev/null
@@ -0,0 +1,459 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package fillstruct defines an Analyzer that automatically
+// fills in a struct declaration with zero value elements for each field.
+package fillstruct
+
+import (
+       "bytes"
+       "fmt"
+       "go/ast"
+       "go/format"
+       "go/token"
+       "go/types"
+       "unicode"
+
+       "golang.org/x/tools/go/analysis"
+       "golang.org/x/tools/go/analysis/passes/inspect"
+       "golang.org/x/tools/go/ast/astutil"
+       "golang.org/x/tools/go/ast/inspector"
+       "golang.org/x/tools/internal/analysisinternal"
+       "golang.org/x/tools/internal/span"
+)
+
+const Doc = `note incomplete struct initializations
+
+This analyzer provides diagnostics for any struct literals that do not have
+any fields initialized. Because the suggested fix for this analysis is
+expensive to compute, callers should compute it separately, using the
+SuggestedFix function below.
+`
+
+var Analyzer = &analysis.Analyzer{
+       Name:             "fillstruct",
+       Doc:              Doc,
+       Requires:         []*analysis.Analyzer{inspect.Analyzer},
+       Run:              run,
+       RunDespiteErrors: true,
+}
+
+func run(pass *analysis.Pass) (interface{}, error) {
+       inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+       nodeFilter := []ast.Node{(*ast.CompositeLit)(nil)}
+       inspect.Preorder(nodeFilter, func(n ast.Node) {
+               info := pass.TypesInfo
+               if info == nil {
+                       return
+               }
+               expr := n.(*ast.CompositeLit)
+
+               var file *ast.File
+               for _, f := range pass.Files {
+                       if f.Pos() <= expr.Pos() && expr.Pos() <= f.End() {
+                               file = f
+                               break
+                       }
+               }
+               if file == nil {
+                       return
+               }
+
+               typ := info.TypeOf(expr)
+               if typ == nil {
+                       return
+               }
+
+               // Find reference to the type declaration of the struct being initialized.
+               for {
+                       p, ok := typ.Underlying().(*types.Pointer)
+                       if !ok {
+                               break
+                       }
+                       typ = p.Elem()
+               }
+               typ = typ.Underlying()
+
+               obj, ok := typ.(*types.Struct)
+               if !ok {
+                       return
+               }
+               fieldCount := obj.NumFields()
+
+               // Skip any struct that is already populated or that has no fields.
+               if fieldCount == 0 || fieldCount == len(expr.Elts) {
+                       return
+               }
+
+               var fillable bool
+               for i := 0; i < fieldCount; i++ {
+                       field := obj.Field(i)
+                       // Ignore fields that are not accessible in the current package.
+                       if field.Pkg() != nil && field.Pkg() != pass.Pkg && !field.Exported() {
+                               continue
+                       }
+                       fillable = true
+               }
+               if !fillable {
+                       return
+               }
+               var name string
+               switch typ := expr.Type.(type) {
+               case *ast.Ident:
+                       name = typ.Name
+               case *ast.SelectorExpr:
+                       name = fmt.Sprintf("%s.%s", typ.X, typ.Sel.Name)
+               default:
+                       name = "anonymous struct"
+               }
+               pass.Report(analysis.Diagnostic{
+                       Message: fmt.Sprintf("Fill %s", name),
+                       Pos:     expr.Pos(),
+                       End:     expr.End(),
+               })
+       })
+       return nil, nil
+}
+
+func SuggestedFix(fset *token.FileSet, rng span.Range, content []byte, file *ast.File, pkg *types.Package, info *types.Info) (*analysis.SuggestedFix, error) {
+       pos := rng.Start // don't use the end
+
+       // TODO(rstambler): Using ast.Inspect would probably be more efficient than
+       // calling PathEnclosingInterval. Switch this approach.
+       path, _ := astutil.PathEnclosingInterval(file, pos, pos)
+       if len(path) == 0 {
+               return nil, fmt.Errorf("no enclosing ast.Node")
+       }
+       var expr *ast.CompositeLit
+       for _, n := range path {
+               if node, ok := n.(*ast.CompositeLit); ok {
+                       expr = node
+                       break
+               }
+       }
+
+       if info == nil {
+               return nil, fmt.Errorf("nil types.Info")
+       }
+       typ := info.TypeOf(expr)
+       if typ == nil {
+               return nil, fmt.Errorf("no composite literal")
+       }
+
+       // Find reference to the type declaration of the struct being initialized.
+       for {
+               p, ok := typ.Underlying().(*types.Pointer)
+               if !ok {
+                       break
+               }
+               typ = p.Elem()
+       }
+       typ = typ.Underlying()
+
+       obj, ok := typ.(*types.Struct)
+       if !ok {
+               return nil, fmt.Errorf("unexpected type %v (%T), expected *types.Struct", typ, typ)
+       }
+       fieldCount := obj.NumFields()
+
+       // Check which types have already been filled in. (we only want to fill in
+       // the unfilled types, or else we'll blat user-supplied details)
+       prefilledTypes := map[string]ast.Expr{}
+       for _, e := range expr.Elts {
+               if kv, ok := e.(*ast.KeyValueExpr); ok {
+                       if key, ok := kv.Key.(*ast.Ident); ok {
+                               prefilledTypes[key.Name] = kv.Value
+                       }
+               }
+       }
+
+       // Use a new fileset to build up a token.File for the new composite
+       // literal. We need one line for foo{, one line for }, and one line for
+       // each field we're going to set. format.Node only cares about line
+       // numbers, so we don't need to set columns, and each line can be
+       // 1 byte long.
+       fakeFset := token.NewFileSet()
+       tok := fakeFset.AddFile("", -1, fieldCount+2)
+
+       line := 2 // account for 1-based lines and the left brace
+       var elts []ast.Expr
+       var fieldTyps []types.Type
+       for i := 0; i < fieldCount; i++ {
+               field := obj.Field(i)
+               // Ignore fields that are not accessible in the current package.
+               if field.Pkg() != nil && field.Pkg() != pkg && !field.Exported() {
+                       fieldTyps = append(fieldTyps, nil)
+                       continue
+               }
+               fieldTyps = append(fieldTyps, field.Type())
+       }
+       matches := analysisinternal.FindMatchingIdents(fieldTyps, file, rng.Start, info, pkg)
+       for i, fieldTyp := range fieldTyps {
+               if fieldTyp == nil {
+                       continue
+               }
+
+               tok.AddLine(line - 1) // add 1 byte per line
+               if line > tok.LineCount() {
+                       panic(fmt.Sprintf("invalid line number %v (of %v) for fillstruct", line, tok.LineCount()))
+               }
+               pos := tok.LineStart(line)
+
+               kv := &ast.KeyValueExpr{
+                       Key: &ast.Ident{
+                               NamePos: pos,
+                               Name:    obj.Field(i).Name(),
+                       },
+                       Colon: pos,
+               }
+               if expr, ok := prefilledTypes[obj.Field(i).Name()]; ok {
+                       kv.Value = expr
+               } else {
+                       idents, ok := matches[fieldTyp]
+                       if !ok {
+                               return nil, fmt.Errorf("invalid struct field type: %v", fieldTyp)
+                       }
+
+                       // Find the identifer whose name is most similar to the name of the field's key.
+                       // If we do not find any identifer that matches the pattern, generate a new value.
+                       // NOTE: We currently match on the name of the field key rather than the field type.
+                       value := analysisinternal.FindBestMatch(obj.Field(i).Name(), idents)
+                       if value == nil {
+                               value = populateValue(fset, file, pkg, fieldTyp)
+                       }
+                       if value == nil {
+                               return nil, nil
+                       }
+
+                       kv.Value = value
+               }
+               elts = append(elts, kv)
+               line++
+       }
+
+       // If all of the struct's fields are unexported, we have nothing to do.
+       if len(elts) == 0 {
+               return nil, fmt.Errorf("no elements to fill")
+       }
+
+       // Add the final line for the right brace. Offset is the number of
+       // bytes already added plus 1.
+       tok.AddLine(len(elts) + 1)
+       line = len(elts) + 2
+       if line > tok.LineCount() {
+               panic(fmt.Sprintf("invalid line number %v (of %v) for fillstruct", line, tok.LineCount()))
+       }
+
+       cl := &ast.CompositeLit{
+               Type:   expr.Type,
+               Lbrace: tok.LineStart(1),
+               Elts:   elts,
+               Rbrace: tok.LineStart(line),
+       }
+
+       // Find the line on which the composite literal is declared.
+       split := bytes.Split(content, []byte("\n"))
+       lineNumber := fset.Position(expr.Lbrace).Line
+       firstLine := split[lineNumber-1] // lines are 1-indexed
+
+       // Trim the whitespace from the left of the line, and use the index
+       // to get the amount of whitespace on the left.
+       trimmed := bytes.TrimLeftFunc(firstLine, unicode.IsSpace)
+       index := bytes.Index(firstLine, trimmed)
+       whitespace := firstLine[:index]
+
+       // First pass through the formatter: turn the expr into a string.
+       var formatBuf bytes.Buffer
+       if err := format.Node(&formatBuf, fakeFset, cl); err != nil {
+               return nil, fmt.Errorf("failed to run first format on:\n%s\ngot err: %v", cl.Type, err)
+       }
+       sug := indent(formatBuf.Bytes(), whitespace)
+
+       if len(prefilledTypes) > 0 {
+               // Attempt a second pass through the formatter to line up columns.
+               sourced, err := format.Source(sug)
+               if err == nil {
+                       sug = indent(sourced, whitespace)
+               }
+       }
+
+       return &analysis.SuggestedFix{
+               TextEdits: []analysis.TextEdit{
+                       {
+                               Pos:     expr.Pos(),
+                               End:     expr.End(),
+                               NewText: sug,
+                       },
+               },
+       }, nil
+}
+
+// indent works line by line through str, indenting (prefixing) each line with
+// ind.
+func indent(str, ind []byte) []byte {
+       split := bytes.Split(str, []byte("\n"))
+       newText := bytes.NewBuffer(nil)
+       for i, s := range split {
+               if len(s) == 0 {
+                       continue
+               }
+               // Don't add the extra indentation to the first line.
+               if i != 0 {
+                       newText.Write(ind)
+               }
+               newText.Write(s)
+               if i < len(split)-1 {
+                       newText.WriteByte('\n')
+               }
+       }
+       return newText.Bytes()
+}
+
+// populateValue constructs an expression to fill the value of a struct field.
+//
+// When the type of a struct field is a basic literal or interface, we return
+// default values. For other types, such as maps, slices, and channels, we create
+// expressions rather than using default values.
+//
+// The reasoning here is that users will call fillstruct with the intention of
+// initializing the struct, in which case setting these fields to nil has no effect.
+func populateValue(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
+       under := typ
+       if n, ok := typ.(*types.Named); ok {
+               under = n.Underlying()
+       }
+       switch u := under.(type) {
+       case *types.Basic:
+               switch {
+               case u.Info()&types.IsNumeric != 0:
+                       return &ast.BasicLit{Kind: token.INT, Value: "0"}
+               case u.Info()&types.IsBoolean != 0:
+                       return &ast.Ident{Name: "false"}
+               case u.Info()&types.IsString != 0:
+                       return &ast.BasicLit{Kind: token.STRING, Value: `""`}
+               default:
+                       panic("unknown basic type")
+               }
+       case *types.Map:
+               k := analysisinternal.TypeExpr(fset, f, pkg, u.Key())
+               v := analysisinternal.TypeExpr(fset, f, pkg, u.Elem())
+               if k == nil || v == nil {
+                       return nil
+               }
+               return &ast.CompositeLit{
+                       Type: &ast.MapType{
+                               Key:   k,
+                               Value: v,
+                       },
+               }
+       case *types.Slice:
+               s := analysisinternal.TypeExpr(fset, f, pkg, u.Elem())
+               if s == nil {
+                       return nil
+               }
+               return &ast.CompositeLit{
+                       Type: &ast.ArrayType{
+                               Elt: s,
+                       },
+               }
+       case *types.Array:
+               a := analysisinternal.TypeExpr(fset, f, pkg, u.Elem())
+               if a == nil {
+                       return nil
+               }
+               return &ast.CompositeLit{
+                       Type: &ast.ArrayType{
+                               Elt: a,
+                               Len: &ast.BasicLit{
+                                       Kind: token.INT, Value: fmt.Sprintf("%v", u.Len()),
+                               },
+                       },
+               }
+       case *types.Chan:
+               v := analysisinternal.TypeExpr(fset, f, pkg, u.Elem())
+               if v == nil {
+                       return nil
+               }
+               dir := ast.ChanDir(u.Dir())
+               if u.Dir() == types.SendRecv {
+                       dir = ast.SEND | ast.RECV
+               }
+               return &ast.CallExpr{
+                       Fun: ast.NewIdent("make"),
+                       Args: []ast.Expr{
+                               &ast.ChanType{
+                                       Dir:   dir,
+                                       Value: v,
+                               },
+                       },
+               }
+       case *types.Struct:
+               s := analysisinternal.TypeExpr(fset, f, pkg, typ)
+               if s == nil {
+                       return nil
+               }
+               return &ast.CompositeLit{
+                       Type: s,
+               }
+       case *types.Signature:
+               var params []*ast.Field
+               for i := 0; i < u.Params().Len(); i++ {
+                       p := analysisinternal.TypeExpr(fset, f, pkg, u.Params().At(i).Type())
+                       if p == nil {
+                               return nil
+                       }
+                       params = append(params, &ast.Field{
+                               Type: p,
+                               Names: []*ast.Ident{
+                                       {
+                                               Name: u.Params().At(i).Name(),
+                                       },
+                               },
+                       })
+               }
+               var returns []*ast.Field
+               for i := 0; i < u.Results().Len(); i++ {
+                       r := analysisinternal.TypeExpr(fset, f, pkg, u.Results().At(i).Type())
+                       if r == nil {
+                               return nil
+                       }
+                       returns = append(returns, &ast.Field{
+                               Type: r,
+                       })
+               }
+               return &ast.FuncLit{
+                       Type: &ast.FuncType{
+                               Params: &ast.FieldList{
+                                       List: params,
+                               },
+                               Results: &ast.FieldList{
+                                       List: returns,
+                               },
+                       },
+                       Body: &ast.BlockStmt{},
+               }
+       case *types.Pointer:
+               switch u.Elem().(type) {
+               case *types.Basic:
+                       return &ast.CallExpr{
+                               Fun: &ast.Ident{
+                                       Name: "new",
+                               },
+                               Args: []ast.Expr{
+                                       &ast.Ident{
+                                               Name: u.Elem().String(),
+                                       },
+                               },
+                       }
+               default:
+                       return &ast.UnaryExpr{
+                               Op: token.AND,
+                               X:  populateValue(fset, f, pkg, u.Elem()),
+                       }
+               }
+       case *types.Interface:
+               return ast.NewIdent("nil")
+       }
+       return nil
+}