Skip to content

Commit e751756

Browse files
h9jianggopherbot
authored andcommitted
internal/analysisinternal: unify zero value function to typesinternal
Refactors the ZeroExpr and ZeroString functions to provide more consistent and correct handling of zero values for input types. - Refactor: Unify similar switch case statements in both functions with exception of types.Tuple. ZeroExpr panic due to the lack of a valid ast.Expr representation. - Fixing an issue where ZeroExpr returned nil for types.Array instead of a composite literal. - Adding support for type parameters in ZeroExpr, similar to ZeroString. - Consolidating tests for both functions into TestZeroValue. Change-Id: Ic77ae17ea091cf51bd4d4642186fe13093e0d461 Reviewed-on: https://go-review.googlesource.com/c/tools/+/627604 Reviewed-by: Tim King <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Alan Donovan <[email protected]> Auto-Submit: Hongxiang Jiang <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent a287481 commit e751756

File tree

8 files changed

+402
-211
lines changed

8 files changed

+402
-211
lines changed

gopls/internal/analysis/fillreturns/fillreturns.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"golang.org/x/tools/go/ast/astutil"
1919
"golang.org/x/tools/gopls/internal/fuzzy"
2020
"golang.org/x/tools/internal/analysisinternal"
21+
"golang.org/x/tools/internal/typesinternal"
2122
)
2223

2324
//go:embed doc.go
@@ -161,7 +162,7 @@ outer:
161162
if t := info.TypeOf(val); t == nil || !matchingTypes(t, retTyp) {
162163
continue
163164
}
164-
if !analysisinternal.IsZeroValue(val) {
165+
if !typesinternal.IsZeroExpr(val) {
165166
match, idx = val, j
166167
break
167168
}
@@ -183,7 +184,7 @@ outer:
183184
// If no identifier matches the pattern, generate a zero value.
184185
if best := fuzzy.BestMatch(retTyp.String(), names); best != "" {
185186
fixed[i] = ast.NewIdent(best)
186-
} else if zero := analysisinternal.ZeroValue(file, pass.Pkg, retTyp); zero != nil {
187+
} else if zero := typesinternal.ZeroExpr(file, pass.Pkg, retTyp); zero != nil {
187188
fixed[i] = zero
188189
} else {
189190
return nil, nil
@@ -194,7 +195,7 @@ outer:
194195
// Remove any non-matching "zero values" from the leftover values.
195196
var nonZeroRemaining []ast.Expr
196197
for _, expr := range remaining {
197-
if !analysisinternal.IsZeroValue(expr) {
198+
if !typesinternal.IsZeroExpr(expr) {
198199
nonZeroRemaining = append(nonZeroRemaining, expr)
199200
}
200201
}

gopls/internal/analysis/fillreturns/testdata/src/a/a.go.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func basic() (uint8, uint16, uint32, uint64, int8, int16, int32, int64, float32,
6767
}
6868

6969
func complex() (*int, []int, [2]int, map[int]int) {
70-
return nil, nil, nil, nil // want "return values"
70+
return nil, nil, [2]int{}, nil // want "return values"
7171
}
7272

7373
func structsAndInterfaces() (T, url.URL, T1, I, I1, io.Reader, Client, ast2.Stmt) {

gopls/internal/analysis/fillstruct/fillstruct.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -349,8 +349,8 @@ func populateValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
349349
}
350350

351351
case *types.Map:
352-
k := analysisinternal.TypeExpr(f, pkg, u.Key())
353-
v := analysisinternal.TypeExpr(f, pkg, u.Elem())
352+
k := typesinternal.TypeExpr(f, pkg, u.Key())
353+
v := typesinternal.TypeExpr(f, pkg, u.Elem())
354354
if k == nil || v == nil {
355355
return nil
356356
}
@@ -361,7 +361,7 @@ func populateValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
361361
},
362362
}
363363
case *types.Slice:
364-
s := analysisinternal.TypeExpr(f, pkg, u.Elem())
364+
s := typesinternal.TypeExpr(f, pkg, u.Elem())
365365
if s == nil {
366366
return nil
367367
}
@@ -372,7 +372,7 @@ func populateValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
372372
}
373373

