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