Skip to content

Commit 1dea13b

Browse files
authored
Merge pull request #156 from yongruilin/conflictmarkers-more-detail
feat(conflictingmarkers): Make error messages more precise
2 parents 5bf7d41 + b09f061 commit 1dea13b

File tree

5 files changed

+119
-9
lines changed

5 files changed

+119
-9
lines changed

pkg/analysis/conflictingmarkers/analyzer.go

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

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

122+
fieldName := field.Names[0].Name
123+
structName := utils.GetStructNameForField(pass, field)
124+
125+
if structName != "" {
126+
fieldName = structName + "." + fieldName
127+
}
128+
122129
message := fmt.Sprintf("field %s has conflicting markers: %s: {%s}. %s",
123-
field.Names[0].Name,
130+
fieldName,
124131
conflictSet.Name,
125132
strings.Join(setDescriptions, ", "),
126133
conflictSet.Description)

pkg/analysis/conflictingmarkers/testdata/src/a/a.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,31 @@ type TestStruct struct {
1717
// Conflict: marker1 vs marker3 (both with values)
1818
// +marker1:=value1
1919
// +marker3:=value2
20-
ConflictWithValuesField string `json:"conflictWithValuesField"` // want "field ConflictWithValuesField has conflicting markers: test_conflict: \\{\\[marker1\\], \\[marker3\\]\\}. Test markers conflict with each other"
20+
ConflictWithValuesField string `json:"conflictWithValuesField"` // want "field TestStruct.ConflictWithValuesField has conflicting markers: test_conflict: \\{\\[marker1\\], \\[marker3\\]\\}. Test markers conflict with each other"
2121

2222
// Conflict: marker1 vs marker3 (mixed with and without values)
2323
// +marker1
2424
// +marker3:=someValue
25-
ConflictMixedField string `json:"conflictMixedField"` // want "field ConflictMixedField has conflicting markers: test_conflict: \\{\\[marker1\\], \\[marker3\\]\\}. Test markers conflict with each other"
25+
ConflictMixedField string `json:"conflictMixedField"` // want "field TestStruct.ConflictMixedField has conflicting markers: test_conflict: \\{\\[marker1\\], \\[marker3\\]\\}. Test markers conflict with each other"
2626

2727
// Multiple conflicts with multiple markers in each set:
2828
// +marker1
2929
// +marker2
3030
// +marker3
3131
// +marker4
32-
MultipleConflictsField string `json:"multipleConflictsField"` // want "field MultipleConflictsField has conflicting markers: test_conflict: \\{\\[marker1 marker2\\], \\[marker3 marker4\\]\\}. Test markers conflict with each other"
32+
MultipleConflictsField string `json:"multipleConflictsField"` // want "field TestStruct.MultipleConflictsField has conflicting markers: test_conflict: \\{\\[marker1 marker2\\], \\[marker3 marker4\\]\\}. Test markers conflict with each other"
3333

3434
// Three-way conflict: marker5 vs marker7 vs marker9
3535
// +marker5
3636
// +marker7
3737
// +marker9
38-
ThreeWayConflictField string `json:"threeWayConflictField"` // want "field ThreeWayConflictField has conflicting markers: three_way_conflict: \\{\\[marker5\\], \\[marker7\\], \\[marker9\\]\\}. Three-way conflict between marker sets"
38+
ThreeWayConflictField string `json:"threeWayConflictField"` // want "field TestStruct.ThreeWayConflictField has conflicting markers: three_way_conflict: \\{\\[marker5\\], \\[marker7\\], \\[marker9\\]\\}. Three-way conflict between marker sets"
3939

4040
// Three-way conflict with values
4141
// +marker6:=value1
4242
// +marker8:=value2
4343
// +marker10:=value3
44-
ThreeWayConflictWithValuesField string `json:"threeWayConflictWithValuesField"` // want "field ThreeWayConflictWithValuesField has conflicting markers: three_way_conflict: \\{\\[marker6\\], \\[marker8\\], \\[marker10\\]\\}. Three-way conflict between marker sets"
44+
ThreeWayConflictWithValuesField string `json:"threeWayConflictWithValuesField"` // want "field TestStruct.ThreeWayConflictWithValuesField has conflicting markers: three_way_conflict: \\{\\[marker6\\], \\[marker8\\], \\[marker10\\]\\}. Three-way conflict between marker sets"
4545

4646
// Valid field with markers from same set in three-way conflict
4747
// +marker5
@@ -55,10 +55,10 @@ type TestStruct struct {
5555
// +marker8
5656
// +marker9
5757
// +marker10
58-
ThreeWayMultipleMarkersField string `json:"threeWayMultipleMarkersField"` // want "field ThreeWayMultipleMarkersField has conflicting markers: three_way_conflict: \\{\\[marker5 marker6\\], \\[marker7 marker8\\], \\[marker10 marker9\\]\\}. Three-way conflict between marker sets"
58+
ThreeWayMultipleMarkersField string `json:"threeWayMultipleMarkersField"` // want "field TestStruct.ThreeWayMultipleMarkersField has conflicting markers: three_way_conflict: \\{\\[marker5 marker6\\], \\[marker7 marker8\\], \\[marker10 marker9\\]\\}. Three-way conflict between marker sets"
5959

6060
// Three-way conflict with only subset of sets triggered (sets 1 and 2 only)
6161
// +marker5
6262
// +marker7
63-
SubsetThreeWayConflictField string `json:"subsetThreeWayConflictField"` // want "field SubsetThreeWayConflictField has conflicting markers: three_way_conflict: \\{\\[marker5\\], \\[marker7\\]\\}. Three-way conflict between marker sets"
63+
SubsetThreeWayConflictField string `json:"subsetThreeWayConflictField"` // want "field TestStruct.SubsetThreeWayConflictField has conflicting markers: three_way_conflict: \\{\\[marker5\\], \\[marker7\\]\\}. Three-way conflict between marker sets"
6464
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package getstructname
2+
3+
type MyStruct1 struct {
4+
Field1 string // want "field Field1 is in struct MyStruct1"
5+
Field2 int // want "field Field2 is in struct MyStruct1"
6+
}
7+
8+
type MyStruct2 struct {
9+
AnotherField bool // want "field AnotherField is in struct MyStruct2"
10+
}

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+
}

pkg/analysis/utils/utils_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ import (
2020

2121
. "github.com/onsi/ginkgo/v2"
2222
. "github.com/onsi/gomega"
23+
"golang.org/x/tools/go/analysis"
24+
"golang.org/x/tools/go/analysis/analysistest"
25+
"golang.org/x/tools/go/analysis/passes/inspect"
26+
"golang.org/x/tools/go/ast/inspector"
2327

2428
"sigs.k8s.io/kube-api-linter/pkg/analysis/utils"
2529
)
@@ -73,3 +77,45 @@ var _ = Describe("FieldName", func() {
7377
}),
7478
)
7579
})
80+
81+
var _ = Describe("GetStructNameForField", func() {
82+
It("should find the correct struct name for a field", func() {
83+
testdata := analysistest.TestData()
84+
analysistest.Run(GinkgoT(), testdata, testGetStructNameAnalyzer(), "getstructname")
85+
})
86+
})
87+
88+
func testGetStructNameAnalyzer() *analysis.Analyzer {
89+
return &analysis.Analyzer{
90+
Name: "testgetstructname",
91+
Doc: "test analyzer for GetStructNameForField",
92+
Requires: []*analysis.Analyzer{inspect.Analyzer},
93+
Run: func(pass *analysis.Pass) (any, error) {
94+
inspect, ok := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
95+
if !ok {
96+
return nil, errCouldNotGetInspector
97+
}
98+
99+
nodeFilter := []ast.Node{
100+
(*ast.Field)(nil),
101+
}
102+
103+
inspect.Preorder(nodeFilter, func(n ast.Node) {
104+
field, ok := n.(*ast.Field)
105+
if !ok {
106+
return
107+
}
108+
if len(field.Names) == 0 {
109+
return
110+
}
111+
112+
structName := utils.GetStructNameForField(pass, field)
113+
if structName != "" {
114+
pass.Reportf(field.Pos(), "field %s is in struct %s", field.Names[0].Name, structName)
115+
}
116+
})
117+
118+
return nil, nil
119+
},
120+
}
121+
}

0 commit comments

Comments
 (0)