mirror of
https://github.com/golang/go
synced 2024-11-18 16:14:46 -07:00
90abf76919
In this example: p := &[]int{} append([]int{}, *<>) At <> we completed to "**p" instead of "*p...". There were two fixes: 1. builtinArgType() wasn't propagating the "modifiers", so we were forgetting about the preceding "*" pointer indirection and inserting it again with the completion. Fix by propagating modifiers. 2. The candidate formatting responsible for adding "..." had over simplified logic to determine if we are completing the variadic param. Now instead the candidate evaluation code marks the candidate as "variadic" so the formatting doesn't have to think at all. Change-Id: Ib71ee8ecfafb915df331f1d2e55b76f76a530243 Reviewed-on: https://go-review.googlesource.com/c/tools/+/248018 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
136 lines
3.4 KiB
Go
136 lines
3.4 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 source
|
|
|
|
import (
|
|
"context"
|
|
"go/ast"
|
|
"go/types"
|
|
)
|
|
|
|
// builtinArgKind determines the expected object kind for a builtin
|
|
// argument. It attempts to use the AST hints from builtin.go where
|
|
// possible.
|
|
func (c *completer) builtinArgKind(ctx context.Context, obj types.Object, call *ast.CallExpr) objKind {
|
|
builtin, err := c.snapshot.BuiltinPackage(ctx)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
exprIdx := exprAtPos(c.pos, call.Args)
|
|
|
|
builtinObj := builtin.Package.Scope.Lookup(obj.Name())
|
|
if builtinObj == nil {
|
|
return 0
|
|
}
|
|
decl, ok := builtinObj.Decl.(*ast.FuncDecl)
|
|
if !ok || exprIdx >= len(decl.Type.Params.List) {
|
|
return 0
|
|
}
|
|
|
|
switch ptyp := decl.Type.Params.List[exprIdx].Type.(type) {
|
|
case *ast.ChanType:
|
|
return kindChan
|
|
case *ast.ArrayType:
|
|
return kindSlice
|
|
case *ast.MapType:
|
|
return kindMap
|
|
case *ast.Ident:
|
|
switch ptyp.Name {
|
|
case "Type":
|
|
switch obj.Name() {
|
|
case "make":
|
|
return kindChan | kindSlice | kindMap
|
|
case "len":
|
|
return kindSlice | kindMap | kindArray | kindString | kindChan
|
|
case "cap":
|
|
return kindSlice | kindArray | kindChan
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
// builtinArgType infers the type of an argument to a builtin
|
|
// function. parentInf is the inferred type info for the builtin
|
|
// call's parent node.
|
|
func (c *completer) builtinArgType(obj types.Object, call *ast.CallExpr, parentInf candidateInference) candidateInference {
|
|
var (
|
|
exprIdx = exprAtPos(c.pos, call.Args)
|
|
|
|
// Propagate certain properties from our parent's inference.
|
|
inf = candidateInference{
|
|
typeName: parentInf.typeName,
|
|
modifiers: parentInf.modifiers,
|
|
}
|
|
)
|
|
|
|
switch obj.Name() {
|
|
case "append":
|
|
if parentInf.objType == nil {
|
|
break
|
|
}
|
|
|
|
inf.objType = parentInf.objType
|
|
|
|
if exprIdx <= 0 {
|
|
break
|
|
}
|
|
|
|
inf.objType = deslice(inf.objType)
|
|
|
|
// Check if we are completing the variadic append() param.
|
|
inf.variadic = exprIdx == 1 && len(call.Args) <= 2
|
|
|
|
// Penalize the first append() argument as a candidate. You
|
|
// don't normally append a slice to itself.
|
|
if sliceChain := objChain(c.pkg.GetTypesInfo(), call.Args[0]); len(sliceChain) > 0 {
|
|
inf.penalized = append(inf.penalized, penalizedObj{objChain: sliceChain, penalty: 0.9})
|
|
}
|
|
case "delete":
|
|
if exprIdx > 0 && len(call.Args) > 0 {
|
|
// Try to fill in expected type of map key.
|
|
firstArgType := c.pkg.GetTypesInfo().TypeOf(call.Args[0])
|
|
if firstArgType != nil {
|
|
if mt, ok := firstArgType.Underlying().(*types.Map); ok {
|
|
inf.objType = mt.Key()
|
|
}
|
|
}
|
|
}
|
|
case "copy":
|
|
var t1, t2 types.Type
|
|
if len(call.Args) > 0 {
|
|
t1 = c.pkg.GetTypesInfo().TypeOf(call.Args[0])
|
|
if len(call.Args) > 1 {
|
|
t2 = c.pkg.GetTypesInfo().TypeOf(call.Args[1])
|
|
}
|
|
}
|
|
|
|
// Fill in expected type of either arg if the other is already present.
|
|
if exprIdx == 1 && t1 != nil {
|
|
inf.objType = t1
|
|
} else if exprIdx == 0 && t2 != nil {
|
|
inf.objType = t2
|
|
}
|
|
case "new":
|
|
inf.typeName.wantTypeName = true
|
|
if parentInf.objType != nil {
|
|
// Expected type for "new" is the de-pointered parent type.
|
|
if ptr, ok := parentInf.objType.Underlying().(*types.Pointer); ok {
|
|
inf.objType = ptr.Elem()
|
|
}
|
|
}
|
|
case "make":
|
|
if exprIdx == 0 {
|
|
inf.typeName.wantTypeName = true
|
|
inf.objType = parentInf.objType
|
|
} else {
|
|
inf.objType = types.Typ[types.Int]
|
|
}
|
|
}
|
|
|
|
return inf
|
|
}
|