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