@@ -14,7 +14,6 @@ package fillswitch
14
14
15
15
import (
16
16
"bytes"
17
- "context"
18
17
"errors"
19
18
"fmt"
20
19
"go/ast"
@@ -24,20 +23,14 @@ import (
24
23
"strings"
25
24
26
25
"golang.org/x/tools/go/analysis"
27
- "golang.org/x/tools/go/ast/astutil"
28
26
"golang.org/x/tools/go/ast/inspector"
29
- "golang.org/x/tools/gopls/internal/cache"
30
- "golang.org/x/tools/gopls/internal/cache/parsego"
31
27
)
32
28
33
- const FixCategory = "fillswitch" // recognized by gopls ApplyFix
29
+ const FixCategory = "fillswitch"
34
30
35
31
// Diagnose computes diagnostics for switch statements with missing cases
36
32
// overlapping with the provided start and end position.
37
33
//
38
- // The diagnostic contains a lazy fix; the actual patch is computed
39
- // (via the ApplyFix command) by a call to [SuggestedFix].
40
- //
41
34
// If either start or end is invalid, the entire package is inspected.
42
35
func Diagnose (inspect * inspector.Inspector , start , end token.Pos , pkg * types.Package , info * types.Info ) []analysis.Diagnostic {
43
36
var diags []analysis.Diagnostic
@@ -50,49 +43,35 @@ func Diagnose(inspect *inspector.Inspector, start, end token.Pos, pkg *types.Pac
50
43
return // non-overlapping
51
44
}
52
45
53
- namedType , err := namedTypeFromSwitch (expr , info )
54
- if err != nil {
55
- return
56
- }
57
-
58
- if fix , err := suggestedFixSwitch (expr , pkg , info ); err != nil || fix == nil {
46
+ fix , err := suggestedFixSwitch (expr , pkg , info )
47
+ if err != nil || fix == nil {
59
48
return
60
49
}
61
50
62
51
diags = append (diags , analysis.Diagnostic {
63
- Message : "Switch has missing cases" ,
64
- Pos : expr .Pos (),
65
- End : expr .End (),
66
- Category : FixCategory ,
67
- SuggestedFixes : []analysis.SuggestedFix {{
68
- Message : fmt .Sprintf ("Add cases for %s" , namedType .Obj ().Name ()),
69
- // No TextEdits => computed later by gopls.
70
- }},
52
+ Message : fix .Message ,
53
+ Pos : expr .Pos (),
54
+ End : expr .End (),
55
+ Category : FixCategory ,
56
+ SuggestedFixes : []analysis.SuggestedFix {* fix },
71
57
})
72
58
case * ast.TypeSwitchStmt :
73
59
if start .IsValid () && expr .End () < start ||
74
60
end .IsValid () && expr .Pos () > end {
75
61
return // non-overlapping
76
62
}
77
63
78
- namedType , err := namedTypeFromTypeSwitch (expr , info )
79
- if err != nil {
80
- return
81
- }
82
-
83
- if fix , err := suggestedFixTypeSwitch (expr , pkg , info ); err != nil || fix == nil {
64
+ fix , err := suggestedFixTypeSwitch (expr , pkg , info )
65
+ if err != nil || fix == nil {
84
66
return
85
67
}
86
68
87
69
diags = append (diags , analysis.Diagnostic {
88
- Message : "Switch has missing cases" ,
89
- Pos : expr .Pos (),
90
- End : expr .End (),
91
- Category : FixCategory ,
92
- SuggestedFixes : []analysis.SuggestedFix {{
93
- Message : fmt .Sprintf ("Add cases for %v" , namedType .Obj ().Name ()),
94
- // No TextEdits => computed later by gopls.
95
- }},
70
+ Message : fix .Message ,
71
+ Pos : expr .Pos (),
72
+ End : expr .End (),
73
+ Category : FixCategory ,
74
+ SuggestedFixes : []analysis.SuggestedFix {* fix },
96
75
})
97
76
}
98
77
})
@@ -134,7 +113,7 @@ func suggestedFixTypeSwitch(stmt *ast.TypeSwitchStmt, pkg *types.Package, info *
134
113
}
135
114
}
136
115
137
- handledVariants := typeSwitchCases (stmt .Body , info )
116
+ handledVariants := caseTypes (stmt .Body , info )
138
117
if len (variants ) == 0 || len (variants ) == len (handledVariants ) {
139
118
return nil , nil
140
119
}
@@ -144,7 +123,7 @@ func suggestedFixTypeSwitch(stmt *ast.TypeSwitchStmt, pkg *types.Package, info *
144
123
TextEdits : []analysis.TextEdit {{
145
124
Pos : stmt .End () - 1 ,
146
125
End : stmt .End () - 1 ,
147
- NewText : buildNewTypesText (variants , handledVariants , pkg ),
126
+ NewText : buildTypesText (variants , handledVariants , pkg ),
148
127
}},
149
128
}, nil
150
129
}
@@ -170,7 +149,7 @@ func suggestedFixSwitch(stmt *ast.SwitchStmt, pkg *types.Package, info *types.In
170
149
171
150
samePkg := obj .Pkg () != pkg
172
151
if samePkg && ! obj .Exported () {
173
- continue
152
+ continue // inaccessible
174
153
}
175
154
176
155
if types .Identical (obj .Type (), namedType .Obj ().Type ()) {
@@ -188,7 +167,7 @@ func suggestedFixSwitch(stmt *ast.SwitchStmt, pkg *types.Package, info *types.In
188
167
TextEdits : []analysis.TextEdit {{
189
168
Pos : stmt .End () - 1 ,
190
169
End : stmt .End () - 1 ,
191
- NewText : buildNewConstsText (variants , handledVariants , pkg ),
170
+ NewText : buildConstsText (variants , handledVariants , pkg ),
192
171
}},
193
172
}, nil
194
173
}
@@ -252,7 +231,7 @@ func hasDefaultCase(body *ast.BlockStmt) bool {
252
231
return false
253
232
}
254
233
255
- func buildNewConstsText (variants []* types.Const , handledVariants []* types.Const , currentPkg * types.Package ) []byte {
234
+ func buildConstsText (variants []* types.Const , handledVariants []* types.Const , currentPkg * types.Package ) []byte {
256
235
var textBuilder strings.Builder
257
236
for _ , c := range variants {
258
237
if slices .Contains (handledVariants , c ) {
@@ -287,7 +266,7 @@ func isSameType(c, t types.Type) bool {
287
266
return false
288
267
}
289
268
290
- func buildNewTypesText (variants []types.Type , handledVariants []types.Type , currentPkg * types.Package ) []byte {
269
+ func buildTypesText (variants []types.Type , handledVariants []types.Type , currentPkg * types.Package ) []byte {
291
270
var textBuilder strings.Builder
292
271
for _ , c := range variants {
293
272
if slices .ContainsFunc (handledVariants , func (t types.Type ) bool { return isSameType (c , t ) }) {
@@ -309,6 +288,7 @@ func buildNewTypesText(variants []types.Type, handledVariants []types.Type, curr
309
288
}
310
289
311
290
if e .Obj ().Pkg () != currentPkg {
291
+ // TODO: use the correct package name when the import is renamed
312
292
textBuilder .WriteString ("*" + e .Obj ().Pkg ().Name () + "." + e .Obj ().Name ())
313
293
} else {
314
294
textBuilder .WriteString ("*" + e .Obj ().Name ())
@@ -335,6 +315,7 @@ func caseConsts(body *ast.BlockStmt, info *types.Info) []*types.Const {
335
315
if ! ok {
336
316
continue
337
317
}
318
+
338
319
c , ok := obj .(* types.Const )
339
320
if ! ok {
340
321
continue
@@ -365,7 +346,7 @@ func caseConsts(body *ast.BlockStmt, info *types.Info) []*types.Const {
365
346
return out
366
347
}
367
348
368
- func typeSwitchCases (body * ast.BlockStmt , info * types.Info ) []types.Type {
349
+ func caseTypes (body * ast.BlockStmt , info * types.Info ) []types.Type {
369
350
var out []types.Type
370
351
for _ , stmt := range body .List {
371
352
for _ , e := range stmt .(* ast.CaseClause ).List {
@@ -421,32 +402,3 @@ func typeSwitchCases(body *ast.BlockStmt, info *types.Info) []types.Type {
421
402
422
403
return out
423
404
}
424
-
425
- // SuggestedFix computes the suggested fix for the kinds of
426
- // diagnostics produced by the Analyzer above.
427
- func SuggestedFix (ctx context.Context , snapshot * cache.Snapshot , pkg * cache.Package , pgf * parsego.File , start , end token.Pos ) (* token.FileSet , * analysis.SuggestedFix , error ) {
428
- pos := start // don't use the end
429
- path , _ := astutil .PathEnclosingInterval (pgf .File , pos , pos )
430
- if len (path ) < 2 {
431
- return nil , nil , fmt .Errorf ("no expression found" )
432
- }
433
-
434
- switch stmt := path [0 ].(type ) {
435
- case * ast.SwitchStmt :
436
- fix , err := suggestedFixSwitch (stmt , pkg .GetTypes (), pkg .GetTypesInfo ())
437
- if err != nil {
438
- return nil , nil , err
439
- }
440
-
441
- return pkg .FileSet (), fix , nil
442
- case * ast.TypeSwitchStmt :
443
- fix , err := suggestedFixTypeSwitch (stmt , pkg .GetTypes (), pkg .GetTypesInfo ())
444
- if err != nil {
445
- return nil , nil , err
446
- }
447
-
448
- return pkg .FileSet (), fix , nil
449
- default :
450
- return nil , nil , fmt .Errorf ("no switch statement found" )
451
- }
452
- }
0 commit comments