mirror of
https://github.com/golang/go
synced 2024-10-01 05:18:33 -06:00
5a76f03bc7
In cases like: var foo []io.Writer var buf *bytes.Buffer foo = append(foo, <>) we weren't giving "buf" a good score. When comparing the candidate type *bytes.Buffer to the (variadic) expected type []io.Writer we were turning the candidate type into []*bytes.Buffer. However, of course, []*bytes.Buffer is not assignable to []io.Writer, so the types didn't match. Now we instead turn the expected type []io.Writer into io.Writer and compare to *bytes.Buffer. I fixed the @rank test note to check that the candidates' scores are strictly decreasing. Previously it would allow candidates with the same score if they happened to be in the right order. This made it easier to right a test for this issue, but also uncovered an issue with untyped completion logic. I fixed it to do the untyped constant check if _either_ the expected or candidate type is untyped (previously it required the candidate type to be untyped). Fixes golang/go#35625. Change-Id: I9a837d6a781669cb7a2f1d6d3d7f360c85be49eb Reviewed-on: https://go-review.googlesource.com/c/tools/+/207518 Reviewed-by: Rebecca Stambler <rstambler@golang.org> Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
187 lines
4.9 KiB
Go
187 lines
4.9 KiB
Go
package tests
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
|
"golang.org/x/tools/internal/lsp/source"
|
|
)
|
|
|
|
func ToProtocolCompletionItems(items []source.CompletionItem) []protocol.CompletionItem {
|
|
var result []protocol.CompletionItem
|
|
for _, item := range items {
|
|
result = append(result, ToProtocolCompletionItem(item))
|
|
}
|
|
return result
|
|
}
|
|
|
|
func ToProtocolCompletionItem(item source.CompletionItem) protocol.CompletionItem {
|
|
pItem := protocol.CompletionItem{
|
|
Label: item.Label,
|
|
Kind: item.Kind,
|
|
Detail: item.Detail,
|
|
Documentation: item.Documentation,
|
|
InsertText: item.InsertText,
|
|
TextEdit: &protocol.TextEdit{
|
|
NewText: item.Snippet(),
|
|
},
|
|
// Negate score so best score has lowest sort text like real API.
|
|
SortText: fmt.Sprint(-item.Score),
|
|
}
|
|
if pItem.InsertText == "" {
|
|
pItem.InsertText = pItem.Label
|
|
}
|
|
return pItem
|
|
}
|
|
|
|
func FilterBuiltins(items []protocol.CompletionItem) []protocol.CompletionItem {
|
|
var got []protocol.CompletionItem
|
|
for _, item := range items {
|
|
if isBuiltin(item.Label, item.Detail, item.Kind) {
|
|
continue
|
|
}
|
|
got = append(got, item)
|
|
}
|
|
return got
|
|
}
|
|
|
|
func isBuiltin(label, detail string, kind protocol.CompletionItemKind) bool {
|
|
if detail == "" && kind == protocol.ClassCompletion {
|
|
return true
|
|
}
|
|
// Remaining builtin constants, variables, interfaces, and functions.
|
|
trimmed := label
|
|
if i := strings.Index(trimmed, "("); i >= 0 {
|
|
trimmed = trimmed[:i]
|
|
}
|
|
switch trimmed {
|
|
case "append", "cap", "close", "complex", "copy", "delete",
|
|
"error", "false", "imag", "iota", "len", "make", "new",
|
|
"nil", "panic", "print", "println", "real", "recover", "true":
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func CheckCompletionOrder(want, got []protocol.CompletionItem, strictScores bool) string {
|
|
var (
|
|
matchedIdxs []int
|
|
lastGotIdx int
|
|
lastGotSort float64
|
|
inOrder = true
|
|
errorMsg = "completions out of order"
|
|
)
|
|
for _, w := range want {
|
|
var found bool
|
|
for i, g := range got {
|
|
if w.Label == g.Label && w.Detail == g.Detail && w.Kind == g.Kind {
|
|
matchedIdxs = append(matchedIdxs, i)
|
|
found = true
|
|
|
|
if i < lastGotIdx {
|
|
inOrder = false
|
|
}
|
|
lastGotIdx = i
|
|
|
|
sort, _ := strconv.ParseFloat(g.SortText, 64)
|
|
if strictScores && len(matchedIdxs) > 1 && sort <= lastGotSort {
|
|
inOrder = false
|
|
errorMsg = "candidate scores not strictly decreasing"
|
|
}
|
|
lastGotSort = sort
|
|
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
return summarizeCompletionItems(-1, []protocol.CompletionItem{w}, got, "didn't find expected completion")
|
|
}
|
|
}
|
|
|
|
sort.Ints(matchedIdxs)
|
|
matched := make([]protocol.CompletionItem, 0, len(matchedIdxs))
|
|
for _, idx := range matchedIdxs {
|
|
matched = append(matched, got[idx])
|
|
}
|
|
|
|
if !inOrder {
|
|
return summarizeCompletionItems(-1, want, matched, errorMsg)
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func DiffSnippets(want string, got *protocol.CompletionItem) string {
|
|
if want == "" {
|
|
if got != nil {
|
|
return fmt.Sprintf("expected no snippet but got %s", got.TextEdit.NewText)
|
|
}
|
|
} else {
|
|
if got == nil {
|
|
return fmt.Sprintf("couldn't find completion matching %q", want)
|
|
}
|
|
if want != got.TextEdit.NewText {
|
|
return fmt.Sprintf("expected snippet %q, got %q", want, got.TextEdit.NewText)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func FindItem(list []protocol.CompletionItem, want source.CompletionItem) *protocol.CompletionItem {
|
|
for _, item := range list {
|
|
if item.Label == want.Label {
|
|
return &item
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DiffCompletionItems prints the diff between expected and actual completion
|
|
// test results.
|
|
func DiffCompletionItems(want, got []protocol.CompletionItem) string {
|
|
if len(got) != len(want) {
|
|
return summarizeCompletionItems(-1, want, got, "different lengths got %v want %v", len(got), len(want))
|
|
}
|
|
for i, w := range want {
|
|
g := got[i]
|
|
if w.Label != g.Label {
|
|
return summarizeCompletionItems(i, want, got, "incorrect Label got %v want %v", g.Label, w.Label)
|
|
}
|
|
if w.Detail != g.Detail {
|
|
return summarizeCompletionItems(i, want, got, "incorrect Detail got %v want %v", g.Detail, w.Detail)
|
|
}
|
|
if w.Documentation != "" && !strings.HasPrefix(w.Documentation, "@") {
|
|
if w.Documentation != g.Documentation {
|
|
return summarizeCompletionItems(i, want, got, "incorrect Documentation got %v want %v", g.Documentation, w.Documentation)
|
|
}
|
|
}
|
|
if w.Kind != g.Kind {
|
|
return summarizeCompletionItems(i, want, got, "incorrect Kind got %v want %v", g.Kind, w.Kind)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func summarizeCompletionItems(i int, want, got []protocol.CompletionItem, reason string, args ...interface{}) string {
|
|
msg := &bytes.Buffer{}
|
|
fmt.Fprint(msg, "completion failed")
|
|
if i >= 0 {
|
|
fmt.Fprintf(msg, " at %d", i)
|
|
}
|
|
fmt.Fprint(msg, " because of ")
|
|
fmt.Fprintf(msg, reason, args...)
|
|
fmt.Fprint(msg, ":\nexpected:\n")
|
|
for _, d := range want {
|
|
fmt.Fprintf(msg, " %v\n", d)
|
|
}
|
|
fmt.Fprintf(msg, "got:\n")
|
|
for _, d := range got {
|
|
fmt.Fprintf(msg, " %v\n", d)
|
|
}
|
|
return msg.String()
|
|
}
|