Skip to content

Commit 74543c4

Browse files
muirdmfindleyr
authored andcommitted
internal/lsp/source: fix composite literal type name completion
Fix completion in the following cases: type foo struct{} // now we offer "&foo" instead of "foo" var _ *foo = fo<>{} struct { f *foo }{ // now we offer "&foo" instead of "*foo" f: fo<>{}, } Composite literal type names are a bit special because they are part of an arbitrary value expression rather than just a standalone type name expression. In particular, they can be preceded by "&", which affects how they relate to the surrounding context. The "&" doesn't technically apply to the type name, but we must take it into account. I made three changes to fix the behavior: 1. When we want to make a composite literal type name into a pointer, we use "&" instead of "*". 2. Record if a composite literal type is already has a "&" so we don't add it again. 3. Fix "var _ *foo = fo<>{}" to properly infer expected type of "*foo" by not stopping at *ast.CompositeLit searching up AST path when the position is in the type name (as opposed to within the curlies). Change-Id: Iee828f259eb939646b68f5066614ea3a262585c2 Reviewed-on: https://go-review.googlesource.com/c/tools/+/247525 Run-TryBot: Muir Manders <muir@mnd.rs> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
1 parent c886c0b commit 74543c4

File tree

4 files changed

+86
-49
lines changed

4 files changed

+86
-49
lines changed

internal/lsp/source/completion.go

Lines changed: 65 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,7 +1445,7 @@ func enclosingCompositeLiteral(path []ast.Node, pos token.Pos, info *types.Info)
14451445