374374
case *types.Array:
375-
a := analysisinternal.TypeExpr(f, pkg, u.Elem())
375+
a := typesinternal.TypeExpr(f, pkg, u.Elem())
376376
if a == nil {
377377
return nil
378378
}
@@ -386,7 +386,7 @@ func populateValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
386386
}
387387

388388
case *types.Chan:
389-
v := analysisinternal.TypeExpr(f, pkg, u.Elem())
389+
v := typesinternal.TypeExpr(f, pkg, u.Elem())
390390
if v == nil {
391391
return nil
392392
}
@@ -405,7 +405,7 @@ func populateValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
405405
}
406406

407407
case *types.Struct:
408-
s := analysisinternal.TypeExpr(f, pkg, typ)
408+
s := typesinternal.TypeExpr(f, pkg, typ)
409409
if s == nil {
410410
return nil
411411
}
@@ -416,7 +416,7 @@ func populateValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
416416
case *types.Signature:
417417
var params []*ast.Field
418418
for i := 0; i < u.Params().Len(); i++ {
419-
p := analysisinternal.TypeExpr(f, pkg, u.Params().At(i).Type())
419+
p := typesinternal.TypeExpr(f, pkg, u.Params().At(i).Type())
420420
if p == nil {
421421
return nil
422422
}
@@ -431,7 +431,7 @@ func populateValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
431431
}
432432
var returns []*ast.Field
433433
for i := 0; i < u.Results().Len(); i++ {
434-
r := analysisinternal.TypeExpr(f, pkg, u.Results().At(i).Type())
434+
r := typesinternal.TypeExpr(f, pkg, u.Results().At(i).Type())
435435
if r == nil {
436436
return nil
437437
}

gopls/internal/golang/extract.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"golang.org/x/tools/gopls/internal/util/bug"
2323
"golang.org/x/tools/gopls/internal/util/safetoken"
2424
"golang.org/x/tools/internal/analysisinternal"
25+
"golang.org/x/tools/internal/typesinternal"
2526
)
2627

2728
func extractVariable(fset *token.FileSet, start, end token.Pos, src []byte, file *ast.File, pkg *types.Package, info *types.Info) (*token.FileSet, *analysis.SuggestedFix, error) {
@@ -360,7 +361,7 @@ func extractFunctionMethod(fset *token.FileSet, start, end token.Pos, src []byte
360361
// The blank identifier is always a local variable
361362
continue
362363
}
363-
typ := analysisinternal.TypeExpr(file, pkg, v.obj.Type())
364+
typ := typesinternal.TypeExpr(file, pkg, v.obj.Type())
364365
if typ == nil {
365366
return nil, nil, fmt.Errorf("nil AST expression for type: %v", v.obj.Name())
366367
}
@@ -1233,7 +1234,7 @@ func generateReturnInfo(enclosing *ast.FuncType, pkg *types.Package, path []ast.
12331234
return nil, nil, fmt.Errorf(
12341235
"failed type conversion, AST expression: %T", field.Type)
12351236
}
1236-
expr := analysisinternal.TypeExpr(file, pkg, typ)
1237+
expr := typesinternal.TypeExpr(file, pkg, typ)
12371238
if expr == nil {
12381239
return nil, nil, fmt.Errorf("nil AST expression")
12391240
}
@@ -1253,7 +1254,7 @@ func generateReturnInfo(enclosing *ast.FuncType, pkg *types.Package, path []ast.
12531254
}
12541255
retName, idx := generateNameOutsideOfRange(start, end, path, pkg, info, bestName, nameIdx[bestName])
12551256
nameIdx[bestName] = idx
1256-
z := analysisinternal.ZeroValue(file, pkg, typ)
1257+
z := typesinternal.ZeroExpr(file, pkg, typ)
12571258
if z == nil {
12581259
return nil, nil, fmt.Errorf("can't generate zero value for %T", typ)
12591260
}
@@ -1332,7 +1333,7 @@ func adjustReturnStatements(returnTypes []*ast.Field, seenVars map[types.Object]
13321333
if typ != returnType.Type {
13331334
continue
13341335
}
1335-
val = analysisinternal.ZeroValue(file, pkg, obj.Type())
1336+
val = typesinternal.ZeroExpr(file, pkg, obj.Type())
13361337
break
13371338
}
13381339
if val == nil {

gopls/internal/golang/undeclared.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"golang.org/x/tools/gopls/internal/util/safetoken"
2020
"golang.org/x/tools/gopls/internal/util/typesutil"
2121
"golang.org/x/tools/internal/analysisinternal"
22+
"golang.org/x/tools/internal/typesinternal"
2223
)
2324

2425
// The prefix for this error message changed in Go 1.20.
@@ -221,15 +222,15 @@ func newFunctionDeclaration(path []ast.Node, file *ast.File, pkg *types.Package,
221222
Names: []*ast.Ident{
222223
ast.NewIdent(name),
223224
},
224-
Type: analysisinternal.TypeExpr(file, pkg, paramTypes[i]),
225+
Type: typesinternal.TypeExpr(file, pkg, paramTypes[i]),
225226
})
226227
}
227228

