1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package completion
6
7import (
8	"context"
9	"go/ast"
10	"go/types"
11)
12
13// builtinArgKind determines the expected object kind for a builtin
14// argument. It attempts to use the AST hints from builtin.go where
15// possible.
16func (c *completer) builtinArgKind(ctx context.Context, obj types.Object, call *ast.CallExpr) objKind {
17	builtin, err := c.snapshot.BuiltinPackage(ctx)
18	if err != nil {
19		return 0
20	}
21	exprIdx := exprAtPos(c.pos, call.Args)
22
23	builtinObj := builtin.Package.Scope.Lookup(obj.Name())
24	if builtinObj == nil {
25		return 0
26	}
27	decl, ok := builtinObj.Decl.(*ast.FuncDecl)
28	if !ok || exprIdx >= len(decl.Type.Params.List) {
29		return 0
30	}
31
32	switch ptyp := decl.Type.Params.List[exprIdx].Type.(type) {
33	case *ast.ChanType:
34		return kindChan
35	case *ast.ArrayType:
36		return kindSlice
37	case *ast.MapType:
38		return kindMap
39	case *ast.Ident:
40		switch ptyp.Name {
41		case "Type":
42			switch obj.Name() {
43			case "make":
44				return kindChan | kindSlice | kindMap
45			case "len":
46				return kindSlice | kindMap | kindArray | kindString | kindChan
47			case "cap":
48				return kindSlice | kindArray | kindChan
49			}
50		}
51	}
52
53	return 0
54}
55
56// builtinArgType infers the type of an argument to a builtin
57// function. parentInf is the inferred type info for the builtin
58// call's parent node.
59func (c *completer) builtinArgType(obj types.Object, call *ast.CallExpr, parentInf candidateInference) candidateInference {
60	var (
61		exprIdx = exprAtPos(c.pos, call.Args)
62
63		// Propagate certain properties from our parent's inference.
64		inf = candidateInference{
65			typeName:  parentInf.typeName,
66			modifiers: parentInf.modifiers,
67		}
68	)
69
70	switch obj.Name() {
71	case "append":
72		if parentInf.objType == nil {
73			break
74		}
75
76		inf.objType = parentInf.objType
77
78		if exprIdx <= 0 {
79			break
80		}
81
82		inf.objType = deslice(inf.objType)
83
84		// Check if we are completing the variadic append() param.
85		inf.variadic = exprIdx == 1 && len(call.Args) <= 2
86
87		// Penalize the first append() argument as a candidate. You
88		// don't normally append a slice to itself.
89		if sliceChain := objChain(c.pkg.GetTypesInfo(), call.Args[0]); len(sliceChain) > 0 {
90			inf.penalized = append(inf.penalized, penalizedObj{objChain: sliceChain, penalty: 0.9})
91		}
92	case "delete":
93		if exprIdx > 0 && len(call.Args) > 0 {
94			// Try to fill in expected type of map key.
95			firstArgType := c.pkg.GetTypesInfo().TypeOf(call.Args[0])
96			if firstArgType != nil {
97				if mt, ok := firstArgType.Underlying().(*types.Map); ok {
98					inf.objType = mt.Key()
99				}
100			}
101		}
102	case "copy":
103		var t1, t2 types.Type
104		if len(call.Args) > 0 {
105			t1 = c.pkg.GetTypesInfo().TypeOf(call.Args[0])
106			if len(call.Args) > 1 {
107				t2 = c.pkg.GetTypesInfo().TypeOf(call.Args[1])
108			}
109		}
110
111		// Fill in expected type of either arg if the other is already present.
112		if exprIdx == 1 && t1 != nil {
113			inf.objType = t1
114		} else if exprIdx == 0 && t2 != nil {
115			inf.objType = t2
116		}
117	case "new":
118		inf.typeName.wantTypeName = true
119		if parentInf.objType != nil {
120			// Expected type for "new" is the de-pointered parent type.
121			if ptr, ok := parentInf.objType.Underlying().(*types.Pointer); ok {
122				inf.objType = ptr.Elem()
123			}
124		}
125	case "make":
126		if exprIdx == 0 {
127			inf.typeName.wantTypeName = true
128			inf.objType = parentInf.objType
129		} else {
130			inf.objType = types.Typ[types.Int]
131		}
132	}
133
134	return inf
135}
136