Skip to content

Commit 3fcf757

Browse files
committed
address review comments
1 parent 2975833 commit 3fcf757

File tree

2 files changed

+59
-69
lines changed

2 files changed

+59
-69
lines changed

gopls/internal/analysis/fillswitch/fillswitch.go

Lines changed: 58 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
// Copyright 2020 The Go Authors. All rights reserved.
1+
// Copyright 2024 The Go Authors. All rights reserved.
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// Package fillswitch defines an Analyzer that automatically
6-
// fills the missing cases in type switches or switches over named types.
5+
// Package fillswitch provides diagnostics and fixes to fills the missing cases
6+
// in type switches or switches over named types.
77
//
88
// The analyzer's diagnostic is merely a prompt.
99
// The actual fix is created by a separate direct call from gopls to
@@ -32,12 +32,6 @@ import (
3232

3333
const FixCategory = "fillswitch" // recognized by gopls ApplyFix
3434

35-
// errNoSuggestedFix is returned when no suggested fix is available. This could
36-
// be because all cases are already covered, or (in the case of a type switch)
37-
// because the remaining cases are for types not accessible by the current
38-
// package.
39-
var errNoSuggestedFix = errors.New("no suggested fix")
40-
4135
// Diagnose computes diagnostics for switch statements with missing cases
4236
// overlapping with the provided start and end position.
4337
//
@@ -49,8 +43,10 @@ func Diagnose(inspect *inspector.Inspector, start, end token.Pos, pkg *types.Pac
4943
var diags []analysis.Diagnostic
5044
nodeFilter := []ast.Node{(*ast.SwitchStmt)(nil), (*ast.TypeSwitchStmt)(nil)}
5145
inspect.Preorder(nodeFilter, func(n ast.Node) {
52-
if expr, ok := n.(*ast.SwitchStmt); ok {
53-
if (start.IsValid() && expr.End() < start) || (end.IsValid() && expr.Pos() > end) {
46+
switch expr := n.(type) {
47+
case *ast.SwitchStmt:
48+
if start.IsValid() && expr.End() < start ||
49+
end.IsValid() && expr.Pos() > end {
5450
return // non-overlapping
5551
}
5652

@@ -63,7 +59,7 @@ func Diagnose(inspect *inspector.Inspector, start, end token.Pos, pkg *types.Pac
6359
return
6460
}
6561

66-
if _, err := suggestedFixSwitch(expr, pkg, info); err != nil {
62+
if fix, err := suggestedFixSwitch(expr, pkg, info); err != nil || fix == nil {
6763
return
6864
}
6965

@@ -77,10 +73,9 @@ func Diagnose(inspect *inspector.Inspector, start, end token.Pos, pkg *types.Pac
7773
// No TextEdits => computed later by gopls.
7874
}},
7975
})
80-
}
81-
82-
if expr, ok := n.(*ast.TypeSwitchStmt); ok {
83-
if (start.IsValid() && expr.End() < start) || (end.IsValid() && expr.Pos() > end) {
76+
case *ast.TypeSwitchStmt:
77+
if start.IsValid() && expr.End() < start ||
78+
end.IsValid() && expr.Pos() > end {
8479
return // non-overlapping
8580
}
8681

@@ -93,7 +88,7 @@ func Diagnose(inspect *inspector.Inspector, start, end token.Pos, pkg *types.Pac
9388
return
9489
}
9590

96-
if _, err := suggestedFixTypeSwitch(expr, pkg, info); err != nil {
91+
if fix, err := suggestedFixTypeSwitch(expr, pkg, info); err != nil || fix == nil {
9792
return
9893
}
9994

@@ -120,43 +115,40 @@ func suggestedFixTypeSwitch(stmt *ast.TypeSwitchStmt, pkg *types.Package, info *
120115
}
121116

122117
scope := namedType.Obj().Pkg().Scope()
123-
variants := make([]string, 0)
118+
var variants []string
124119
for _, name := range scope.Names() {
125120
obj := scope.Lookup(name)
126121
if _, ok := obj.(*types.TypeName); !ok {
127-
continue
122+
continue // not a type
128123
}
129124

130125
if types.Identical(obj.Type(), namedType.Obj().Type()) {
131126
continue
132127
}
133128

134-
if types.AssignableTo(obj.Type(), namedType.Obj().Type()) {
135-
if obj.Pkg().Name() != pkg.Name() {
136-
if !obj.Exported() {
137-
continue
138-
}
129+
if types.IsInterface(obj.Type()) {
130+
continue
131+
}
139132

140-
variants = append(variants, obj.Pkg().Name()+"."+obj.Name())
141-
} else {
142-
variants = append(variants, obj.Name())
133+
name := obj.Name()
134+
samePkg := obj.Pkg() == pkg
135+
if !samePkg {
136+
if !obj.Exported() {
137+
continue // inaccessible
143138
}
144-
} else if types.AssignableTo(types.NewPointer(obj.Type()), namedType.Obj().Type()) {
145-
if obj.Pkg().Name() != pkg.Name() {
146-
if !obj.Exported() {
147-
continue
148-
}
139+
name = obj.Pkg().Name() + name
140+
}
149141

150-
variants = append(variants, "*"+obj.Pkg().Name()+"."+obj.Name())
151-
} else {
152-
variants = append(variants, "*"+obj.Name())
153-
}
142+
if types.AssignableTo(obj.Type(), namedType.Obj().Type()) {
143+
variants = append(variants, name)
144+
} else if types.AssignableTo(types.NewPointer(obj.Type()), namedType.Obj().Type()) {
145+
variants = append(variants, "*"+name)
154146
}
155147
}
156148

157-
handledVariants := getHandledVariants(stmt.Body)
149+
handledVariants := caseTypes(stmt.Body, info)
158150
if len(variants) == 0 || len(variants) == len(handledVariants) {
159-
return nil, errNoSuggestedFix
151+
return nil, nil
160152
}
161153

162154
newText := buildNewText(variants, handledVariants)
@@ -165,7 +157,7 @@ func suggestedFixTypeSwitch(stmt *ast.TypeSwitchStmt, pkg *types.Package, info *
165157
TextEdits: []analysis.TextEdit{{
166158
Pos: stmt.End() - 1,
167159
End: stmt.End() - 1,
168-
NewText: indent([]byte(newText), []byte{'\t'}),
160+
NewText: bytes.ReplaceAll([]byte(newText), []byte("\n"), []byte("\n\t")),
169161
}},
170162
}, nil
171163
}
@@ -198,9 +190,9 @@ func suggestedFixSwitch(stmt *ast.SwitchStmt, pkg *types.Package, info *types.In
198190
}
199191
}
200192

201-
handledVariants := getHandledVariants(stmt.Body)
193+
handledVariants := caseTypes(stmt.Body, info)
202194
if len(variants) == 0 || len(variants) == len(handledVariants) {
203-
return nil, errNoSuggestedFix
195+
return nil, nil
204196
}
205197

206198
newText := buildNewText(variants, handledVariants)
@@ -209,7 +201,7 @@ func suggestedFixSwitch(stmt *ast.SwitchStmt, pkg *types.Package, info *types.In
209201
TextEdits: []analysis.TextEdit{{
210202
Pos: stmt.End() - 1,
211203
End: stmt.End() - 1,
212-
NewText: indent([]byte(newText), []byte{'\t'}),
204+
NewText: bytes.ReplaceAll([]byte(newText), []byte("\n"), []byte("\n\t")),
213205
}},
214206
}, nil
215207
}
@@ -288,20 +280,36 @@ func buildNewText(variants []string, handledVariants []string) string {
288280
return textBuilder.String()
289281
}
290282

291-
func getHandledVariants(body *ast.BlockStmt) []string {
292-
out := make([]string, 0)
293-
for _, bl := range body.List {
294-
for _, c := range bl.(*ast.CaseClause).List {
295-
switch v := c.(type) {
283+
func caseTypes(body *ast.BlockStmt, info *types.Info) []string {
284+
var out []string
285+
for _, stmt := range body.List {
286+
for _, e := range stmt.(*ast.CaseClause).List {
287+
switch e := e.(type) {
296288
case *ast.Ident:
297-
out = append(out, v.Name)
289+
out = append(out, e.Name)
298290
case *ast.SelectorExpr:
299-
out = append(out, v.X.(*ast.Ident).Name+"."+v.Sel.Name)
291+
if _, ok := e.X.(*ast.Ident); !ok {
292+
continue
293+
}
294+
295+
out = append(out, e.X.(*ast.Ident).Name+"."+e.Sel.Name)
300296
case *ast.StarExpr:
301-
switch v := v.X.(type) {
297+
switch v := e.X.(type) {
302298
case *ast.Ident:
299+
if !info.Types[v].IsType() {
300+
continue
301+
}
302+
303303
out = append(out, "*"+v.Name)
304304
case *ast.SelectorExpr:
305+
if !info.Types[v].IsType() {
306+
continue
307+
}
308+
309+
if _, ok := e.X.(*ast.Ident); !ok {
310+
continue
311+
}
312+
305313
out = append(out, "*"+v.X.(*ast.Ident).Name+"."+v.Sel.Name)
306314
}
307315
}
@@ -339,21 +347,3 @@ func SuggestedFix(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.Pack
339347
return nil, nil, fmt.Errorf("no switch statement found")
340348
}
341349
}
342-
343-
// indent works line by line through str, prefixing each line with
344-
// prefix.
345-
func indent(str, prefix []byte) []byte {
346-
split := bytes.Split(str, []byte("\n"))
347-
newText := bytes.NewBuffer(nil)
348-
for i, s := range split {
349-
if i != 0 {
350-
newText.Write(prefix)
351-
}
352-
353-
newText.Write(s)
354-
if i < len(split)-1 {
355-
newText.WriteByte('\n')
356-
}
357-
}
358-
return newText.Bytes()
359-
}

gopls/internal/analysis/fillswitch/fillswitch_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
)
1717

1818
// analyzer allows us to test the fillswitch code action using the analysistest
19-
// harness. (fillswitch used to be a gopls analyzer.)
19+
// harness.
2020
var analyzer = &analysis.Analyzer{
2121
Name: "fillswitch",
2222
Doc: "test only",

0 commit comments

Comments
 (0)