func expectedCandidate()

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
}