+++ /dev/null
-// 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) + "{}"
- }
-}