1// Copyright 2019 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	"fmt"
10	"go/types"
11	"strings"
12	"unicode"
13
14	"golang.org/x/tools/internal/event"
15	"golang.org/x/tools/internal/lsp/protocol"
16	"golang.org/x/tools/internal/lsp/snippet"
17	"golang.org/x/tools/internal/lsp/source"
18)
19
20// literal generates composite literal, function literal, and make()
21// completion items.
22func (c *completer) literal(ctx context.Context, literalType types.Type, imp *importInfo) {
23	if !c.opts.literal {
24		return
25	}
26
27	expType := c.inference.objType
28
29	if c.inference.matchesVariadic(literalType) {
30		// Don't offer literal slice candidates for variadic arguments.
31		// For example, don't offer "[]interface{}{}" in "fmt.Print(<>)".
32		return
33	}
34
35	// Avoid literal candidates if the expected type is an empty
36	// interface. It isn't very useful to suggest a literal candidate of
37	// every possible type.
38	if expType != nil && isEmptyInterface(expType) {
39		return
40	}
41
42	// We handle unnamed literal completions explicitly before searching
43	// for candidates. Avoid named-type literal completions for
44	// unnamed-type expected type since that results in duplicate
45	// candidates. For example, in
46	//
47	// type mySlice []int
48	// var []int = <>
49	//
50	// don't offer "mySlice{}" since we have already added a candidate
51	// of "[]int{}".
52	if _, named := literalType.(*types.Named); named && expType != nil {
53		if _, named := source.Deref(expType).(*types.Named); !named {
54			return
55		}
56	}
57
58	// Check if an object of type literalType would match our expected type.
59	cand := candidate{
60		obj: c.fakeObj(literalType),
61	}
62
63	switch literalType.Underlying().(type) {
64	// These literal types are addressable (e.g. "&[]int{}"), others are
65	// not (e.g. can't do "&(func(){})").
66	case *types.Struct, *types.Array, *types.Slice, *types.Map:
67		cand.addressable = true
68	}
69
70	if !c.matchingCandidate(&cand) || cand.convertTo != nil {
71		return
72	}
73
74	var (
75		qf  = c.qf
76		sel = enclosingSelector(c.path, c.pos)
77	)
78
79	// Don't qualify the type name if we are in a selector expression
80	// since the package name is already present.
81	if sel != nil {
82		qf = func(_ *types.Package) string { return "" }
83	}
84
85	typeName := types.TypeString(literalType, qf)
86
87	// A type name of "[]int" doesn't work very will with the matcher
88	// since "[" isn't a valid identifier prefix. Here we strip off the
89	// slice (and array) prefix yielding just "int".
90	matchName := typeName
91	switch t := literalType.(type) {
92	case *types.Slice:
93		matchName = types.TypeString(t.Elem(), qf)
94	case *types.Array:
95		matchName = types.TypeString(t.Elem(), qf)
96	}
97
98	addlEdits, err := c.importEdits(imp)
99	if err != nil {
100		event.Error(ctx, "error adding import for literal candidate", err)
101		return
102	}
103
104	// If prefix matches the type name, client may want a composite literal.
105	if score := c.matcher.Score(matchName); score > 0 {
106		if cand.takeAddress {
107			if sel != nil {
108				// If we are in a selector we must place the "&" before the selector.
109				// For example, "foo.B<>" must complete to "&foo.Bar{}", not
110				// "foo.&Bar{}".
111				edits, err := c.editText(sel.Pos(), sel.Pos(), "&")
112				if err != nil {
113					event.Error(ctx, "error making edit for literal pointer completion", err)
114					return
115				}
116				addlEdits = append(addlEdits, edits...)
117			} else {
118				// Otherwise we can stick the "&" directly before the type name.
119				typeName = "&" + typeName
120			}
121		}
122
123		switch t := literalType.Underlying().(type) {
124		case *types.Struct, *types.Array, *types.Slice, *types.Map:
125			c.compositeLiteral(t, typeName, float64(score), addlEdits)
126		case *types.Signature:
127			// Add a literal completion for a signature type that implements
128			// an interface. For example, offer "http.HandlerFunc()" when
129			// expected type is "http.Handler".
130			if source.IsInterface(expType) {
131				c.basicLiteral(t, typeName, float64(score), addlEdits)
132			}
133		case *types.Basic:
134			// Add a literal completion for basic types that implement our
135			// expected interface (e.g. named string type http.Dir
136			// implements http.FileSystem), or are identical to our expected
137			// type (i.e. yielding a type conversion such as "float64()").
138			if source.IsInterface(expType) || types.Identical(expType, literalType) {
139				c.basicLiteral(t, typeName, float64(score), addlEdits)
140			}
141		}
142	}
143
144	// If prefix matches "make", client may want a "make()"
145	// invocation. We also include the type name to allow for more
146	// flexible fuzzy matching.
147	if score := c.matcher.Score("make." + matchName); !cand.takeAddress && score > 0 {
148		switch literalType.Underlying().(type) {
149		case *types.Slice:
150			// The second argument to "make()" for slices is required, so default to "0".
151			c.makeCall(typeName, "0", float64(score), addlEdits)
152		case *types.Map, *types.Chan:
153			// Maps and channels don't require the second argument, so omit
154			// to keep things simple for now.
155			c.makeCall(typeName, "", float64(score), addlEdits)
156		}
157	}
158
159	// If prefix matches "func", client may want a function literal.
160	if score := c.matcher.Score("func"); !cand.takeAddress && score > 0 && !source.IsInterface(expType) {
161		switch t := literalType.Underlying().(type) {
162		case *types.Signature:
163			c.functionLiteral(ctx, t, float64(score))
164		}
165	}
166}
167
168// literalCandidateScore is the base score for literal candidates.
169// Literal candidates match the expected type so they should be high
170// scoring, but we want them ranked below lexical objects of the
171// correct type, so scale down highScore.
172const literalCandidateScore = highScore / 2
173
174// functionLiteral adds a function literal completion item for the
175// given signature.
176func (c *completer) functionLiteral(ctx context.Context, sig *types.Signature, matchScore float64) {
177	snip := &snippet.Builder{}
178	snip.WriteText("func(")
179
180	// First we generate names for each param and keep a seen count so
181	// we know if we need to uniquify param names. For example,
182	// "func(int)" will become "func(i int)", but "func(int, int64)"
183	// will become "func(i1 int, i2 int64)".
184	var (
185		paramNames     = make([]string, sig.Params().Len())
186		paramNameCount = make(map[string]int)
187	)
188	for i := 0; i < sig.Params().Len(); i++ {
189		var (
190			p    = sig.Params().At(i)
191			name = p.Name()
192		)
193		if name == "" {
194			// If the param has no name in the signature, guess a name based
195			// on the type. Use an empty qualifier to ignore the package.
196			// For example, we want to name "http.Request" "r", not "hr".
197			name = source.FormatVarType(ctx, c.snapshot, c.pkg, p, func(p *types.Package) string {
198				return ""
199			})
200			name = abbreviateTypeName(name)
201		}
202		paramNames[i] = name
203		if name != "_" {
204			paramNameCount[name]++
205		}
206	}
207
208	for n, c := range paramNameCount {
209		// Any names we saw more than once will need a unique suffix added
210		// on. Reset the count to 1 to act as the suffix for the first
211		// name.
212		if c >= 2 {
213			paramNameCount[n] = 1
214		} else {
215			delete(paramNameCount, n)
216		}
217	}
218
219	for i := 0; i < sig.Params().Len(); i++ {
220		if i > 0 {
221			snip.WriteText(", ")
222		}
223
224		var (
225			p    = sig.Params().At(i)
226			name = paramNames[i]
227		)
228
229		// Uniquify names by adding on an incrementing numeric suffix.
230		if idx, found := paramNameCount[name]; found {
231			paramNameCount[name]++
232			name = fmt.Sprintf("%s%d", name, idx)
233		}
234
235		if name != p.Name() && c.opts.placeholders {
236			// If we didn't use the signature's param name verbatim then we
237			// may have chosen a poor name. Give the user a placeholder so
238			// they can easily fix the name.
239			snip.WritePlaceholder(func(b *snippet.Builder) {
240				b.WriteText(name)
241			})
242		} else {
243			snip.WriteText(name)
244		}
245
246		// If the following param's type is identical to this one, omit
247		// this param's type string. For example, emit "i, j int" instead
248		// of "i int, j int".
249		if i == sig.Params().Len()-1 || !types.Identical(p.Type(), sig.Params().At(i+1).Type()) {
250			snip.WriteText(" ")
251			typeStr := source.FormatVarType(ctx, c.snapshot, c.pkg, p, c.qf)
252			if sig.Variadic() && i == sig.Params().Len()-1 {
253				typeStr = strings.Replace(typeStr, "[]", "...", 1)
254			}
255			snip.WriteText(typeStr)
256		}
257	}
258	snip.WriteText(")")
259
260	results := sig.Results()
261	if results.Len() > 0 {
262		snip.WriteText(" ")
263	}
264
265	resultsNeedParens := results.Len() > 1 ||
266		results.Len() == 1 && results.At(0).Name() != ""
267
268	if resultsNeedParens {
269		snip.WriteText("(")
270	}
271	for i := 0; i < results.Len(); i++ {
272		if i > 0 {
273			snip.WriteText(", ")
274		}
275		r := results.At(i)
276		if name := r.Name(); name != "" {
277			snip.WriteText(name + " ")
278		}
279		snip.WriteText(source.FormatVarType(ctx, c.snapshot, c.pkg, r, c.qf))
280	}
281	if resultsNeedParens {
282		snip.WriteText(")")
283	}
284
285	snip.WriteText(" {")
286	snip.WriteFinalTabstop()
287	snip.WriteText("}")
288
289	c.items = append(c.items, CompletionItem{
290		Label:   "func(...) {}",
291		Score:   matchScore * literalCandidateScore,
292		Kind:    protocol.VariableCompletion,
293		snippet: snip,
294	})
295}
296
297// abbreviateTypeName abbreviates type names into acronyms. For
298// example, "fooBar" is abbreviated "fb". Care is taken to ignore
299// non-identifier runes. For example, "[]int" becomes "i", and
300// "struct { i int }" becomes "s".
301func abbreviateTypeName(s string) string {
302	var (
303		b            strings.Builder
304		useNextUpper bool
305	)
306
307	// Trim off leading non-letters. We trim everything between "[" and
308	// "]" to handle array types like "[someConst]int".
309	var inBracket bool
310	s = strings.TrimFunc(s, func(r rune) bool {
311		if inBracket {
312			inBracket = r != ']'
313			return true
314		}
315
316		if r == '[' {
317			inBracket = true
318		}
319
320		return !unicode.IsLetter(r)
321	})
322
323	for i, r := range s {
324		// Stop if we encounter a non-identifier rune.
325		if !unicode.IsLetter(r) && !unicode.IsNumber(r) {
326			break
327		}
328
329		if i == 0 {
330			b.WriteRune(unicode.ToLower(r))
331		}
332
333		if unicode.IsUpper(r) {
334			if useNextUpper {
335				b.WriteRune(unicode.ToLower(r))
336				useNextUpper = false
337			}
338		} else {
339			useNextUpper = true
340		}
341	}
342
343	return b.String()
344}
345
346// compositeLiteral adds a composite literal completion item for the given typeName.
347func (c *completer) compositeLiteral(T types.Type, typeName string, matchScore float64, edits []protocol.TextEdit) {
348	snip := &snippet.Builder{}
349	snip.WriteText(typeName + "{")
350	// Don't put the tab stop inside the composite literal curlies "{}"
351	// for structs that have no accessible fields.
352	if strct, ok := T.(*types.Struct); !ok || fieldsAccessible(strct, c.pkg.GetTypes()) {
353		snip.WriteFinalTabstop()
354	}
355	snip.WriteText("}")
356
357	nonSnippet := typeName + "{}"
358
359	c.items = append(c.items, CompletionItem{
360		Label:               nonSnippet,
361		InsertText:          nonSnippet,
362		Score:               matchScore * literalCandidateScore,
363		Kind:                protocol.VariableCompletion,
364		AdditionalTextEdits: edits,
365		snippet:             snip,
366	})
367}
368
369// basicLiteral adds a literal completion item for the given basic
370// type name typeName.
371func (c *completer) basicLiteral(T types.Type, typeName string, matchScore float64, edits []protocol.TextEdit) {
372	snip := &snippet.Builder{}
373	snip.WriteText(typeName + "(")
374	snip.WriteFinalTabstop()
375	snip.WriteText(")")
376
377	nonSnippet := typeName + "()"
378
379	c.items = append(c.items, CompletionItem{
380		Label:               nonSnippet,
381		InsertText:          nonSnippet,
382		Detail:              T.String(),
383		Score:               matchScore * literalCandidateScore,
384		Kind:                protocol.VariableCompletion,
385		AdditionalTextEdits: edits,
386		snippet:             snip,
387	})
388}
389
390// makeCall adds a completion item for a "make()" call given a specific type.
391func (c *completer) makeCall(typeName string, secondArg string, matchScore float64, edits []protocol.TextEdit) {
392	// Keep it simple and don't add any placeholders for optional "make()" arguments.
393
394	snip := &snippet.Builder{}
395	snip.WriteText("make(" + typeName)
396	if secondArg != "" {
397		snip.WriteText(", ")
398		snip.WritePlaceholder(func(b *snippet.Builder) {
399			if c.opts.placeholders {
400				b.WriteText(secondArg)
401			}
402		})
403	}
404	snip.WriteText(")")
405
406	var nonSnippet strings.Builder
407	nonSnippet.WriteString("make(" + typeName)
408	if secondArg != "" {
409		nonSnippet.WriteString(", ")
410		nonSnippet.WriteString(secondArg)
411	}
412	nonSnippet.WriteByte(')')
413
414	c.items = append(c.items, CompletionItem{
415		Label:               nonSnippet.String(),
416		InsertText:          nonSnippet.String(),
417		Score:               matchScore * literalCandidateScore,
418		Kind:                protocol.FunctionCompletion,
419		AdditionalTextEdits: edits,
420		snippet:             snip,
421	})
422}
423