// Copyright 2019 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 completion import ( "context" "fmt" "go/ast" "go/token" "go/types" "strings" "unicode" "golang.org/x/tools/internal/event" "golang.org/x/tools/internal/lsp/diff" "golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/snippet" "golang.org/x/tools/internal/lsp/source" ) // literal generates composite literal, function literal, and make() // completion items. func (c *completer) literal(ctx context.Context, literalType types.Type, imp *importInfo) { if !c.opts.literal { return } expType := c.inference.objType if c.inference.matchesVariadic(literalType) { // Don't offer literal slice candidates for variadic arguments. // For example, don't offer "[]interface{}{}" in "fmt.Print(<>)". return } // Avoid literal candidates if the expected type is an empty // interface. It isn't very useful to suggest a literal candidate of // every possible type. if expType != nil && isEmptyInterface(expType) { return } // We handle unnamed literal completions explicitly before searching // for candidates. Avoid named-type literal completions for // unnamed-type expected type since that results in duplicate // candidates. For example, in // // type mySlice []int // var []int = <> // // don't offer "mySlice{}" since we have already added a candidate // of "[]int{}". if _, named := literalType.(*types.Named); named && expType != nil { if _, named := source.Deref(expType).(*types.Named); !named { return } } // Check if an object of type literalType would match our expected type. cand := candidate{ obj: c.fakeObj(literalType), } switch literalType.Underlying().(type) { // These literal types are addressable (e.g. "&[]int{}"), others are // not (e.g. can't do "&(func(){})"). case *types.Struct, *types.Array, *types.Slice, *types.Map: cand.addressable = true } if !c.matchingCandidate(&cand) { return } var ( qf = c.qf sel = enclosingSelector(c.path, c.pos) ) // Don't qualify the type name if we are in a selector expression // since the package name is already present. if sel != nil { qf = func(_ *types.Package) string { return "" } } typeName := types.TypeString(literalType, qf) // A type name of "[]int" doesn't work very will with the matcher // since "[" isn't a valid identifier prefix. Here we strip off the // slice (and array) prefix yielding just "int". matchName := typeName switch t := literalType.(type) { case *types.Slice: matchName = types.TypeString(t.Elem(), qf) case *types.Array: matchName = types.TypeString(t.Elem(), qf) } addlEdits, err := c.importEdits(imp) if err != nil { event.Error(ctx, "error adding import for literal candidate", err) return } // If prefix matches the type name, client may want a composite literal. if score := c.matcher.Score(matchName); score > 0 { if cand.takeAddress { if sel != nil { // If we are in a selector we must place the "&" before the selector. // For example, "foo.B<>" must complete to "&foo.Bar{}", not // "foo.&Bar{}". edits, err := prependEdit(c.snapshot.FileSet(), c.mapper, sel, "&") if err != nil { event.Error(ctx, "error making edit for literal pointer completion", err) return } addlEdits = append(addlEdits, edits...) } else { // Otherwise we can stick the "&" directly before the type name. typeName = "&" + typeName } } switch t := literalType.Underlying().(type) { case *types.Struct, *types.Array, *types.Slice, *types.Map: c.compositeLiteral(t, typeName, float64(score), addlEdits) case *types.Signature: // Add a literal completion for a signature type that implements // an interface. For example, offer "http.HandlerFunc()" when // expected type is "http.Handler". if source.IsInterface(expType) { c.basicLiteral(t, typeName, float64(score), addlEdits) } case *types.Basic: // Add a literal completion for basic types that implement our // expected interface (e.g. named string type http.Dir // implements http.FileSystem), or are identical to our expected // type (i.e. yielding a type conversion such as "float64()"). if source.IsInterface(expType) || types.Identical(expType, literalType) { c.basicLiteral(t, typeName, float64(score), addlEdits) } } } // If prefix matches "make", client may want a "make()" // invocation. We also include the type name to allow for more // flexible fuzzy matching. if score := c.matcher.Score("make." + matchName); !cand.takeAddress && score > 0 { switch literalType.Underlying().(type) { case *types.Slice: // The second argument to "make()" for slices is required, so default to "0". c.makeCall(typeName, "0", float64(score), addlEdits) case *types.Map, *types.Chan: // Maps and channels don't require the second argument, so omit // to keep things simple for now. c.makeCall(typeName, "", float64(score), addlEdits) } } // If prefix matches "func", client may want a function literal. if score := c.matcher.Score("func"); !cand.takeAddress && score > 0 && !source.IsInterface(expType) { switch t := literalType.Underlying().(type) { case *types.Signature: c.functionLiteral(ctx, t, float64(score)) } } } // prependEdit produces text edits that preprend the specified prefix // to the specified node. func prependEdit(fset *token.FileSet, m *protocol.ColumnMapper, node ast.Node, prefix string) ([]protocol.TextEdit, error) { rng := source.NewMappedRange(fset, m, node.Pos(), node.Pos()) spn, err := rng.Span() if err != nil { return nil, err } return source.ToProtocolEdits(m, []diff.TextEdit{{ Span: spn, NewText: prefix, }}) } // literalCandidateScore is the base score for literal candidates. // Literal candidates match the expected type so they should be high // scoring, but we want them ranked below lexical objects of the // correct type, so scale down highScore. const literalCandidateScore = highScore / 2 // functionLiteral adds a function literal completion item for the // given signature. func (c *completer) functionLiteral(ctx context.Context, sig *types.Signature, matchScore float64) { snip := &snippet.Builder{} snip.WriteText("func(") // First we generate names for each param and keep a seen count so // we know if we need to uniquify param names. For example, // "func(int)" will become "func(i int)", but "func(int, int64)" // will become "func(i1 int, i2 int64)". var ( paramNames = make([]string, sig.Params().Len()) paramNameCount = make(map[string]int) ) for i := 0; i < sig.Params().Len(); i++ { var ( p = sig.Params().At(i) name = p.Name() ) if name == "" { // If the param has no name in the signature, guess a name based // on the type. Use an empty qualifier to ignore the package. // For example, we want to name "http.Request" "r", not "hr". name = source.FormatVarType(ctx, c.snapshot, c.pkg, p, func(p *types.Package) string { return "" }) name = abbreviateTypeName(name) } paramNames[i] = name if name != "_" { paramNameCount[name]++ } } for n, c := range paramNameCount { // Any names we saw more than once will need a unique suffix added // on. Reset the count to 1 to act as the suffix for the first // name. if c >= 2 { paramNameCount[n] = 1 } else { delete(paramNameCount, n) } } for i := 0; i < sig.Params().Len(); i++ { if i > 0 { snip.WriteText(", ") } var ( p = sig.Params().At(i) name = paramNames[i] ) // Uniquify names by adding on an incrementing numeric suffix. if idx, found := paramNameCount[name]; found { paramNameCount[name]++ name = fmt.Sprintf("%s%d", name, idx) } if name != p.Name() && c.opts.placeholders { // If we didn't use the signature's param name verbatim then we // may have chosen a poor name. Give the user a placeholder so // they can easily fix the name. snip.WritePlaceholder(func(b *snippet.Builder) { b.WriteText(name) }) } else { snip.WriteText(name) } // If the following param's type is identical to this one, omit // this param's type string. For example, emit "i, j int" instead // of "i int, j int". if i == sig.Params().Len()-1 || !types.Identical(p.Type(), sig.Params().At(i+1).Type()) { snip.WriteText(" ") typeStr := source.FormatVarType(ctx, c.snapshot, c.pkg, p, c.qf) if sig.Variadic() && i == sig.Params().Len()-1 { typeStr = strings.Replace(typeStr, "[]", "...", 1) } snip.WriteText(typeStr) } } snip.WriteText(")") results := sig.Results() if results.Len() > 0 { snip.WriteText(" ") } resultsNeedParens := results.Len() > 1 || results.Len() == 1 && results.At(0).Name() != "" if resultsNeedParens { snip.WriteText("(") } for i := 0; i < results.Len(); i++ { if i > 0 { snip.WriteText(", ") } r := results.At(i) if name := r.Name(); name != "" { snip.WriteText(name + " ") } snip.WriteText(source.FormatVarType(ctx, c.snapshot, c.pkg, r, c.qf)) } if resultsNeedParens { snip.WriteText(")") } snip.WriteText(" {") snip.WriteFinalTabstop() snip.WriteText("}") c.items = append(c.items, CompletionItem{ Label: "func(...) {}", Score: matchScore * literalCandidateScore, Kind: protocol.VariableCompletion, snippet: snip, }) } // abbreviateTypeName abbreviates type names into acronyms. For // example, "fooBar" is abbreviated "fb". Care is taken to ignore // non-identifier runes. For example, "[]int" becomes "i", and // "struct { i int }" becomes "s". func abbreviateTypeName(s string) string { var ( b strings.Builder useNextUpper bool ) // Trim off leading non-letters. We trim everything between "[" and // "]" to handle array types like "[someConst]int". var inBracket bool s = strings.TrimFunc(s, func(r rune) bool { if inBracket { inBracket = r != ']' return true } if r == '[' { inBracket = true } return !unicode.IsLetter(r) }) for i, r := range s { // Stop if we encounter a non-identifier rune. if !unicode.IsLetter(r) && !unicode.IsNumber(r) { break } if i == 0 { b.WriteRune(unicode.ToLower(r)) } if unicode.IsUpper(r) { if useNextUpper { b.WriteRune(unicode.ToLower(r)) useNextUpper = false } } else { useNextUpper = true } } return b.String() } // compositeLiteral adds a composite literal completion item for the given typeName. func (c *completer) compositeLiteral(T types.Type, typeName string, matchScore float64, edits []protocol.TextEdit) { snip := &snippet.Builder{} snip.WriteText(typeName + "{") // Don't put the tab stop inside the composite literal curlies "{}" // for structs that have no accessible fields. if strct, ok := T.(*types.Struct); !ok || fieldsAccessible(strct, c.pkg.GetTypes()) { snip.WriteFinalTabstop() } snip.WriteText("}") nonSnippet := typeName + "{}" c.items = append(c.items, CompletionItem{ Label: nonSnippet, InsertText: nonSnippet, Score: matchScore * literalCandidateScore, Kind: protocol.VariableCompletion, AdditionalTextEdits: edits, snippet: snip, }) } // basicLiteral adds a literal completion item for the given basic // type name typeName. func (c *completer) basicLiteral(T types.Type, typeName string, matchScore float64, edits []protocol.TextEdit) { snip := &snippet.Builder{} snip.WriteText(typeName + "(") snip.WriteFinalTabstop() snip.WriteText(")") nonSnippet := typeName + "()" c.items = append(c.items, CompletionItem{ Label: nonSnippet, InsertText: nonSnippet, Detail: T.String(), Score: matchScore * literalCandidateScore, Kind: protocol.VariableCompletion, AdditionalTextEdits: edits, snippet: snip, }) } // makeCall adds a completion item for a "make()" call given a specific type. func (c *completer) makeCall(typeName string, secondArg string, matchScore float64, edits []protocol.TextEdit) { // Keep it simple and don't add any placeholders for optional "make()" arguments. snip := &snippet.Builder{} snip.WriteText("make(" + typeName) if secondArg != "" { snip.WriteText(", ") snip.WritePlaceholder(func(b *snippet.Builder) { if c.opts.placeholders { b.WriteText(secondArg) } }) } snip.WriteText(")") var nonSnippet strings.Builder nonSnippet.WriteString("make(" + typeName) if secondArg != "" { nonSnippet.WriteString(", ") nonSnippet.WriteString(secondArg) } nonSnippet.WriteByte(')') c.items = append(c.items, CompletionItem{ Label: nonSnippet.String(), InsertText: nonSnippet.String(), Score: matchScore * literalCandidateScore, Kind: protocol.FunctionCompletion, AdditionalTextEdits: edits, snippet: snip, }) }