internal/lsp: improve completion support for type conversions
Now when completing in code like:
foo := int64(<>)
we prefer candidates whose type is convertible to int64.
Change-Id: Iadc6cdc7de097ac30d8807d6f5aa21d83f89d756
GitHub-Last-Rev: a86dd72496ba752a1f20877c0594ec6a0ed8160e
GitHub-Pull-Request: golang/tools#127
Reviewed-on: https://go-review.googlesource.com/c/tools/+/183941
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/internal/lsp/source/completion.go b/internal/lsp/source/completion.go
index d06f9c6..e060c2b 100644
--- a/internal/lsp/source/completion.go
+++ b/internal/lsp/source/completion.go
@@ -727,6 +727,9 @@
// assertableFrom is a type that must be assertable to our candidate type.
assertableFrom types.Type
+
+ // convertibleTo is a type our candidate type must be convertible to.
+ convertibleTo types.Type
}
// expectedType returns information about the expected type for an expression at
@@ -741,8 +744,9 @@
}
var (
- modifiers []typeModifier
- typ types.Type
+ modifiers []typeModifier
+ typ types.Type
+ convertibleTo types.Type
)
Nodes:
@@ -774,6 +778,13 @@
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.info); typ != nil {
+ convertibleTo = typ
+ break Nodes
+ }
+
if tv, ok := c.info.Types[node.Fun]; ok {
if sig, ok := tv.Type.(*types.Signature); ok {
if sig.Params().Len() == 0 {
@@ -860,8 +871,9 @@
}
return typeInference{
- objType: typ,
- modifiers: modifiers,
+ objType: typ,
+ modifiers: modifiers,
+ convertibleTo: convertibleTo,
}
}
@@ -1045,6 +1057,10 @@
}
}
+ if c.expectedType.convertibleTo != nil {
+ return types.ConvertibleTo(objType, c.expectedType.convertibleTo)
+ }
+
return false
}
diff --git a/internal/lsp/source/util.go b/internal/lsp/source/util.go
index ef7d782..8fe0498 100644
--- a/internal/lsp/source/util.go
+++ b/internal/lsp/source/util.go
@@ -139,6 +139,27 @@
return ok
}
+// typeConversion returns the type being converted to if call is a type
+// conversion expression.
+func typeConversion(call *ast.CallExpr, info *types.Info) types.Type {
+ var ident *ast.Ident
+ switch expr := call.Fun.(type) {
+ case *ast.Ident:
+ ident = expr
+ case *ast.SelectorExpr:
+ ident = expr.Sel
+ default:
+ return nil
+ }
+
+ // Type conversion (e.g. "float64(foo)").
+ if fun, _ := info.ObjectOf(ident).(*types.TypeName); fun != nil {
+ return fun.Type()
+ }
+
+ return nil
+}
+
func formatParams(tup *types.Tuple, variadic bool, qf types.Qualifier) []string {
params := make([]string, 0, tup.Len())
for i := 0; i < tup.Len(); i++ {
diff --git a/internal/lsp/testdata/rank/convert_rank.go.in b/internal/lsp/testdata/rank/convert_rank.go.in
new file mode 100644
index 0000000..dc0a3a5
--- /dev/null
+++ b/internal/lsp/testdata/rank/convert_rank.go.in
@@ -0,0 +1,12 @@
+package rank
+
+func _() {
+ type strList []string
+ wantsStrList := func(strList) {}
+
+ var (
+ convA string //@item(convertA, "convA", "string", "var")
+ convB []string //@item(convertB, "convB", "[]string", "var")
+ )
+ wantsStrList(strList(conv)) //@complete("))", convertB, convertA)
+}