14461446
return &clInfo
14471447
default:
1448-
if breaksExpectedTypeInference(n) {
1448+
if breaksExpectedTypeInference(n, pos) {
14491449
return nil
14501450
}
14511451
}
@@ -1535,11 +1535,11 @@ type typeModifier struct {
15351535
type typeMod int
15361536

15371537
const (
1538-
star typeMod = iota // pointer indirection for expressions, pointer indicator for types
1539-
address // address operator ("&")
1540-
chanRead // channel read operator ("<-")
1541-
slice // make a slice type ("[]" in "[]int")
1542-
array // make an array type ("[2]" in "[2]int")
1538+
dereference typeMod = iota // pointer indirection: "*"
1539+
reference // adds level of pointer: "&" for values, "*" for type names
1540+
chanRead // channel read operator ("<-")
1541+
slice // make a slice type ("[]" in "[]int")
1542+
array // make an array type ("[2]" in "[2]int")
15431543
)
15441544

15451545
type objKind int
@@ -1651,6 +1651,10 @@ type typeNameInference struct {
16511651
// seenTypeSwitchCases tracks types that have already been used by
16521652
// the containing type switch.
16531653
seenTypeSwitchCases []types.Type
1654+
1655+
// compLitType is true if we are completing a composite literal type
1656+
// name, e.g "foo<>{}".
1657+
compLitType bool
16541658
}
16551659

16561660
// expectedCandidate returns information about the expected candidate
@@ -1862,19 +1866,19 @@ Nodes:
18621866
}
18631867
return inf
18641868
case *ast.StarExpr:
1865-
inf.modifiers = append(inf.modifiers, typeModifier{mod: star})
1869+
inf.modifiers = append(inf.modifiers, typeModifier{mod: dereference})
18661870
case *ast.UnaryExpr:
18671871
switch node.Op {
18681872
case token.AND:
1869-
inf.modifiers = append(inf.modifiers, typeModifier{mod: address})
1873+
inf.modifiers = append(inf.modifiers, typeModifier{mod: reference})
18701874
case token.ARROW:
18711875
inf.modifiers = append(inf.modifiers, typeModifier{mod: chanRead})
18721876
}
18731877
case *ast.DeferStmt, *ast.GoStmt:
18741878
inf.objKind |= kindFunc
18751879
return inf
18761880
default:
1877-
if breaksExpectedTypeInference(node) {
1881+
if breaksExpectedTypeInference(node, c.pos) {
18781882
return inf
18791883
}
18801884
}
@@ -1928,15 +1932,15 @@ func objChain(info *types.Info, e ast.Expr) []types.Object {
19281932
func (ci candidateInference) applyTypeModifiers(typ types.Type, addressable bool) types.Type {
19291933
for _, mod := range ci.modifiers {
19301934
switch mod.mod {
1931-
case star:
1935+
case dereference:
19321936
// For every "*" indirection operator, remove a pointer layer
19331937
// from candidate type.
19341938
if ptr, ok := typ.Underlying().(*types.Pointer); ok {
19351939
typ = ptr.Elem()
19361940
} else {
19371941
return nil
19381942
}
1939-
case address:
1943+
case reference:
19401944
// For every "&" address operator, add another pointer layer to
19411945
// candidate type, if the candidate is addressable.
19421946
if addressable {
@@ -1961,8 +1965,7 @@ func (ci candidateInference) applyTypeModifiers(typ types.Type, addressable bool
19611965
func (ci candidateInference) applyTypeNameModifiers(typ types.Type) types.Type {
19621966
for _, mod := range ci.typeName.modifiers {
19631967
switch mod.mod {
1964-
case star:
1965-
// For every "*" indicator, add a pointer layer to type name.
1968+
case reference:
19661969
typ = types.NewPointer(typ)
19671970
case array:
19681971
typ = types.NewArray(typ, mod.arrayLen)
@@ -2006,9 +2009,17 @@ func findSwitchStmt(path []ast.Node, pos token.Pos, c *ast.CaseClause) ast.Stmt
20062009
// breaksExpectedTypeInference reports if an expression node's type is unrelated
20072010
// to its child expression node types. For example, "Foo{Bar: x.Baz(<>)}" should
20082011
// expect a function argument, not a composite literal value.
2009-
func breaksExpectedTypeInference(n ast.Node) bool {
2010-
switch n.(type) {
2011-
case *ast.FuncLit, *ast.CallExpr, *ast.IndexExpr, *ast.SliceExpr, *ast.CompositeLit:
2012+
func breaksExpectedTypeInference(n ast.Node, pos token.Pos) bool {
2013+
switch n := n.(type) {
2014+
case *ast.CompositeLit:
2015+
// Doesn't break inference if pos is in type name.
2016+
// For example: "Foo<>{Bar: 123}"
2017+
return !nodeContains(n.Type, pos)
2018+
case *ast.CallExpr:
2019+
// Doesn't break inference if pos is in func name.
2020+
// For example: "Foo<>(123)"
2021+
return !nodeContains(n.Fun, pos)
2022+
case *ast.FuncLit, *ast.IndexExpr, *ast.SliceExpr:
20122023
return true
20132024
default:
20142025
return false
@@ -2017,13 +2028,7 @@ func breaksExpectedTypeInference(n ast.Node) bool {
20172028

20182029
// expectTypeName returns information about the expected type name at position.
20192030
func expectTypeName(c *completer) typeNameInference {
2020-
var (
2021-
wantTypeName bool
2022-
wantComparable bool
2023-
modifiers []typeModifier
2024-
assertableFrom types.Type
2025-
seenTypeSwitchCases []types.Type
2026-
)
2031+
var inf typeNameInference
20272032

20282033
Nodes:
20292034
for i, p := range c.path {
@@ -2034,20 +2039,20 @@ Nodes:
20342039
// InterfaceType. We don't need to worry about the field name
20352040
// because completion bails out early if pos is in an *ast.Ident
20362041
// that defines an object.
2037-
wantTypeName = true
2042+
inf.wantTypeName = true
20382043
break Nodes
20392044
case *ast.CaseClause:
20402045
// Expect type names in type switch case clauses.
20412046
if swtch, ok := findSwitchStmt(c.path[i+1:], c.pos, n).(*ast.TypeSwitchStmt); ok {
20422047
// The case clause types must be assertable from the type switch parameter.
20432048
ast.Inspect(swtch.Assign, func(n ast.Node) bool {
20442049
if ta, ok := n.(*ast.TypeAssertExpr); ok {
2045-
assertableFrom = c.pkg.GetTypesInfo().TypeOf(ta.X)
2050+
inf.assertableFrom = c.pkg.GetTypesInfo().TypeOf(ta.X)
20462051
return false
20472052
}
20482053
return true
20492054
})
2050-
wantTypeName = true
2055+
inf.wantTypeName = true
20512056

20522057
// Track the types that have already been used in this
20532058
// switch's case statements so we don't recommend them.
@@ -2060,7 +2065,7 @@ Nodes:
20602065
}
20612066

20622067
if t := c.pkg.GetTypesInfo().TypeOf(typeExpr); t != nil {
2063-
seenTypeSwitchCases = append(seenTypeSwitchCases, t)
2068+
inf.seenTypeSwitchCases = append(inf.seenTypeSwitchCases, t)
20642069
}
20652070
}
20662071
}
@@ -2072,33 +2077,43 @@ Nodes:
20722077
// Expect type names in type assert expressions.
20732078
if n.Lparen < c.pos && c.pos <= n.Rparen {
20742079
// The type in parens must be assertable from the expression type.
2075-
assertableFrom = c.pkg.GetTypesInfo().TypeOf(n.X)
2076-
wantTypeName = true
2080+
inf.assertableFrom = c.pkg.GetTypesInfo().TypeOf(n.X)
2081+
inf.wantTypeName = true
20772082
break Nodes
20782083
}
20792084
return typeNameInference{}
20802085
case *ast.StarExpr:
2081-
modifiers = append(modifiers, typeModifier{mod: star})
2086+
inf.modifiers = append(inf.modifiers, typeModifier{mod: reference})
20822087
case *ast.CompositeLit:
20832088
// We want a type name if position is in the "Type" part of a
20842089
// composite literal (e.g. "Foo<>{}").
20852090
if n.Type != nil && n.Type.Pos() <= c.pos && c.pos <= n.Type.End() {
2086-
wantTypeName = true
2091+
inf.wantTypeName = true
2092+
inf.compLitType = true
2093+
2094+
if i < len(c.path)-1 {
2095+
// Track preceding "&" operator. Technically it applies to
2096+
// the composite literal and not the type name, but if
2097+
// affects our type completion nonetheless.
2098+
if u, ok := c.path[i+1].(*ast.UnaryExpr); ok && u.Op == token.AND {
2099+
inf.modifiers = append(inf.modifiers, typeModifier{mod: reference})
2100+
}
2101+
}
20872102
}
20882103
break Nodes
20892104
case *ast.ArrayType:
20902105
// If we are inside the "Elt" part of an array type, we want a type name.
20912106
if n.Elt.Pos() <= c.pos && c.pos <= n.Elt.End() {
2092-
wantTypeName = true
2107+
inf.wantTypeName = true
20932108
if n.Len == nil {
20942109
// No "Len" expression means a slice type.
2095-
modifiers = append(modifiers, typeModifier{mod: slice})
2110+
inf.modifiers = append(inf.modifiers, typeModifier{mod: slice})
20962111
} else {
20972112
// Try to get the array type using the constant value of "Len".
20982113
tv, ok := c.pkg.GetTypesInfo().Types[n.Len]
20992114
if ok && tv.Value != nil && tv.Value.Kind() == constant.Int {
21002115
if arrayLen, ok := constant.Int64Val(tv.Value); ok {
2101-
modifiers = append(modifiers, typeModifier{mod: array, arrayLen: arrayLen})
2116+
inf.modifiers = append(inf.modifiers, typeModifier{mod: array, arrayLen: arrayLen})
21022117
}
21032118
}
21042119
}
@@ -2114,34 +2129,28 @@ Nodes:
21142129
break Nodes
21152130
}
21162131
case *ast.MapType:
2117-
wantTypeName = true
2132+
inf.wantTypeName = true
21182133
if n.Key != nil {
2119-
wantComparable = nodeContains(n.Key, c.pos)
2134+
inf.wantComparable = nodeContains(n.Key, c.pos)
21202135
} else {
21212136
// If the key is empty, assume we are completing the key if
21222137
// pos is directly after the "map[".
2123-
wantComparable = c.pos == n.Pos()+token.Pos(len("map["))
2138+
inf.wantComparable = c.pos == n.Pos()+token.Pos(len("map["))
21242139
}
21252140
break Nodes
21262141
case *ast.ValueSpec:
2127-
wantTypeName = nodeContains(n.Type, c.pos)
2142+
inf.wantTypeName = nodeContains(n.Type, c.pos)
21282143
break Nodes
21292144
case *ast.TypeSpec:
2130-
wantTypeName = nodeContains(n.Type, c.pos)
2145+
inf.wantTypeName = nodeContains(n.Type, c.pos)
21312146
default:
2132-
if breaksExpectedTypeInference(p) {
2147+
if breaksExpectedTypeInference(p, c.pos) {
21332148
return typeNameInference{}
21342149
}
21352150
}
21362151
}
21372152

2138-
return typeNameInference{
2139-
wantTypeName: wantTypeName,
2140-
wantComparable: wantComparable,
2141-
modifiers: modifiers,
2142-
assertableFrom: assertableFrom,
2143-
seenTypeSwitchCases: seenTypeSwitchCases,
2144-
}
2153+
return inf
21452154
}
21462155

21472156
func (c *completer) fakeObj(T types.Type) *types.Var {
@@ -2519,7 +2528,15 @@ func (c *completer) matchingTypeName(cand *candidate) bool {
25192528
}
25202529

25212530
if !isInterface(t) && typeMatches(types.NewPointer(t)) {
2522-
cand.makePointer = true
2531+
if c.inference.typeName.compLitType {
2532+
// If we are completing a composite literal type as in
2533+
// "foo<>{}", to make a pointer we must prepend "&".
2534+
cand.takeAddress = true
2535+
} else {
2536+
// If we are completing a normal type name such as "foo<>", to
2537+
// make a pointer we must prepend "*".
2538+
cand.makePointer = true
2539+
}
25232540
return true
25242541
}
25252542

internal/lsp/testdata/lsp/primarymod/complit/complit.go.in

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,20 @@ func _() {
9494
_ = position{X} //@complete("}", fieldX, varX)
9595
}
9696

97+
func _() {
98+
type foo struct{} //@item(complitFoo, "foo", "struct{...}", "struct")
99+
100+
"&foo" //@item(complitAndFoo, "&foo", "struct{...}", "struct")
101+
102+
var _ *foo = &fo{} //@rank("{", complitFoo)
103+
var _ *foo = fo{} //@rank("{", complitAndFoo)
104+
105+
struct { a, b *foo }{
106+
a: &fo{}, //@rank("{", complitFoo)
107+
b: fo{}, //@rank("{", complitAndFoo)
108+
}
109+
}
110+
97111
func _() {
98112
_ := position{
99113
X: 1, //@complete("X", fieldX),complete(" 1", exportedFunc, multilineWithPrefix, structPosition, cVar, exportedConst, exportedType)

internal/lsp/testdata/lsp/primarymod/snippets/literal_snippets.go.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ func _() {
199199
ptrStruct{
200200
p: &ptrSt, //@rank(",", litPtrStruct)
201201
}
202+
203+
&ptrStruct{} //@item(litPtrStructPtr, "&ptrStruct{}", "", "var")
204+
205+
&ptrStruct{
206+
p: ptrSt, //@rank(",", litPtrStructPtr)
207+
}
202208
}
203209

204210
func _() {

internal/lsp/testdata/lsp/summary.txt.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ CompletionSnippetCount = 85
66
UnimportedCompletionsCount = 6
77
DeepCompletionsCount = 5
88
FuzzyCompletionsCount = 8
9-
RankedCompletionsCount = 152
9+
RankedCompletionsCount = 157
1010
CaseSensitiveCompletionsCount = 4
1111
DiagnosticsCount = 44
1212
FoldingRangesCount = 2

0 commit comments

Comments
 (0)