// 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 completion import ( "go/ast" "go/token" "go/types" "golang.org/x/tools/internal/lsp/source" ) // exprAtPos returns the index of the expression containing pos. func exprAtPos(pos token.Pos, args []ast.Expr) int { for i, expr := range args { if expr.Pos() <= pos && pos <= expr.End() { return i } } return len(args) } // eachField invokes fn for each field that can be selected from a // value of type T. func eachField(T types.Type, fn func(*types.Var)) { // TODO(adonovan): this algorithm doesn't exclude ambiguous // selections that match more than one field/method. // types.NewSelectionSet should do that for us. // for termination on recursive types var seen map[*types.Struct]bool var visit func(T types.Type) visit = func(T types.Type) { if T, ok := source.Deref(T).Underlying().(*types.Struct); ok { if seen[T] { return } for i := 0; i < T.NumFields(); i++ { f := T.Field(i) fn(f) if f.Anonymous() { if seen == nil { // Lazily create "seen" since it is only needed for // embedded structs. seen = make(map[*types.Struct]bool) } seen[T] = true visit(f.Type()) } } } } visit(T) } // typeIsValid reports whether typ doesn't contain any Invalid types. func typeIsValid(typ types.Type) bool { // Check named types separately, because we don't want // to call Underlying() on them to avoid problems with recursive types. if _, ok := typ.(*types.Named); ok { return true } switch typ := typ.Underlying().(type) { case *types.Basic: return typ.Kind() != types.Invalid case *types.Array: return typeIsValid(typ.Elem()) case *types.Slice: return typeIsValid(typ.Elem()) case *types.Pointer: return typeIsValid(typ.Elem()) case *types.Map: return typeIsValid(typ.Key()) && typeIsValid(typ.Elem()) case *types.Chan: return typeIsValid(typ.Elem()) case *types.Signature: return typeIsValid(typ.Params()) && typeIsValid(typ.Results()) case *types.Tuple: for i := 0; i < typ.Len(); i++ { if !typeIsValid(typ.At(i).Type()) { return false } } return true case *types.Struct, *types.Interface: // Don't bother checking structs, interfaces for validity. return true default: return false } } // resolveInvalid traverses the node of the AST that defines the scope // containing the declaration of obj, and attempts to find a user-friendly // name for its invalid type. The resulting Object and its Type are fake. func resolveInvalid(fset *token.FileSet, obj types.Object, node ast.Node, info *types.Info) types.Object { var resultExpr ast.Expr ast.Inspect(node, func(node ast.Node) bool { switch n := node.(type) { case *ast.ValueSpec: for _, name := range n.Names { if info.Defs[name] == obj { resultExpr = n.Type } } return false case *ast.Field: // This case handles parameters and results of a FuncDecl or FuncLit. for _, name := range n.Names { if info.Defs[name] == obj { resultExpr = n.Type } } return false default: return true } }) // Construct a fake type for the object and return a fake object with this type. typename := source.FormatNode(fset, resultExpr) typ := types.NewNamed(types.NewTypeName(token.NoPos, obj.Pkg(), typename, nil), types.Typ[types.Invalid], nil) return types.NewVar(obj.Pos(), obj.Pkg(), obj.Name(), typ) } func isPointer(T types.Type) bool { _, ok := T.(*types.Pointer) return ok } func isVar(obj types.Object) bool { _, ok := obj.(*types.Var) return ok } func isTypeName(obj types.Object) bool { _, ok := obj.(*types.TypeName) return ok } func isFunc(obj types.Object) bool { _, ok := obj.(*types.Func) return ok } func isEmptyInterface(T types.Type) bool { intf, _ := T.(*types.Interface) return intf != nil && intf.NumMethods() == 0 } func isUntyped(T types.Type) bool { if basic, ok := T.(*types.Basic); ok { return basic.Info()&types.IsUntyped > 0 } return false } func isPkgName(obj types.Object) bool { _, ok := obj.(*types.PkgName) return ok } func isASTFile(n ast.Node) bool { _, ok := n.(*ast.File) return ok } func deslice(T types.Type) types.Type { if slice, ok := T.Underlying().(*types.Slice); ok { return slice.Elem() } return nil } // isSelector returns the enclosing *ast.SelectorExpr when pos is in the // selector. func enclosingSelector(path []ast.Node, pos token.Pos) *ast.SelectorExpr { if len(path) == 0 { return nil } if sel, ok := path[0].(*ast.SelectorExpr); ok { return sel } if _, ok := path[0].(*ast.Ident); ok && len(path) > 1 { if sel, ok := path[1].(*ast.SelectorExpr); ok && pos >= sel.Sel.Pos() { return sel } } return nil } func enclosingValueSpec(path []ast.Node) *ast.ValueSpec { for _, n := range path { if vs, ok := n.(*ast.ValueSpec); ok { return vs } } return nil } // exprObj returns the types.Object associated with the *ast.Ident or // *ast.SelectorExpr e. func exprObj(info *types.Info, e ast.Expr) types.Object { var ident *ast.Ident switch expr := e.(type) { case *ast.Ident: ident = expr case *ast.SelectorExpr: ident = expr.Sel default: return nil } return info.ObjectOf(ident) } // typeConversion returns the type being converted to if call is a type // conversion expression. func typeConversion(call *ast.CallExpr, info *types.Info) types.Type { // Type conversion (e.g. "float64(foo)"). if fun, _ := exprObj(info, call.Fun).(*types.TypeName); fun != nil { return fun.Type() } return nil } // fieldsAccessible returns whether s has at least one field accessible by p. func fieldsAccessible(s *types.Struct, p *types.Package) bool { for i := 0; i < s.NumFields(); i++ { f := s.Field(i) if f.Exported() || f.Pkg() == p { return true } } return false } // prevStmt returns the statement that precedes the statement containing pos. // For example: // // foo := 1 // bar(1 + 2<>) // // If "<>" is pos, prevStmt returns "foo := 1" func prevStmt(pos token.Pos, path []ast.Node) ast.Stmt { var blockLines []ast.Stmt for i := 0; i < len(path) && blockLines == nil; i++ { switch n := path[i].(type) { case *ast.BlockStmt: blockLines = n.List case *ast.CommClause: blockLines = n.Body case *ast.CaseClause: blockLines = n.Body } } for i := len(blockLines) - 1; i >= 0; i-- { if blockLines[i].End() < pos { return blockLines[i] } } return nil } // formatZeroValue produces Go code representing the zero value of T. It // returns the empty string if T is invalid. func formatZeroValue(T types.Type, qf types.Qualifier) string { switch u := T.Underlying().(type) { case *types.Basic: switch { case u.Info()&types.IsNumeric > 0: return "0" case u.Info()&types.IsString > 0: return `""` case u.Info()&types.IsBoolean > 0: return "false" default: return "" } case *types.Pointer, *types.Interface, *types.Chan, *types.Map, *types.Slice, *types.Signature: return "nil" default: return types.TypeString(T, qf) + "{}" } }