Skip to content

Commit 4bcf6a3

Browse files
findleyrgopherbot
authored andcommitted
internal/golang: add a fast path for FormatVarType with gopls at 1.23
Now that gopls is only built with Go 1.23, we can rely on types.TypeString to preserve alias information, and only need the bespoke logic of FormatVarType to handle the cases where types contain an invalid type. This should improve performance for completion, particularly since it affects the single-threaded construction of candidates. For example, here is the impact on a relevant benchmark: Completion/kubernetes_selector/edit=false/unimported=false/budget=100ms Results: │ before.txt │ after.txt │ │ sec/op │ sec/op vs base │ 81.29m ± 1% 65.83m ± 1% -19.02% (p=0.000 n=10) │ before.txt │ after.txt │ │ cpu_seconds/op │ cpu_seconds/op vs base │ 151.8m ± 15% 101.1m ± 33% -33.36% (p=0.000 n=10) Change-Id: I60d890ca102a97cf6b198621ba82afe7eeab7fb9 Reviewed-on: https://go-review.googlesource.com/c/tools/+/611836 LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Robert Findley <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent 7398f36 commit 4bcf6a3

File tree

3 files changed

+30
-8
lines changed

3 files changed

+30
-8
lines changed

gopls/internal/golang/types_format.go

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,9 @@ func NewSignature(ctx context.Context, s *cache.Snapshot, pkg *cache.Package, si
214214
if err != nil {
215215
return nil, err
216216
}
217+
if sig.Variadic() && i == sig.Params().Len()-1 {
218+
typ = strings.Replace(typ, "[]", "...", 1)
219+
}
217220
p := typ
218221
if el.Name() != "" {
219222
p = el.Name() + " " + typ
@@ -261,13 +264,32 @@ func NewSignature(ctx context.Context, s *cache.Snapshot, pkg *cache.Package, si
261264
}, nil
262265
}
263266

267+
// We look for 'invalidTypeString' to determine if we can use the fast path for
268+
// FormatVarType.
269+
var invalidTypeString = types.Typ[types.Invalid].String()
270+
264271
// FormatVarType formats a *types.Var, accounting for type aliases.
265272
// To do this, it looks in the AST of the file in which the object is declared.
266273
// On any errors, it always falls back to types.TypeString.
267274
//
268275
// TODO(rfindley): this function could return the actual name used in syntax,
269276
// for better parameter names.
270277
func FormatVarType(ctx context.Context, snapshot *cache.Snapshot, srcpkg *cache.Package, obj *types.Var, qf types.Qualifier, mq MetadataQualifier) (string, error) {
278+
typeString := types.TypeString(obj.Type(), qf)
279+
// Fast path: if the type string does not contain 'invalid type', we no
280+
// longer need to do any special handling, thanks to materialized aliases in
281+
// Go 1.23+.
282+
//
283+
// Unfortunately, due to the handling of invalid types, we can't quite delete
284+
// the rather complicated preexisting logic of FormatVarType--it isn't an
285+
// acceptable regression to start printing "invalid type" in completion or
286+
// signature help. strings.Contains is conservative: the type string of a
287+
// valid type may actually contain "invalid type" (due to struct tags or
288+
// field formatting), but such cases should be exceedingly rare.
289+
if !strings.Contains(typeString, invalidTypeString) {
290+
return typeString, nil
291+
}
292+
271293
// TODO(rfindley): This looks wrong. The previous comment said:
272294
// "If the given expr refers to a type parameter, then use the
273295
// object's Type instead of the type parameter declaration. This helps
@@ -280,13 +302,13 @@ func FormatVarType(ctx context.Context, snapshot *cache.Snapshot, srcpkg *cache.
280302
//
281303
// Left this during refactoring in order to preserve pre-existing logic.
282304
if typeparams.IsTypeParam(obj.Type()) {
283-
return types.TypeString(obj.Type(), qf), nil
305+
return typeString, nil
284306
}
285307

286308
if isBuiltin(obj) {
287309
// This is defensive, though it is extremely unlikely we'll ever have a
288310
// builtin var.
289-
return types.TypeString(obj.Type(), qf), nil
311+
return typeString, nil
290312
}
291313

292314
// TODO(rfindley): parsing to produce candidates can be costly; consider
@@ -309,26 +331,26 @@ func FormatVarType(ctx context.Context, snapshot *cache.Snapshot, srcpkg *cache.
309331
// for parameterized decls.
310332
if decl, _ := decl.(*ast.FuncDecl); decl != nil {
311333
if decl.Type.TypeParams.NumFields() > 0 {
312-
return types.TypeString(obj.Type(), qf), nil // in generic function
334+
return typeString, nil // in generic function
313335
}
314336
if decl.Recv != nil && len(decl.Recv.List) > 0 {
315337
rtype := decl.Recv.List[0].Type
316338
if e, ok := rtype.(*ast.StarExpr); ok {
317339
rtype = e.X
318340
}
319341
if x, _, _, _ := typeparams.UnpackIndexExpr(rtype); x != nil {
320-
return types.TypeString(obj.Type(), qf), nil // in method of generic type
342+
return typeString, nil // in method of generic type
321343
}
322344
}
323345
}
324346
if spec, _ := spec.(*ast.TypeSpec); spec != nil && spec.TypeParams.NumFields() > 0 {
325-
return types.TypeString(obj.Type(), qf), nil // in generic type decl
347+
return typeString, nil // in generic type decl
326348
}
327349

328350
if field == nil {
329351
// TODO(rfindley): we should never reach here from an ordinary var, so
330352
// should probably return an error here.
331-
return types.TypeString(obj.Type(), qf), nil
353+
return typeString, nil
332354
}
333355
expr := field.Type
334356

gopls/internal/test/marker/marker_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1412,7 +1412,7 @@ func snippetMarker(mark marker, src protocol.Location, label completionLabel, wa
14121412
return
14131413
}
14141414
if got != want {
1415-
mark.errorf("snippets do not match: got %q, want %q", got, want)
1415+
mark.errorf("snippets do not match: got:\n%q\nwant:\n%q", got, want)
14161416
}
14171417
}
14181418

gopls/internal/test/marker/testdata/completion/foobarbaz.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ func _() {
476476

477477
const two = 2
478478
var builtinTypes func([]int, [two]bool, map[string]string, struct{ i int }, interface{ foo() }, <-chan int)
479-
builtinTypes = f //@snippet(" //", litFunc, "func(i1 []int, b [two]bool, m map[string]string, s struct{ i int \\}, i2 interface{ foo() \\}, c <-chan int) {$0\\}")
479+
builtinTypes = f //@snippet(" //", litFunc, "func(i1 []int, b [2]bool, m map[string]string, s struct{i int\\}, i2 interface{foo()\\}, c <-chan int) {$0\\}")
480480

481481
var _ func(ast.Node) = f //@snippet(" //", litFunc, "func(n ast.Node) {$0\\}")
482482
var _ func(error) = f //@snippet(" //", litFunc, "func(err error) {$0\\}")

0 commit comments

Comments
 (0)