mirror of https://github.com/golang/go.git
327 lines
7.9 KiB
Go
327 lines
7.9 KiB
Go
// 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/diff"
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
|
"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
|
|
}
|
|
|
|
// enclosingDeclLHS returns LHS idents from containing value spec or
|
|
// assign statement.
|
|
func enclosingDeclLHS(path []ast.Node) []*ast.Ident {
|
|
for _, n := range path {
|
|
switch n := n.(type) {
|
|
case *ast.ValueSpec:
|
|
return n.Names
|
|
case *ast.AssignStmt:
|
|
ids := make([]*ast.Ident, 0, len(n.Lhs))
|
|
for _, e := range n.Lhs {
|
|
if id, ok := e.(*ast.Ident); ok {
|
|
ids = append(ids, id)
|
|
}
|
|
}
|
|
return ids
|
|
}
|
|
}
|
|
|
|
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) + "{}"
|
|
}
|
|
}
|
|
|
|
// isBasicKind returns whether t is a basic type of kind k.
|
|
func isBasicKind(t types.Type, k types.BasicInfo) bool {
|
|
b, _ := t.Underlying().(*types.Basic)
|
|
return b != nil && b.Info()&k > 0
|
|
}
|
|
|
|
func (c *completer) editText(from, to token.Pos, newText string) ([]protocol.TextEdit, error) {
|
|
rng := source.NewMappedRange(c.snapshot.FileSet(), c.mapper, from, to)
|
|
spn, err := rng.Span()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return source.ToProtocolEdits(c.mapper, []diff.TextEdit{{
|
|
Span: spn,
|
|
NewText: newText,
|
|
}})
|
|
}
|