Skip to content

Commit 5b2b076

Browse files
committed
Print qualified field name in error messages
1 parent 7dea50a commit 5b2b076

File tree

20 files changed

+96
-57
lines changed

20 files changed

+96
-57
lines changed

pkg/analysis/arrayofstruct/analyzer.go

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,14 @@ func run(pass *analysis.Pass) (any, error) {
4444
return nil, kalerrors.ErrCouldNotGetInspector
4545
}
4646

47-
inspect.InspectFields(func(field *ast.Field, jsonTagInfo extractjsontags.FieldTagInfo, markersAccess markershelper.Markers) {
48-
checkField(pass, field, markersAccess)
47+
inspect.InspectFields(func(field *ast.Field, jsonTagInfo extractjsontags.FieldTagInfo, markersAccess markershelper.Markers, qualifiedFieldName string) {
48+
checkField(pass, field, markersAccess, qualifiedFieldName)
4949
})
5050

5151
return nil, nil //nolint:nilnil
5252
}
5353

54-
func checkField(pass *analysis.Pass, field *ast.Field, markersAccess markershelper.Markers) {
54+
func checkField(pass *analysis.Pass, field *ast.Field, markersAccess markershelper.Markers, qualifiedFieldName string) {
5555
// Get the element type of the array
5656
elementType := getArrayElementType(pass, field)
5757
if elementType == nil {
@@ -79,7 +79,7 @@ func checkField(pass *analysis.Pass, field *ast.Field, markersAccess markershelp
7979
return
8080
}
8181

82-
reportArrayOfStructIssue(pass, field)
82+
reportArrayOfStructIssue(pass, field, qualifiedFieldName)
8383
}
8484

8585
// getArrayElementType extracts the element type from an array field.
@@ -107,19 +107,8 @@ func getArrayElementType(pass *analysis.Pass, field *ast.Field) ast.Expr {
107107
}
108108

109109
// reportArrayOfStructIssue reports a diagnostic for an array of structs without required fields.
110-
func reportArrayOfStructIssue(pass *analysis.Pass, field *ast.Field) {
111-
fieldName := utils.FieldName(field)
112-
structName := utils.GetStructNameForField(pass, field)
113-
114-
var prefix string
115-
if structName != "" {
116-
prefix = fmt.Sprintf("%s.%s", structName, fieldName)
117-
} else {
118-
prefix = fieldName
119-
}
120-
121-
message := fmt.Sprintf("%s is an array of structs, but the struct has no required fields. At least one field should be marked as required to prevent ambiguous YAML configurations", prefix)
122-
110+
func reportArrayOfStructIssue(pass *analysis.Pass, field *ast.Field, qualifiedFieldName string) {
111+
message := fmt.Sprintf("%s is an array of structs, but the struct has no required fields. At least one field should be marked as required to prevent ambiguous YAML configurations", qualifiedFieldName)
123112
pass.Report(analysis.Diagnostic{
124113
Pos: field.Pos(),
125114
Message: message,

pkg/analysis/commentstart/analyzer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func run(pass *analysis.Pass) (any, error) {
4747
return nil, kalerrors.ErrCouldNotGetInspector
4848
}
4949

50-
inspect.InspectFields(func(field *ast.Field, jsonTagInfo extractjsontags.FieldTagInfo, _ markers.Markers) {
50+
inspect.InspectFields(func(field *ast.Field, jsonTagInfo extractjsontags.FieldTagInfo, _ markers.Markers, _ string) {
5151
checkField(pass, field, jsonTagInfo)
5252
})
5353

pkg/analysis/conflictingmarkers/analyzer.go

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -67,26 +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, _ extractjsontags.FieldTagInfo, markersAccess markers.Markers) {
71-
checkField(pass, field, markersAccess, a.conflictSets)
70+
inspect.InspectFields(func(field *ast.Field, _ extractjsontags.FieldTagInfo, markersAccess markers.Markers, qualifiedFieldName string) {
71+
checkField(pass, field, markersAccess, a.conflictSets, qualifiedFieldName)
7272
})
7373

7474
return nil, nil //nolint:nilnil
7575
}
7676

77-
func checkField(pass *analysis.Pass, field *ast.Field, markersAccess markers.Markers, conflictSets []ConflictSet) {
77+
func checkField(pass *analysis.Pass, field *ast.Field, markersAccess markers.Markers, conflictSets []ConflictSet, qualifiedFieldName string) {
7878
if field == nil || len(field.Names) == 0 {
7979
return
8080
}
8181

8282
markers := utils.TypeAwareMarkerCollectionForField(pass, markersAccess, field)
8383

8484
for _, conflictSet := range conflictSets {
85-
checkConflict(pass, field, markers, conflictSet)
85+
checkConflict(pass, field, markers, conflictSet, qualifiedFieldName)
8686
}
8787
}
8888

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

@@ -106,11 +106,11 @@ func checkConflict(pass *analysis.Pass, field *ast.Field, markers markers.Marker
106106

107107
// If two or more sets have markers, report the conflict
108108
if len(conflictingMarkers) >= 2 {
109-
reportConflict(pass, field, conflictSet, conflictingMarkers)
109+
reportConflict(pass, field, conflictSet, conflictingMarkers, qualifiedFieldName)
110110
}
111111
}
112112

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

@@ -119,15 +119,8 @@ func reportConflict(pass *analysis.Pass, field *ast.Field, conflictSet ConflictS
119119
setDescriptions = append(setDescriptions, fmt.Sprintf("%v", markersList))
120120
}
121121

122-
fieldName := utils.FieldName(field)
123-
structName := utils.GetStructNameForField(pass, field)
124-
125-
if structName != "" {
126-
fieldName = structName + "." + fieldName
127-
}
128-
129122
message := fmt.Sprintf("field %s has conflicting markers: %s: {%s}. %s",
130-
fieldName,
123+
qualifiedFieldName,
131124
conflictSet.Name,
132125
strings.Join(setDescriptions, ", "),
133126
conflictSet.Description)

pkg/analysis/duplicatemarkers/analyzer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func run(pass *analysis.Pass) (any, error) {
4848
return nil, kalerrors.ErrCouldNotGetInspector
4949
}
5050

51-
inspect.InspectFields(func(field *ast.Field, _ extractjsontags.FieldTagInfo, markersAccess markers.Markers) {
51+
inspect.InspectFields(func(field *ast.Field, _ extractjsontags.FieldTagInfo, markersAccess markers.Markers, _ string) {
5252
checkField(pass, field, markersAccess)
5353
})
5454

pkg/analysis/forbiddenmarkers/analyzer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func (a *analyzer) run(pass *analysis.Pass) (any, error) {
6161
return nil, kalerrors.ErrCouldNotGetInspector
6262
}
6363

64-
inspect.InspectFields(func(field *ast.Field, _ extractjsontags.FieldTagInfo, markersAccess markers.Markers) {
64+
inspect.InspectFields(func(field *ast.Field, _ extractjsontags.FieldTagInfo, markersAccess markers.Markers, _ string) {
6565
checkField(pass, field, markersAccess, a.forbiddenMarkers)
6666
})
6767

pkg/analysis/helpers/inspector/analyzer_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func run(pass *analysis.Pass) (any, error) {
4949
return nil, errCouldNotGetInspector
5050
}
5151

52-
inspect.InspectFields(func(field *ast.Field, jsonTagInfo extractjsontags.FieldTagInfo, _ markers.Markers) {
52+
inspect.InspectFields(func(field *ast.Field, jsonTagInfo extractjsontags.FieldTagInfo, _ markers.Markers, _ string) {
5353
pass.Reportf(field.Pos(), "field: %v", utils.FieldName(field))
5454

5555
if jsonTagInfo.Name != "" {

pkg/analysis/helpers/inspector/inspector.go

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
3132
type 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+
}

pkg/analysis/jsontags/analyzer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func (a *analyzer) run(pass *analysis.Pass) (any, error) {
7171
return nil, kalerrors.ErrCouldNotGetInspector
7272
}
7373

74-
inspect.InspectFieldsIncludingListTypes(func(field *ast.Field, jsonTagInfo extractjsontags.FieldTagInfo, _ markers.Markers) {
74+
inspect.InspectFieldsIncludingListTypes(func(field *ast.Field, jsonTagInfo extractjsontags.FieldTagInfo, _ markers.Markers, _ string) {
7575
a.checkField(pass, field, jsonTagInfo)
7676
})
7777

pkg/analysis/maxlength/analyzer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func run(pass *analysis.Pass) (any, error) {
4747
return nil, kalerrors.ErrCouldNotGetInspector
4848
}
4949

50-
inspect.InspectFields(func(field *ast.Field, _ extractjsontags.FieldTagInfo, markersAccess markershelper.Markers) {
50+
inspect.InspectFields(func(field *ast.Field, _ extractjsontags.FieldTagInfo, markersAccess markershelper.Markers, _ string) {
5151
checkField(pass, field, markersAccess)
5252
})
5353

pkg/analysis/minlength/analyzer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func run(pass *analysis.Pass) (any, error) {
4747
return nil, kalerrors.ErrCouldNotGetInspector
4848
}
4949

50-
inspect.InspectFields(func(field *ast.Field, _ extractjsontags.FieldTagInfo, markersAccess markershelper.Markers) {
50+
inspect.InspectFields(func(field *ast.Field, _ extractjsontags.FieldTagInfo, markersAccess markershelper.Markers, _ string) {
5151
checkField(pass, field, markersAccess)
5252
})
5353

0 commit comments

Comments
 (0)