@@ -19,6 +19,7 @@ import (
1919 "fmt"
2020 "go/ast"
2121 "go/token"
22+ "slices"
2223
2324 astinspector "golang.org/x/tools/go/ast/inspector"
2425 "sigs.k8s.io/kube-api-linter/pkg/analysis/helpers/extractjsontags"
@@ -30,10 +31,10 @@ import (
3031// Inspector is an interface that allows for the inspection of fields in structs.
3132type Inspector interface {
3233 // InspectFields is a function that iterates over fields in structs.
33- InspectFields (func (field * ast.Field , jsonTagInfo extractjsontags.FieldTagInfo , markersAccess markers.Markers ))
34+ InspectFields (func (field * ast.Field , jsonTagInfo extractjsontags.FieldTagInfo , markersAccess markers.Markers , qualifiedFieldName string ))
3435
3536 // InspectFieldsIncludingListTypes is a function that iterates over fields in structs, including list types.
36- InspectFieldsIncludingListTypes (func (field * ast.Field , jsonTagInfo extractjsontags.FieldTagInfo , markersAccess markers.Markers ))
37+ InspectFieldsIncludingListTypes (func (field * ast.Field , jsonTagInfo extractjsontags.FieldTagInfo , markersAccess markers.Markers , qualifiedFieldName string ))
3738
3839 // InspectTypeSpec is a function that inspects the type spec and calls the provided inspectTypeSpec function.
3940 InspectTypeSpec (func (typeSpec * ast.TypeSpec , markersAccess markers.Markers ))
@@ -58,19 +59,19 @@ func newInspector(astinspector *astinspector.Inspector, jsonTags extractjsontags
5859// InspectFields iterates over fields in structs, ignoring any struct that is not a type declaration, and any field that is ignored and
5960// therefore would not be included in the CRD spec.
6061// For the remaining fields, it calls the provided inspectField function to apply analysis logic.
61- func (i * inspector ) InspectFields (inspectField func (field * ast.Field , jsonTagInfo extractjsontags.FieldTagInfo , markersAccess markers.Markers )) {
62+ func (i * inspector ) InspectFields (inspectField func (field * ast.Field , jsonTagInfo extractjsontags.FieldTagInfo , markersAccess markers.Markers , qualifiedFieldName string )) {
6263 i .inspectFields (inspectField , true )
6364}
6465
6566// InspectFieldsIncludingListTypes iterates over fields in structs, including list types.
6667// Unlike InspectFields, this method does not skip fields in list type structs.
67- func (i * inspector ) InspectFieldsIncludingListTypes (inspectField func (field * ast.Field , jsonTagInfo extractjsontags.FieldTagInfo , markersAccess markers.Markers )) {
68+ func (i * inspector ) InspectFieldsIncludingListTypes (inspectField func (field * ast.Field , jsonTagInfo extractjsontags.FieldTagInfo , markersAccess markers.Markers , qualifiedFieldName string )) {
6869 i .inspectFields (inspectField , false )
6970}
7071
7172// inspectFields is a shared implementation for field iteration.
7273// The skipListTypes parameter controls whether list type structs should be skipped.
73- func (i * inspector ) inspectFields (inspectField func (field * ast.Field , jsonTagInfo extractjsontags.FieldTagInfo , markersAccess markers.Markers ), skipListTypes bool ) {
74+ func (i * inspector ) inspectFields (inspectField func (field * ast.Field , jsonTagInfo extractjsontags.FieldTagInfo , markersAccess markers.Markers , qualifiedFieldName string ), skipListTypes bool ) {
7475 // Filter to fields so that we can iterate over fields in a struct.
7576 nodeFilter := []ast.Node {
7677 (* ast .Field )(nil ),
@@ -90,7 +91,21 @@ func (i *inspector) inspectFields(inspectField func(field *ast.Field, jsonTagInf
9091 return false
9192 }
9293
93- i .processFieldWithRecovery (field , inspectField )
94+ var structName string
95+
96+ qualifiedFieldName := utils .FieldName (field )
97+
98+ // The 0th node in the stack is the *ast.File.
99+ file , ok := stack [0 ].(* ast.File )
100+ if ok {
101+ structName = getStructName (file , field )
102+ }
103+
104+ if structName != "" {
105+ qualifiedFieldName = fmt .Sprintf ("%s.%s" , structName , qualifiedFieldName )
106+ }
107+
108+ i .processFieldWithRecovery (field , qualifiedFieldName , inspectField )
94109
95110 return true
96111 })
@@ -138,7 +153,7 @@ func (i *inspector) shouldSkipField(field *ast.Field) bool {
138153}
139154
140155// processFieldWithRecovery processes a field with panic recovery.
141- func (i * inspector ) processFieldWithRecovery (field * ast.Field , inspectField func (field * ast.Field , jsonTagInfo extractjsontags.FieldTagInfo , markersAccess markers.Markers )) {
156+ func (i * inspector ) processFieldWithRecovery (field * ast.Field , qualifiedFieldName string , inspectField func (field * ast.Field , jsonTagInfo extractjsontags.FieldTagInfo , markersAccess markers.Markers , qualifiedFieldName string )) {
142157 tagInfo := i .jsonTags .FieldTags (field )
143158
144159 defer func () {
@@ -149,7 +164,7 @@ func (i *inspector) processFieldWithRecovery(field *ast.Field, inspectField func
149164 }
150165 }()
151166
152- inspectField (field , tagInfo , i .markers )
167+ inspectField (field , tagInfo , i .markers , qualifiedFieldName )
153168}
154169
155170// InspectTypeSpec inspects the type spec and calls the provided inspectTypeSpec function.
@@ -183,3 +198,45 @@ func printDebugInfo(field *ast.Field) string {
183198
184199 return debug
185200}
201+
202+ func getStructName (file * ast.File , field * ast.Field ) string {
203+ var (
204+ structName string
205+ found bool
206+ )
207+
208+ ast .Inspect (file , func (n ast.Node ) bool {
209+ if found {
210+ return false
211+ }
212+
213+ typeSpec , ok := n .(* ast.TypeSpec )
214+ if ! ok {
215+ return true
216+ }
217+
218+ structType , ok := typeSpec .Type .(* ast.StructType )
219+ if ! ok {
220+ return true
221+ }
222+
223+ structName = typeSpec .Name .Name
224+
225+ if structType .Fields == nil {
226+ return true
227+ }
228+
229+ if slices .Contains (structType .Fields .List , field ) {
230+ found = true
231+ return false
232+ }
233+
234+ return true
235+ })
236+
237+ if found {
238+ return structName
239+ }
240+
241+ return ""
242+ }
0 commit comments