Skip to content

Commit 9127bf1

Browse files
committed
feat(utils): Add GetStruct-NameForField utility
Adds a new utility function, `GetStructNameForField`, to determine the parent struct name for a given field by inspecting the analysis pass. Refactors the `conflictingmarkers` analyzer to use this new utility, simplifying the analyzer and improving the detail in its reports.
1 parent c117432 commit 9127bf1

File tree

2 files changed

+56
-18
lines changed

2 files changed

+56
-18
lines changed

pkg/analysis/conflictingmarkers/analyzer.go

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -67,37 +67,26 @@ func (a *analyzer) run(pass *analysis.Pass) (any, error) {
6767
return nil, kalerrors.ErrCouldNotGetInspector
6868
}
6969

70-
inspect.InspectFields(func(field *ast.Field, stack []ast.Node, _ extractjsontags.FieldTagInfo, markersAccess markers.Markers) {
71-
var structName string
72-
// Find the struct name from the stack
73-
for i := len(stack) - 2; i >= 0; i-- {
74-
if typeSpec, ok := stack[i].(*ast.TypeSpec); ok {
75-
if _, ok := typeSpec.Type.(*ast.StructType); ok {
76-
structName = typeSpec.Name.Name
77-
break
78-
}
79-
}
80-
}
81-
82-
checkField(pass, field, structName, markersAccess, a.conflictSets)
70+
inspect.InspectFields(func(field *ast.Field, _ []ast.Node, _ extractjsontags.FieldTagInfo, markersAccess markers.Markers) {
71+
checkField(pass, field, markersAccess, a.conflictSets)
8372
})
8473

8574
return nil, nil //nolint:nilnil
8675
}
8776

88-
func checkField(pass *analysis.Pass, field *ast.Field, structName string, markersAccess markers.Markers, conflictSets []ConflictSet) {
77+
func checkField(pass *analysis.Pass, field *ast.Field, markersAccess markers.Markers, conflictSets []ConflictSet) {
8978
if field == nil || len(field.Names) == 0 {
9079
return
9180
}
9281

9382
markers := utils.TypeAwareMarkerCollectionForField(pass, markersAccess, field)
9483

9584
for _, conflictSet := range conflictSets {
96-
checkConflict(pass, field, structName, markers, conflictSet)
85+
checkConflict(pass, field, markers, conflictSet)
9786
}
9887
}
9988

100-
func checkConflict(pass *analysis.Pass, field *ast.Field, structName string, markers markers.MarkerSet, conflictSet ConflictSet) {
89+
func checkConflict(pass *analysis.Pass, field *ast.Field, markers markers.MarkerSet, conflictSet ConflictSet) {
10190
// Track which sets have markers present
10291
conflictingMarkers := make([]sets.Set[string], 0)
10392

@@ -117,11 +106,11 @@ func checkConflict(pass *analysis.Pass, field *ast.Field, structName string, mar
117106

118107
// If two or more sets have markers, report the conflict
119108
if len(conflictingMarkers) >= 2 {
120-
reportConflict(pass, field, structName, conflictSet, conflictingMarkers)
109+
reportConflict(pass, field, conflictSet, conflictingMarkers)
121110
}
122111
}
123112

124-
func reportConflict(pass *analysis.Pass, field *ast.Field, structName string, conflictSet ConflictSet, conflictingMarkers []sets.Set[string]) {
113+
func reportConflict(pass *analysis.Pass, field *ast.Field, conflictSet ConflictSet, conflictingMarkers []sets.Set[string]) {
125114
// Build a descriptive message showing which sets conflict
126115
setDescriptions := make([]string, 0, len(conflictingMarkers))
127116

@@ -131,6 +120,8 @@ func reportConflict(pass *analysis.Pass, field *ast.Field, structName string, co
131120
}
132121

133122
fieldName := field.Names[0].Name
123+
structName := utils.GetStructNameForField(pass, field)
124+
134125
if structName != "" {
135126
fieldName = structName + "." + fieldName
136127
}

pkg/analysis/utils/utils.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"go/ast"
2020
"go/token"
2121
"go/types"
22+
"slices"
2223

2324
"golang.org/x/tools/go/analysis"
2425
"sigs.k8s.io/kube-api-linter/pkg/analysis/helpers/markers"
@@ -348,3 +349,49 @@ func isTypeBasic(t types.Type) bool {
348349

349350
return false
350351
}
352+
353+
// GetStructNameForField inspects the AST of the package and returns the name of the struct
354+
// that contains the field being inspected.
355+
func GetStructNameForField(pass *analysis.Pass, field *ast.Field) string {
356+
for _, file := range pass.Files {
357+
var (
358+
structName string
359+
found bool
360+
)
361+
362+
ast.Inspect(file, func(n ast.Node) bool {
363+
if found {
364+
return false
365+
}
366+
367+
typeSpec, ok := n.(*ast.TypeSpec)
368+
if !ok {
369+
return true
370+
}
371+
372+
structType, ok := typeSpec.Type.(*ast.StructType)
373+
if !ok {
374+
return true
375+
}
376+
377+
structName = typeSpec.Name.Name
378+
379+
if structType.Fields == nil {
380+
return true
381+
}
382+
383+
if slices.Contains(structType.Fields.List, field) {
384+
found = true
385+
return false
386+
}
387+
388+
return true
389+
})
390+
391+
if found {
392+
return structName
393+
}
394+
}
395+
396+
return ""
397+
}

0 commit comments

Comments
 (0)