228229
rets := &ast.FieldList{}
229230
retTypes := typesutil.TypesFromContext(info, path[1:], path[1].Pos())
230231
for _, rt := range retTypes {
231232
rets.List = append(rets.List, &ast.Field{
232-
Type: analysisinternal.TypeExpr(file, pkg, rt),
233+
Type: typesinternal.TypeExpr(file, pkg, rt),
233234
})
234235
}
235236

internal/analysisinternal/analysis.go

Lines changed: 0 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"go/types"
1616
"os"
1717
pathpkg "path"
18-
"strconv"
1918

2019
"golang.org/x/tools/go/analysis"
2120
)
@@ -66,192 +65,6 @@ func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos
6665
return end
6766
}
6867

69-
// ZeroValue returns the ast.Expr representation of the "zero" value of the type t.
70-
// See [typesinternal.ZeroString] for a variant that returns a string.
71-
func ZeroValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
72-
// TODO(adonovan): think about generics, and also generic aliases.
73-
under := types.Unalias(typ)
74-
// Don't call Underlying unconditionally: although it removes
75-
// Named and Alias, it also removes TypeParam.
76-
if n, ok := under.(*types.Named); ok {
77-
under = n.Underlying()
78-
}
79-
switch under := under.(type) {
80-
case *types.Basic:
81-
switch {
82-
case under.Info()&types.IsNumeric != 0:
83-
return &ast.BasicLit{Kind: token.INT, Value: "0"}
84-
case under.Info()&types.IsBoolean != 0:
85-
return ast.NewIdent("false")
86-
case under.Info()&types.IsString != 0:
87-
return &ast.BasicLit{Kind: token.STRING, Value: `""`}
88-
case under == types.Typ[types.Invalid]:
89-
return nil
90-
default:
91-
panic(fmt.Sprintf("unknown basic type %v", under))
92-
}
93-
case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice, *types.Array:
94-
return ast.NewIdent("nil")
95-
case *types.Struct:
96-
texpr := TypeExpr(f, pkg, typ) // typ because we want the name here.
97-
if texpr == nil {
98-
return nil
99-
}
100-
return &ast.CompositeLit{
101-
Type: texpr,
102-
}
103-
}
104-
return nil
105-
}
106-
107-
// IsZeroValue checks whether the given expression is a 'zero value' (as determined by output of
108-
// analysisinternal.ZeroValue)
109-
func IsZeroValue(expr ast.Expr) bool {
110-
switch e := expr.(type) {
111-
case *ast.BasicLit:
112-
return e.Value == "0" || e.Value == `""`
113-
case *ast.Ident:
114-
return e.Name == "nil" || e.Name == "false"
115-
default:
116-
return false
117-
}
118-
}
119-
120-
// TypeExpr returns syntax for the specified type. References to
121-
// named types from packages other than pkg are qualified by an appropriate
122-
// package name, as defined by the import environment of file.
123-
func TypeExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
124-
switch t := typ.(type) {
125-
case *types.Basic:
126-
switch t.Kind() {
127-
case types.UnsafePointer:
128-
return &ast.SelectorExpr{X: ast.NewIdent("unsafe"), Sel: ast.NewIdent("Pointer")}
129-
default:
130-
return ast.NewIdent(t.Name())
131-
}
132-
case *types.Pointer:
133-
x := TypeExpr(f, pkg, t.Elem())
134-
if x == nil {
135-
return nil
136-
}
137-
return &ast.UnaryExpr{
138-
Op: token.MUL,
139-
X: x,
140-
}
141-
case *types.Array:
142-
elt := TypeExpr(f, pkg, t.Elem())
143-
if elt == nil {
144-
return nil
145-
}
146-
return &ast.ArrayType{
147-
Len: &ast.BasicLit{
148-
Kind: token.INT,
149-
Value: fmt.Sprintf("%d", t.Len()),
150-
},
151-
Elt: elt,
152-
}
153-
case *types.Slice:
154-
elt := TypeExpr(f, pkg, t.Elem())
155-
if elt == nil {
156-
return nil
157-
}
158-
return &ast.ArrayType{
159-
Elt: elt,
160-
}
161-
case *types.Map:
162-
key := TypeExpr(f, pkg, t.Key())
163-
value := TypeExpr(f, pkg, t.Elem())
164-
if key == nil || value == nil {
165-
return nil
166-
}
167-
return &ast.MapType{
168-
Key: key,
169-
Value: value,
170-
}
171-
case *types.Chan:
172-
dir := ast.ChanDir(t.Dir())
173-
if t.Dir() == types.SendRecv {
174-
dir = ast.SEND | ast.RECV
175-
}
176-
value := TypeExpr(f, pkg, t.Elem())
177-
if value == nil {
178-
return nil
179-
}
180-
return &ast.ChanType{
181-
Dir: dir,
182-
Value: value,
183-
}
184-
case *types.Signature:
185-
var params []*ast.Field
186-
for i := 0; i < t.Params().Len(); i++ {
187-
p := TypeExpr(f, pkg, t.Params().At(i).Type())
188-
if p == nil {
189-
return nil
190-
}
191-
params = append(params, &ast.Field{
192-
Type: p,
193-
Names: []*ast.Ident{
194-
{
195-
Name: t.Params().At(i).Name(),
196-
},
197-
},
198-
})
199-
}
200-
if t.Variadic() {
201-
last := params[len(params)-1]
202-
last.Type = &ast.Ellipsis{Elt: last.Type.(*ast.ArrayType).Elt}
203-
}
204-
var returns []*ast.Field
205-
for i := 0; i < t.Results().Len(); i++ {
206-
r := TypeExpr(f, pkg, t.Results().At(i).Type())
207-
if r == nil {
208-
return nil
209-
}
210-
returns = append(returns, &ast.Field{
211-
Type: r,
212-
})
213-
}
214-
return &ast.FuncType{
215-
Params: &ast.FieldList{
216-
List: params,
217-
},
218-
Results: &ast.FieldList{
219-
List: returns,
220-
},
221-
}
222-
case interface{ Obj() *types.TypeName }: // *types.{Alias,Named,TypeParam}
223-
if t.Obj().Pkg() == nil {
224-
return ast.NewIdent(t.Obj().Name())
225-
}
226-
if t.Obj().Pkg() == pkg {
227-
return ast.NewIdent(t.Obj().Name())
228-
}
229-
pkgName := t.Obj().Pkg().Name()
230-
231-
// If the file already imports the package under another name, use that.
232-
for _, cand := range f.Imports {
233-
if path, _ := strconv.Unquote(cand.Path.Value); path == t.Obj().Pkg().Path() {
234-
if cand.Name != nil && cand.Name.Name != "" {
235-
pkgName = cand.Name.Name
236-
}
237-
}
238-
}
239-
if pkgName == "." {
240-
return ast.NewIdent(t.Obj().Name())
241-
}
242-
return &ast.SelectorExpr{
243-
X: ast.NewIdent(pkgName),
244-
Sel: ast.NewIdent(t.Obj().Name()),
245-
}
246-
case *types.Struct:
247-
return ast.NewIdent(t.String())
248-
case *types.Interface:
249-
return ast.NewIdent(t.String())
250-
default:
251-
return nil
252-
}
253-
}
254-
25568
// StmtToInsertVarBefore returns the ast.Stmt before which we can safely insert a new variable.
25669
// Some examples:
25770
//

0 commit comments

Comments
 (0)