Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / lsp / analysis / undeclaredname / undeclared.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 undeclaredname defines an Analyzer that applies suggested fixes
6 // to errors of the type "undeclared name: %s".
7 package undeclaredname
8
9 import (
10         "bytes"
11         "fmt"
12         "go/ast"
13         "go/token"
14         "go/types"
15         "strings"
16
17         "golang.org/x/tools/go/analysis"
18         "golang.org/x/tools/go/ast/astutil"
19         "golang.org/x/tools/internal/analysisinternal"
20         "golang.org/x/tools/internal/span"
21 )
22
23 const Doc = `suggested fixes for "undeclared name: <>"
24
25 This checker provides suggested fixes for type errors of the
26 type "undeclared name: <>". It will insert a new statement:
27 "<> := ".`
28
29 var Analyzer = &analysis.Analyzer{
30         Name:             string(analysisinternal.UndeclaredName),
31         Doc:              Doc,
32         Requires:         []*analysis.Analyzer{},
33         Run:              run,
34         RunDespiteErrors: true,
35 }
36
37 const undeclaredNamePrefix = "undeclared name: "
38
39 func run(pass *analysis.Pass) (interface{}, error) {
40         for _, err := range analysisinternal.GetTypeErrors(pass) {
41                 if !FixesError(err.Msg) {
42                         continue
43                 }
44                 name := strings.TrimPrefix(err.Msg, undeclaredNamePrefix)
45                 var file *ast.File
46                 for _, f := range pass.Files {
47                         if f.Pos() <= err.Pos && err.Pos < f.End() {
48                                 file = f
49                                 break
50                         }
51                 }
52                 if file == nil {
53                         continue
54                 }
55
56                 // Get the path for the relevant range.
57                 path, _ := astutil.PathEnclosingInterval(file, err.Pos, err.Pos)
58                 if len(path) < 2 {
59                         continue
60                 }
61                 ident, ok := path[0].(*ast.Ident)
62                 if !ok || ident.Name != name {
63                         continue
64                 }
65                 // Skip selector expressions because it might be too complex
66                 // to try and provide a suggested fix for fields and methods.
67                 if _, ok := path[1].(*ast.SelectorExpr); ok {
68                         continue
69                 }
70                 // TODO(golang.org/issue/34644): Handle call expressions with suggested
71                 // fixes to create a function.
72                 if _, ok := path[1].(*ast.CallExpr); ok {
73                         continue
74                 }
75                 tok := pass.Fset.File(file.Pos())
76                 if tok == nil {
77                         continue
78                 }
79                 offset := pass.Fset.Position(err.Pos).Offset
80                 end := tok.Pos(offset + len(name))
81                 pass.Report(analysis.Diagnostic{
82                         Pos:     err.Pos,
83                         End:     end,
84                         Message: err.Msg,
85                 })
86         }
87         return nil, nil
88 }
89
90 func SuggestedFix(fset *token.FileSet, rng span.Range, content []byte, file *ast.File, _ *types.Package, _ *types.Info) (*analysis.SuggestedFix, error) {
91         pos := rng.Start // don't use the end
92         path, _ := astutil.PathEnclosingInterval(file, pos, pos)
93         if len(path) < 2 {
94                 return nil, fmt.Errorf("")
95         }
96         ident, ok := path[0].(*ast.Ident)
97         if !ok {
98                 return nil, fmt.Errorf("")
99         }
100         // Get the place to insert the new statement.
101         insertBeforeStmt := analysisinternal.StmtToInsertVarBefore(path)
102         if insertBeforeStmt == nil {
103                 return nil, fmt.Errorf("")
104         }
105
106         insertBefore := fset.Position(insertBeforeStmt.Pos()).Offset
107
108         // Get the indent to add on the line after the new statement.
109         // Since this will have a parse error, we can not use format.Source().
110         contentBeforeStmt, indent := content[:insertBefore], "\n"
111         if nl := bytes.LastIndex(contentBeforeStmt, []byte("\n")); nl != -1 {
112                 indent = string(contentBeforeStmt[nl:])
113         }
114         // Create the new local variable statement.
115         newStmt := fmt.Sprintf("%s := %s", ident.Name, indent)
116         return &analysis.SuggestedFix{
117                 Message: fmt.Sprintf("Create variable \"%s\"", ident.Name),
118                 TextEdits: []analysis.TextEdit{{
119                         Pos:     insertBeforeStmt.Pos(),
120                         End:     insertBeforeStmt.Pos(),
121                         NewText: []byte(newStmt),
122                 }},
123         }, nil
124 }
125
126 func FixesError(msg string) bool {
127         return strings.HasPrefix(msg, undeclaredNamePrefix)
128 }