in internal/lsp/source/completion/completion.go [1898:2124]
func expectedCandidate(ctx context.Context, c *completer) (inf candidateInference) {
inf.typeName = expectTypeName(c)
if c.enclosingCompositeLiteral != nil {
inf.objType = c.expectedCompositeLiteralType()
}
Nodes:
for i, node := range c.path {
switch node := node.(type) {
case *ast.BinaryExpr:
// Determine if query position comes from left or right of op.
e := node.X
if c.pos < node.OpPos {
e = node.Y
}
if tv, ok := c.pkg.GetTypesInfo().Types[e]; ok {
switch node.Op {
case token.LAND, token.LOR:
// Don't infer "bool" type for "&&" or "||". Often you want
// to compose a boolean expression from non-boolean
// candidates.
default:
inf.objType = tv.Type
}
break Nodes
}
case *ast.AssignStmt:
// Only rank completions if you are on the right side of the token.
if c.pos > node.TokPos {
i := exprAtPos(c.pos, node.Rhs)
if i >= len(node.Lhs) {
i = len(node.Lhs) - 1
}
if tv, ok := c.pkg.GetTypesInfo().Types[node.Lhs[i]]; ok {
inf.objType = tv.Type
}
// If we have a single expression on the RHS, record the LHS
// assignees so we can favor multi-return function calls with
// matching result values.
if len(node.Rhs) <= 1 {
for _, lhs := range node.Lhs {
inf.assignees = append(inf.assignees, c.pkg.GetTypesInfo().TypeOf(lhs))
}
} else {
// Otherwse, record our single assignee, even if its type is
// not available. We use this info to downrank functions
// with the wrong number of result values.
inf.assignees = append(inf.assignees, c.pkg.GetTypesInfo().TypeOf(node.Lhs[i]))
}
}
return inf
case *ast.ValueSpec:
if node.Type != nil && c.pos > node.Type.End() {
inf.objType = c.pkg.GetTypesInfo().TypeOf(node.Type)
}
return inf
case *ast.CallExpr:
// Only consider CallExpr args if position falls between parens.
if node.Lparen < c.pos && c.pos <= node.Rparen {
// For type conversions like "int64(foo)" we can only infer our
// desired type is convertible to int64.
if typ := typeConversion(node, c.pkg.GetTypesInfo()); typ != nil {
inf.convertibleTo = typ
break Nodes
}
if tv, ok := c.pkg.GetTypesInfo().Types[node.Fun]; ok {
if sig, ok := tv.Type.(*types.Signature); ok {
numParams := sig.Params().Len()
if numParams == 0 {
return inf
}
exprIdx := exprAtPos(c.pos, node.Args)
// If we have one or zero arg expressions, we may be
// completing to a function call that returns multiple
// values, in turn getting passed in to the surrounding
// call. Record the assignees so we can favor function
// calls that return matching values.
if len(node.Args) <= 1 && exprIdx == 0 {
for i := 0; i < sig.Params().Len(); i++ {
inf.assignees = append(inf.assignees, sig.Params().At(i).Type())
}
// Record that we may be completing into variadic parameters.
inf.variadicAssignees = sig.Variadic()
}
// Make sure not to run past the end of expected parameters.
if exprIdx >= numParams {
inf.objType = sig.Params().At(numParams - 1).Type()
} else {
inf.objType = sig.Params().At(exprIdx).Type()
}
if sig.Variadic() && exprIdx >= (numParams-1) {
// If we are completing a variadic param, deslice the variadic type.
inf.objType = deslice(inf.objType)
// Record whether we are completing the initial variadic param.
inf.variadic = exprIdx == numParams-1 && len(node.Args) <= numParams
// Check if we can infer object kind from printf verb.
inf.objKind |= printfArgKind(c.pkg.GetTypesInfo(), node, exprIdx)
}
}
}
if funIdent, ok := node.Fun.(*ast.Ident); ok {
obj := c.pkg.GetTypesInfo().ObjectOf(funIdent)
if obj != nil && obj.Parent() == types.Universe {
// Defer call to builtinArgType so we can provide it the
// inferred type from its parent node.
defer func() {
inf = c.builtinArgType(obj, node, inf)
inf.objKind = c.builtinArgKind(ctx, obj, node)
}()
// The expected type of builtin arguments like append() is
// the expected type of the builtin call itself. For
// example:
//
// var foo []int = append(<>)
//
// To find the expected type at <> we "skip" the append()
// node and get the expected type one level up, which is
// []int.
continue Nodes
}
}
return inf
}
case *ast.ReturnStmt:
if c.enclosingFunc != nil {
sig := c.enclosingFunc.sig
// Find signature result that corresponds to our return statement.
if resultIdx := exprAtPos(c.pos, node.Results); resultIdx < len(node.Results) {
if resultIdx < sig.Results().Len() {
inf.objType = sig.Results().At(resultIdx).Type()
}
}
}
return inf
case *ast.CaseClause:
if swtch, ok := findSwitchStmt(c.path[i+1:], c.pos, node).(*ast.SwitchStmt); ok {
if tv, ok := c.pkg.GetTypesInfo().Types[swtch.Tag]; ok {
inf.objType = tv.Type
// Record which objects have already been used in the case
// statements so we don't suggest them again.
for _, cc := range swtch.Body.List {
for _, caseExpr := range cc.(*ast.CaseClause).List {
// Don't record the expression we are currently completing.
if caseExpr.Pos() < c.pos && c.pos <= caseExpr.End() {
continue
}
if objs := objChain(c.pkg.GetTypesInfo(), caseExpr); len(objs) > 0 {
inf.penalized = append(inf.penalized, penalizedObj{objChain: objs, penalty: 0.1})
}
}
}
}
}
return inf
case *ast.SliceExpr:
// Make sure position falls within the brackets (e.g. "foo[a:<>]").
if node.Lbrack < c.pos && c.pos <= node.Rbrack {
inf.objType = types.Typ[types.UntypedInt]
}
return inf
case *ast.IndexExpr:
// Make sure position falls within the brackets (e.g. "foo[<>]").
if node.Lbrack < c.pos && c.pos <= node.Rbrack {
if tv, ok := c.pkg.GetTypesInfo().Types[node.X]; ok {
switch t := tv.Type.Underlying().(type) {
case *types.Map:
inf.objType = t.Key()
case *types.Slice, *types.Array:
inf.objType = types.Typ[types.UntypedInt]
}
}
}
return inf
case *ast.SendStmt:
// Make sure we are on right side of arrow (e.g. "foo <- <>").
if c.pos > node.Arrow+1 {
if tv, ok := c.pkg.GetTypesInfo().Types[node.Chan]; ok {
if ch, ok := tv.Type.Underlying().(*types.Chan); ok {
inf.objType = ch.Elem()
}
}
}
return inf
case *ast.RangeStmt:
if source.NodeContains(node.X, c.pos) {
inf.objKind |= kindSlice | kindArray | kindMap | kindString
if node.Value == nil {
inf.objKind |= kindChan
}
}
return inf
case *ast.StarExpr:
inf.modifiers = append(inf.modifiers, typeMod{mod: dereference})
case *ast.UnaryExpr:
switch node.Op {
case token.AND:
inf.modifiers = append(inf.modifiers, typeMod{mod: reference})
case token.ARROW:
inf.modifiers = append(inf.modifiers, typeMod{mod: chanRead})
}
case *ast.DeferStmt, *ast.GoStmt:
inf.objKind |= kindFunc
return inf
default:
if breaksExpectedTypeInference(node, c.pos) {
return inf
}
}
}
return inf
}