-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
-
110
-
111
-
112
-
113
-
114
-
115
-
116
-
117
-
118
-
119
-
120
-
121
-
122
-
123
-
124
-
125
-
126
-
127
-
128
-
129
-
130
-
131
-
132
-
133
-
134
-
135
-
136
-
137
-
138
-
139
-
140
-
141
-
142
-
143
-
144
-
145
-
146
-
147
-
148
-
149
-
150
-
151
-
152
-
153
-
154
-
155
-
156
-
157
-
158
-
159
-
160
-
161
-
162
-
163
-
164
-
165
-
166
-
167
-
168
-
169
-
170
-
171
-
172
-
173
-
174
-
175
-
176
-
177
-
178
-
179
-
180
-
181
-
182
-
183
-
184
-
185
-
186
-
187
-
188
-
189
-
190
-
191
-
192
-
193
-
194
-
195
-
196
-
197
-
198
-
199
-
200
-
201
-
202
-
203
-
204
-
205
-
206
-
207
-
208
-
209
-
210
-
211
-
212
-
213
-
214
-
215
-
216
-
217
-
218
-
219
-
220
-
221
-
222
-
223
-
224
-
225
-
226
-
227
-
228
-
229
-
230
-
231
-
232
-
233
-
234
-
235
-
236
-
237
-
238
-
239
-
240
-
241
-
242
-
243
-
244
-
245
-
246
-
247
-
248
-
249
-
250
-
251
-
252
-
253
-
254
-
255
-
256
-
257
-
258
-
259
-
260
-
261
-
262
-
263
-
264
-
265
-
266
-
267
-
268
-
269
-
270
-
271
-
272
-
273
-
274
-
275
-
276
-
277
-
278
-
279
-
280
-
281
-
282
-
283
-
284
-
285
-
286
-
287
-
288
-
289
-
290
-
291
-
292
-
293
-
294
-
295
-
296
-
297
-
298
-
299
-
300
-
301
-
302
-
303
-
304
-
305
-
306
-
307
-
308
-
309
-
310
-
311
-
312
-
313
-
314
-
315
-
316
-
317
-
318
-
319
-
320
-
321
-
322
-
323
-
324
-
325
-
326
-
327
-
328
-
329
-
330
-
331
-
332
-
333
-
334
-
335
-
336
-
337
-
338
-
339
-
340
-
341
-
342
-
343
-
344
-
345
-
346
-
347
-
348
-
349
-
350
-
351
-
352
-
353
-
354
-
355
-
356
-
357
-
358
-
359
-
360
-
361
-
362
-
363
-
364
-
365
-
366
-
367
-
368
-
369
-
370
-
371
-
372
-
373
-
374
-
375
-
376
-
377
-
378
-
379
-
380
-
381
-
382
-
383
-
384
-
385
-
386
-
387
-
388
-
389
-
390
-
391
-
392
-
393
-
394
-
395
-
396
-
397
-
398
-
399
-
400
-
401
-
402
-
403
-
404
-
405
-
406
-
407
-
408
-
409
-
410
-
411
-
412
-
413
-
414
-
415
-
416
-
417
-
418
-
419
-
420
-
421
-
422
-
423
-
424
-
425
// 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 analysisinternal exposes internal-only fields from go/analysis.
package analysisinternal
import (
"bytes"
"fmt"
"go/ast"
"go/token"
"go/types"
"strings"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/internal/lsp/fuzzy"
)
var (
GetTypeErrors func(p interface{}) []types.Error
SetTypeErrors func(p interface{}, errors []types.Error)
)
func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos {
// Get the end position for the type error.
offset, end := fset.PositionFor(start, false).Offset, start
if offset >= len(src) {
return end
}
if width := bytes.IndexAny(src[offset:], " \n,():;[]+-*"); width > 0 {
end = start + token.Pos(width)
}
return end
}
func ZeroValue(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.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice, *types.Array:
return ast.NewIdent("nil")
case *types.Struct:
texpr := TypeExpr(fset, f, pkg, typ) // typ because we want the name here.
if texpr == nil {
return nil
}
return &ast.CompositeLit{
Type: texpr,
}
}
return nil
}
// IsZeroValue checks whether the given expression is a 'zero value' (as determined by output of
// analysisinternal.ZeroValue)
func IsZeroValue(expr ast.Expr) bool {
switch e := expr.(type) {
case *ast.BasicLit:
return e.Value == "0" || e.Value == `""`
case *ast.Ident:
return e.Name == "nil" || e.Name == "false"
default:
return false
}
}
func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
switch t := typ.(type) {
case *types.Basic:
switch t.Kind() {
case types.UnsafePointer:
return &ast.SelectorExpr{X: ast.NewIdent("unsafe"), Sel: ast.NewIdent("Pointer")}
default:
return ast.NewIdent(t.Name())
}
case *types.Pointer:
x := TypeExpr(fset, f, pkg, t.Elem())
if x == nil {
return nil
}
return &ast.UnaryExpr{
Op: token.MUL,
X: x,
}
case *types.Array:
elt := TypeExpr(fset, f, pkg, t.Elem())
if elt == nil {
return nil
}
return &ast.ArrayType{
Len: &ast.BasicLit{
Kind: token.INT,
Value: fmt.Sprintf("%d", t.Len()),
},
Elt: elt,
}
case *types.Slice:
elt := TypeExpr(fset, f, pkg, t.Elem())
if elt == nil {
return nil
}
return &ast.ArrayType{
Elt: elt,
}
case *types.Map:
key := TypeExpr(fset, f, pkg, t.Key())
value := TypeExpr(fset, f, pkg, t.Elem())
if key == nil || value == nil {
return nil
}
return &ast.MapType{
Key: key,
Value: value,
}
case *types.Chan:
dir := ast.ChanDir(t.Dir())
if t.Dir() == types.SendRecv {
dir = ast.SEND | ast.RECV
}
value := TypeExpr(fset, f, pkg, t.Elem())
if value == nil {
return nil
}
return &ast.ChanType{
Dir: dir,
Value: value,
}
case *types.Signature:
var params []*ast.Field
for i := 0; i < t.Params().Len(); i++ {
p := TypeExpr(fset, f, pkg, t.Params().At(i).Type())
if p == nil {
return nil
}
params = append(params, &ast.Field{
Type: p,
Names: []*ast.Ident{
{
Name: t.Params().At(i).Name(),
},
},
})
}
var returns []*ast.Field
for i := 0; i < t.Results().Len(); i++ {
r := TypeExpr(fset, f, pkg, t.Results().At(i).Type())
if r == nil {
return nil
}
returns = append(returns, &ast.Field{
Type: r,
})
}
return &ast.FuncType{
Params: &ast.FieldList{
List: params,
},
Results: &ast.FieldList{
List: returns,
},
}
case *types.Named:
if t.Obj().Pkg() == nil {
return ast.NewIdent(t.Obj().Name())
}
if t.Obj().Pkg() == pkg {
return ast.NewIdent(t.Obj().Name())
}
pkgName := t.Obj().Pkg().Name()
// If the file already imports the package under another name, use that.
for _, group := range astutil.Imports(fset, f) {
for _, cand := range group {
if strings.Trim(cand.Path.Value, `"`) == t.Obj().Pkg().Path() {
if cand.Name != nil && cand.Name.Name != "" {
pkgName = cand.Name.Name
}
}
}
}
if pkgName == "." {
return ast.NewIdent(t.Obj().Name())
}
return &ast.SelectorExpr{
X: ast.NewIdent(pkgName),
Sel: ast.NewIdent(t.Obj().Name()),
}
case *types.Struct:
return ast.NewIdent(t.String())
case *types.Interface:
return ast.NewIdent(t.String())
default:
return nil
}
}
type TypeErrorPass string
const (
NoNewVars TypeErrorPass = "nonewvars"
NoResultValues TypeErrorPass = "noresultvalues"
UndeclaredName TypeErrorPass = "undeclaredname"
)
// StmtToInsertVarBefore returns the ast.Stmt before which we can safely insert a new variable.
// Some examples:
//
// Basic Example:
// z := 1
// y := z + x
// If x is undeclared, then this function would return `y := z + x`, so that we
// can insert `x := ` on the line before `y := z + x`.
//
// If stmt example:
// if z == 1 {
// } else if z == y {}
// If y is undeclared, then this function would return `if z == 1 {`, because we cannot
// insert a statement between an if and an else if statement. As a result, we need to find
// the top of the if chain to insert `y := ` before.
func StmtToInsertVarBefore(path []ast.Node) ast.Stmt {
enclosingIndex := -1
for i, p := range path {
if _, ok := p.(ast.Stmt); ok {
enclosingIndex = i
break
}
}
if enclosingIndex == -1 {
return nil
}
enclosingStmt := path[enclosingIndex]
switch enclosingStmt.(type) {
case *ast.IfStmt:
// The enclosingStmt is inside of the if declaration,
// We need to check if we are in an else-if stmt and
// get the base if statement.
return baseIfStmt(path, enclosingIndex)
case *ast.CaseClause:
// Get the enclosing switch stmt if the enclosingStmt is
// inside of the case statement.
for i := enclosingIndex + 1; i < len(path); i++ {
if node, ok := path[i].(*ast.SwitchStmt); ok {
return node
} else if node, ok := path[i].(*ast.TypeSwitchStmt); ok {
return node
}
}
}
if len(path) <= enclosingIndex+1 {
return enclosingStmt.(ast.Stmt)
}
// Check if the enclosing statement is inside another node.
switch expr := path[enclosingIndex+1].(type) {
case *ast.IfStmt:
// Get the base if statement.
return baseIfStmt(path, enclosingIndex+1)
case *ast.ForStmt:
if expr.Init == enclosingStmt || expr.Post == enclosingStmt {
return expr
}
}
return enclosingStmt.(ast.Stmt)
}
// baseIfStmt walks up the if/else-if chain until we get to
// the top of the current if chain.
func baseIfStmt(path []ast.Node, index int) ast.Stmt {
stmt := path[index]
for i := index + 1; i < len(path); i++ {
if node, ok := path[i].(*ast.IfStmt); ok && node.Else == stmt {
stmt = node
continue
}
break
}
return stmt.(ast.Stmt)
}
// WalkASTWithParent walks the AST rooted at n. The semantics are
// similar to ast.Inspect except it does not call f(nil).
func WalkASTWithParent(n ast.Node, f func(n ast.Node, parent ast.Node) bool) {
var ancestors []ast.Node
ast.Inspect(n, func(n ast.Node) (recurse bool) {
if n == nil {
ancestors = ancestors[:len(ancestors)-1]
return false
}
var parent ast.Node
if len(ancestors) > 0 {
parent = ancestors[len(ancestors)-1]
}
ancestors = append(ancestors, n)
return f(n, parent)
})
}
// FindMatchingIdents finds all identifiers in 'node' that match any of the given types.
// 'pos' represents the position at which the identifiers may be inserted. 'pos' must be within
// the scope of each of identifier we select. Otherwise, we will insert a variable at 'pos' that
// is unrecognized.
func FindMatchingIdents(typs []types.Type, node ast.Node, pos token.Pos, info *types.Info, pkg *types.Package) map[types.Type][]*ast.Ident {
matches := map[types.Type][]*ast.Ident{}
// Initialize matches to contain the variable types we are searching for.
for _, typ := range typs {
if typ == nil {
continue
}
matches[typ] = []*ast.Ident{}
}
seen := map[types.Object]struct{}{}
ast.Inspect(node, func(n ast.Node) bool {
if n == nil {
return false
}
// Prevent circular definitions. If 'pos' is within an assignment statement, do not
// allow any identifiers in that assignment statement to be selected. Otherwise,
// we could do the following, where 'x' satisfies the type of 'f0':
//
// x := fakeStruct{f0: x}
//
assignment, ok := n.(*ast.AssignStmt)
if ok && pos > assignment.Pos() && pos <= assignment.End() {
return false
}
if n.End() > pos {
return n.Pos() <= pos
}
ident, ok := n.(*ast.Ident)
if !ok || ident.Name == "_" {
return true
}
obj := info.Defs[ident]
if obj == nil || obj.Type() == nil {
return true
}
if _, ok := obj.(*types.TypeName); ok {
return true
}
// Prevent duplicates in matches' values.
if _, ok = seen[obj]; ok {
return true
}
seen[obj] = struct{}{}
// Find the scope for the given position. Then, check whether the object
// exists within the scope.
innerScope := pkg.Scope().Innermost(pos)
if innerScope == nil {
return true
}
_, foundObj := innerScope.LookupParent(ident.Name, pos)
if foundObj != obj {
return true
}
// The object must match one of the types that we are searching for.
if idents, ok := matches[obj.Type()]; ok {
matches[obj.Type()] = append(idents, ast.NewIdent(ident.Name))
}
// If the object type does not exactly match any of the target types, greedily
// find the first target type that the object type can satisfy.
for typ := range matches {
if obj.Type() == typ {
continue
}
if equivalentTypes(obj.Type(), typ) {
matches[typ] = append(matches[typ], ast.NewIdent(ident.Name))
}
}
return true
})
return matches
}
func equivalentTypes(want, got types.Type) bool {
if want == got || types.Identical(want, got) {
return true
}
// Code segment to help check for untyped equality from (golang/go#32146).
if rhs, ok := want.(*types.Basic); ok && rhs.Info()&types.IsUntyped > 0 {
if lhs, ok := got.Underlying().(*types.Basic); ok {
return rhs.Info()&types.IsConstType == lhs.Info()&types.IsConstType
}
}
return types.AssignableTo(want, got)
}
// FindBestMatch employs fuzzy matching to evaluate the similarity of each given identifier to the
// given pattern. We return the identifier whose name is most similar to the pattern.
func FindBestMatch(pattern string, idents []*ast.Ident) ast.Expr {
fuzz := fuzzy.NewMatcher(pattern)
var bestFuzz ast.Expr
highScore := float32(0) // minimum score is 0 (no match)
for _, ident := range idents {
// TODO: Improve scoring algorithm.
score := fuzz.Score(ident.Name)
if score > highScore {
highScore = score
bestFuzz = ident
} else if score == 0 {
// Order matters in the fuzzy matching algorithm. If we find no match
// when matching the target to the identifier, try matching the identifier
// to the target.
revFuzz := fuzzy.NewMatcher(ident.Name)
revScore := revFuzz.Score(pattern)
if revScore > highScore {
highScore = revScore
bestFuzz = ident
}
}
}
return bestFuzz
}