Skip to content

Commit 82b6f75

Browse files
committed
internal/typesinternal: rollback of Zero{Value,Expr} for gopls
In CL 626537, the gopls behavior of formatting or generating a type expression for the zero value of a types.Type was changed to panic in the presence of invalid types. This leads to multiple panics in gopls (see tests). Fix by selectively rolling back this aspect of the change, as it relates to gopls. Fixes golang/go#70744 Change-Id: Ife9ae9a0fedf5a9d6b321cfb959f0d16eeec2986 Reviewed-on: https://go-review.googlesource.com/c/tools/+/634923 Reviewed-by: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent ab5f969 commit 82b6f75

File tree

5 files changed

+57
-4
lines changed

5 files changed

+57
-4
lines changed

gopls/internal/golang/completion/postfix_snippets.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,8 @@ func (a *postfixTmplArgs) TypeName(t types.Type) (string, error) {
442442

443443
// Zero return the zero value representation of type t
444444
func (a *postfixTmplArgs) Zero(t types.Type) string {
445-
return typesinternal.ZeroString(t, a.qf)
445+
// TODO: use typesinternal.ZeroString, once it supports invalid types.
446+
return formatZeroValue(t, a.qf)
446447
}
447448

448449
func (a *postfixTmplArgs) IsIdent() bool {

gopls/internal/golang/completion/statements.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"golang.org/x/tools/gopls/internal/golang"
1616
"golang.org/x/tools/gopls/internal/golang/completion/snippet"
1717
"golang.org/x/tools/gopls/internal/protocol"
18-
"golang.org/x/tools/internal/typesinternal"
1918
)
2019

2120
// addStatementCandidates adds full statement completion candidates
@@ -295,7 +294,7 @@ func (c *completer) addErrCheck() {
295294
} else {
296295
snip.WriteText("return ")
297296
for i := 0; i < result.Len()-1; i++ {
298-
snip.WriteText(typesinternal.ZeroString(result.At(i).Type(), c.qf))
297+
snip.WriteText(formatZeroValue(result.At(i).Type(), c.qf))
299298
snip.WriteText(", ")
300299
}
301300
snip.WritePlaceholder(func(b *snippet.Builder) {
@@ -405,7 +404,7 @@ func (c *completer) addReturnZeroValues() {
405404
fmt.Fprintf(&label, ", ")
406405
}
407406

408-
zero := typesinternal.ZeroString(result.At(i).Type(), c.qf)
407+
zero := formatZeroValue(result.At(i).Type(), c.qf)
409408
snip.WritePlaceholder(func(b *snippet.Builder) {
410409
b.WriteText(zero)
411410
})

gopls/internal/golang/completion/util.go

+25
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,31 @@ func prevStmt(pos token.Pos, path []ast.Node) ast.Stmt {
277277
return nil
278278
}
279279

280+
// formatZeroValue produces Go code representing the zero value of T. It
281+
// returns the empty string if T is invalid.
282+
//
283+
// TODO(rfindley): use typesinternal.ZeroString once we can sort out how to
284+
// propagate invalid types (see golang/go#70744).
285+
func formatZeroValue(T types.Type, qf types.Qualifier) string {
286+
switch u := T.Underlying().(type) {
287+
case *types.Basic:
288+
switch {
289+
case u.Info()&types.IsNumeric > 0:
290+
return "0"
291+
case u.Info()&types.IsString > 0:
292+
return `""`
293+
case u.Info()&types.IsBoolean > 0:
294+
return "false"
295+
default:
296+
return ""
297+
}
298+
case *types.Pointer, *types.Interface, *types.Chan, *types.Map, *types.Slice, *types.Signature:
299+
return "nil"
300+
default:
301+
return types.TypeString(T, qf) + "{}"
302+
}
303+
}
304+
280305
// isBasicKind returns whether t is a basic type of kind k.
281306
func isBasicKind(t types.Type, k types.BasicInfo) bool {
282307
b, _ := t.Underlying().(*types.Basic)

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

+21
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,20 @@ func two() error {
9898
//@snippet("", stmtTwoIfErrReturn, "if s.err != nil {\n\treturn ${1:s.err}\n\\}")
9999
}
100100

101+
-- if_err_check_return3.go --
102+
package statements
103+
104+
import "os"
105+
106+
// Check that completion logic handles an invalid return type.
107+
func badReturn() (NotAType, error) {
108+
_, err := os.Open("foo")
109+
//@snippet("", stmtOneIfErrReturn, "if err != nil {\n\treturn , ${1:err}\n\\}")
110+
111+
_, err = os.Open("foo")
112+
if er //@snippet(" //", stmtOneErrReturn, "err != nil {\n\treturn , ${1:err}\n\\}")
113+
}
114+
101115
-- if_err_check_test.go --
102116
package statements
103117

@@ -132,3 +146,10 @@ func foo() (int, string, error) {
132146
func bar() (int, string, error) {
133147
return //@snippet(" ", stmtReturnZeroValues, "return ${1:0}, ${2:\"\"}, ${3:nil}")
134148
}
149+
150+
151+
//@item(stmtReturnInvalidValues, `return `)
152+
153+
func invalidReturnStatement() NotAType {
154+
return //@snippet(" ", stmtReturnInvalidValues, "return ${1:}")
155+
}

internal/typesinternal/zerovalue.go

+7
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ func ZeroString(t types.Type, qf types.Qualifier) string {
8080
// ZeroExpr is defined for types that are suitable for variables.
8181
// It may panic for other types such as Tuple or Union.
8282
// See [ZeroString] for a variant that returns a string.
83+
//
84+
// TODO(rfindley): improve the behavior of this function in the presence of
85+
// invalid types. It should probably not return nil for
86+
// types.Typ[types.Invalid], but must to preserve previous behavior.
87+
// (golang/go#70744)
8388
func ZeroExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
8489
switch t := typ.(type) {
8590
case *types.Basic:
@@ -94,6 +99,8 @@ func ZeroExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
9499
fallthrough
95100
case t.Kind() == types.UntypedNil:
96101
return ast.NewIdent("nil")
102+
case t == types.Typ[types.Invalid]:
103+
return nil
97104
default:
98105
panic(fmt.Sprint("ZeroExpr for unexpected type:", t))
99106
}

0 commit comments

Comments
 (0)