Skip to content

Commit 66e4db4

Browse files
Updated lint failures
1 parent a76ac80 commit 66e4db4

File tree

4 files changed

+53
-12
lines changed

4 files changed

+53
-12
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.24.0
55
require (
66
github.com/golangci/golangci-lint/v2 v2.5.0
77
github.com/golangci/plugin-module-register v0.1.2
8+
github.com/google/go-cmp v0.7.0
89
github.com/onsi/ginkgo/v2 v2.23.4
910
github.com/onsi/gomega v1.38.0
1011
golang.org/x/tools v0.37.0
@@ -100,7 +101,6 @@ require (
100101
github.com/golangci/revgrep v0.8.0 // indirect
101102
github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e // indirect
102103
github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect
103-
github.com/google/go-cmp v0.7.0 // indirect
104104
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect
105105
github.com/gordonklaus/ineffassign v0.2.0 // indirect
106106
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect

pkg/analysis/enums/analyzer.go

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"fmt"
2020
"go/ast"
2121
"go/types"
22+
"slices"
2223
"strings"
2324
"unicode"
2425

@@ -41,6 +42,7 @@ type analyzer struct {
4142

4243
func newAnalyzer(cfg *Config) *analysis.Analyzer {
4344
a := &analyzer{config: cfg}
45+
4446
return &analysis.Analyzer{
4547
Name: name,
4648
Doc: "Enforces that enumerated fields use type aliases with +enum marker and have PascalCase values",
@@ -56,15 +58,16 @@ func (a *analyzer) run(pass *analysis.Pass) (any, error) {
5658
}
5759

5860
// Check struct fields for proper enum usage
59-
inspect.InspectFields(func(field *ast.Field, _ extractjsontags.FieldTagInfo, markersAccess markershelper.Markers) {
61+
inspect.InspectFields(func(field *ast.Field, _ extractjsontags.FieldTagInfo, markersAccess markershelper.Markers, _ string) {
6062
a.checkField(pass, field, markersAccess)
6163
})
6264

6365
// Check type declarations for +enum markers
6466
inspect.InspectTypeSpec(func(typeSpec *ast.TypeSpec, markersAccess markershelper.Markers) {
6567
a.checkTypeSpec(pass, typeSpec, markersAccess)
6668
})
67-
a.checkConstValues(pass, inspect)
69+
a.checkConstValues(pass)
70+
6871
return nil, nil //nolint:nilnil
6972
}
7073

@@ -75,22 +78,29 @@ func (a *analyzer) checkField(pass *analysis.Pass, field *ast.Field, markersAcce
7578
}
7679
// Get the underlying type, unwrapping pointers and arrays
7780
fieldType, isArray := unwrapTypeWithArrayTracking(field.Type)
81+
7882
ident, ok := fieldType.(*ast.Ident)
83+
7984
if !ok {
8085
return
8186
}
87+
8288
prefix := buildFieldPrefix(fieldName, isArray)
89+
8390
if ident.Name == "string" && utils.IsBasicType(pass, ident) {
8491
a.checkPlainStringField(pass, field, markersAccess, prefix)
92+
8593
return
8694
}
95+
8796
a.checkTypeAliasField(pass, field, ident, markersAccess, prefix)
8897
}
8998

9099
func buildFieldPrefix(fieldName string, isArray bool) string {
91100
if isArray {
92101
return fmt.Sprintf("field %s array element", fieldName)
93102
}
103+
94104
return fmt.Sprintf("field %s", fieldName)
95105
}
96106

@@ -106,10 +116,13 @@ func (a *analyzer) checkTypeAliasField(pass *analysis.Pass, field *ast.Field, id
106116
if utils.IsBasicType(pass, ident) {
107117
return
108118
}
119+
109120
typeSpec, ok := utils.LookupTypeSpec(pass, ident)
121+
110122
if !ok || !isStringTypeAlias(pass, typeSpec) {
111123
return
112124
}
125+
113126
if !hasEnumMarker(markersAccess.TypeMarkers(typeSpec)) {
114127
pass.Reportf(field.Pos(),
115128
"%s uses type %s which appears to be an enum but is missing +enum marker (kubebuilder:validation:Enum)",
@@ -121,24 +134,28 @@ func (a *analyzer) checkTypeSpec(pass *analysis.Pass, typeSpec *ast.TypeSpec, ma
121134
if typeSpec.Name == nil {
122135
return
123136
}
137+
124138
typeMarkers := markersAccess.TypeMarkers(typeSpec)
139+
125140
if !hasEnumMarker(typeMarkers) {
126141
return
127142
}
143+
128144
if !isStringTypeAlias(pass, typeSpec) {
129145
pass.Reportf(typeSpec.Pos(),
130146
"type %s has +enum marker but underlying type is not string",
131147
typeSpec.Name.Name)
132148
}
133149
}
134150

135-
func (a *analyzer) checkConstValues(pass *analysis.Pass, inspect inspector.Inspector) {
151+
func (a *analyzer) checkConstValues(pass *analysis.Pass) {
136152
for _, file := range pass.Files {
137153
for _, decl := range file.Decls {
138154
genDecl, ok := decl.(*ast.GenDecl)
139155
if !ok || genDecl.Tok.String() != "const" {
140156
continue
141157
}
158+
142159
for _, spec := range genDecl.Specs {
143160
if valueSpec, ok := spec.(*ast.ValueSpec); ok {
144161
a.checkConstSpec(pass, valueSpec)
@@ -158,16 +175,20 @@ func (a *analyzer) validateEnumConstant(pass *analysis.Pass, name *ast.Ident, va
158175
if name == nil || index >= len(valueSpec.Values) {
159176
return
160177
}
178+
161179
typeSpec := a.getEnumTypeSpec(pass, name)
162180
if typeSpec == nil {
163181
return
164182
}
183+
165184
// Extract and validate the enum value
166185
basicLit, ok := valueSpec.Values[index].(*ast.BasicLit)
167186
if !ok {
168187
return
169188
}
189+
170190
strValue := strings.Trim(basicLit.Value, `"`)
191+
171192
if !a.isInAllowlist(strValue) && !isPascalCase(strValue) {
172193
pass.Reportf(basicLit.Pos(),
173194
"enum value %q should be PascalCase (e.g., \"PhasePending\", \"StateRunning\")",
@@ -180,18 +201,22 @@ func (a *analyzer) getEnumTypeSpec(pass *analysis.Pass, name *ast.Ident) *ast.Ty
180201
if !ok {
181202
return nil
182203
}
204+
183205
namedType, ok := constObj.Type().(*types.Named)
184206
if !ok || namedType.Obj().Pkg() == nil || namedType.Obj().Pkg() != pass.Pkg {
185207
return nil
186208
}
209+
187210
typeSpec := findTypeSpecByName(pass, namedType.Obj().Name())
211+
188212
if typeSpec == nil || !hasEnumMarkerOnTypeSpec(pass, typeSpec) {
189213
return nil
190214
}
215+
191216
return typeSpec
192217
}
193218

194-
// unwrapType removes pointer and array wrappers to get the underlying type
219+
// unwrapType removes pointer and array wrappers to get the underlying type.
195220
func unwrapType(expr ast.Expr) ast.Expr {
196221
switch t := expr.(type) {
197222
case *ast.StarExpr:
@@ -207,6 +232,7 @@ func unwrapType(expr ast.Expr) ast.Expr {
207232
// and tracks whether an array was encountered during unwrapping.
208233
func unwrapTypeWithArrayTracking(expr ast.Expr) (ast.Expr, bool) {
209234
isArray := false
235+
210236
for {
211237
switch t := expr.(type) {
212238
case *ast.StarExpr:
@@ -222,10 +248,13 @@ func unwrapTypeWithArrayTracking(expr ast.Expr) (ast.Expr, bool) {
222248

223249
func isStringTypeAlias(pass *analysis.Pass, typeSpec *ast.TypeSpec) bool {
224250
underlyingType := unwrapType(typeSpec.Type)
251+
225252
ident, ok := underlyingType.(*ast.Ident)
253+
226254
if !ok {
227255
return false
228256
}
257+
229258
return ident.Name == "string" && utils.IsBasicType(pass, ident)
230259
}
231260

@@ -239,6 +268,7 @@ func hasEnumMarkerOnTypeSpec(pass *analysis.Pass, typeSpec *ast.TypeSpec) bool {
239268
return hasEnumMarkerInDoc(genDecl.Doc)
240269
}
241270
}
271+
242272
return false
243273
}
244274

@@ -248,39 +278,39 @@ func findGenDeclForSpec(file *ast.File, typeSpec *ast.TypeSpec) *ast.GenDecl {
248278
if !ok {
249279
continue
250280
}
281+
251282
for _, spec := range genDecl.Specs {
252283
if spec == typeSpec {
253284
return genDecl
254285
}
255286
}
256287
}
288+
257289
return nil
258290
}
259291

260292
func hasEnumMarkerInDoc(doc *ast.CommentGroup) bool {
261293
if doc == nil {
262294
return false
263295
}
296+
264297
for _, comment := range doc.List {
265298
text := comment.Text
266299
if strings.Contains(text, markers.KubebuilderEnumMarker) || strings.Contains(text, markers.K8sEnumMarker) {
267300
return true
268301
}
269302
}
303+
270304
return false
271305
}
272306

273-
// isInAllowlist checks if a value is in the configured allowlist
307+
// isInAllowlist checks if a value is in the configured allowlist.
274308
func (a *analyzer) isInAllowlist(value string) bool {
275309
if a.config == nil {
276310
return false
277311
}
278-
for _, allowed := range a.config.Allowlist {
279-
if value == allowed {
280-
return true
281-
}
282-
}
283-
return false
312+
313+
return slices.Contains(a.config.Allowlist, value)
284314
}
285315

286316
func findTypeSpecByName(pass *analysis.Pass, typeName string) *ast.TypeSpec {
@@ -290,38 +320,47 @@ func findTypeSpecByName(pass *analysis.Pass, typeName string) *ast.TypeSpec {
290320
if !ok {
291321
continue
292322
}
323+
293324
for _, spec := range genDecl.Specs {
294325
typeSpec, ok := spec.(*ast.TypeSpec)
295326
if !ok {
296327
continue
297328
}
329+
298330
if typeSpec.Name != nil && typeSpec.Name.Name == typeName {
299331
return typeSpec
300332
}
301333
}
302334
}
303335
}
336+
304337
return nil
305338
}
306339

307340
func isPascalCase(s string) bool {
308341
if len(s) == 0 {
309342
return false
310343
}
344+
311345
if !unicode.IsUpper(rune(s[0])) {
312346
return false
313347
}
348+
314349
hasLower := false
350+
315351
for _, r := range s {
316352
if r == '_' || r == '-' {
317353
return false
318354
}
355+
319356
if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
320357
return false
321358
}
359+
322360
if unicode.IsLower(r) {
323361
hasLower = true
324362
}
325363
}
364+
326365
return len(s) == 1 || hasLower
327366
}

pkg/analysis/enums/analyzer_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func TestAnalyzer(t *testing.T) {
2828
// Test without allowlist
2929
config := &enums.Config{}
3030
analyzer, err := enums.Initializer().Init(config)
31+
3132
if err != nil {
3233
t.Fatalf("initializing enums linter: %v", err)
3334
}

pkg/analysis/enums/initializer.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func initAnalyzer(cfg *Config) (*analysis.Analyzer, error) {
4343
if cfg == nil {
4444
cfg = &Config{}
4545
}
46+
4647
return newAnalyzer(cfg), nil
4748
}
4849

0 commit comments

Comments
 (0)