Skip to content

Commit 8191661

Browse files
committed
Address review comments
1 parent d5f8bf2 commit 8191661

File tree

4 files changed

+41
-21
lines changed

4 files changed

+41
-21
lines changed

pkg/analysis/helpers/extractjsontags/analyzer.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525
"golang.org/x/tools/go/analysis"
2626
"golang.org/x/tools/go/analysis/passes/inspect"
2727
"golang.org/x/tools/go/ast/inspector"
28-
28+
"k8s.io/utils/set"
2929
kalerrors "sigs.k8s.io/kube-api-linter/pkg/analysis/errors"
3030
)
3131

@@ -142,15 +142,15 @@ func extractTagInfo(tag *ast.BasicLit) FieldTagInfo {
142142
}
143143

144144
tagName := tagValues[0]
145+
tagSet := set.New[string](tagValues...)
145146

146147
return FieldTagInfo{
147148
Name: tagName,
148-
OmitEmpty: (len(tagValues) == 2 && tagValues[1] == omitEmpty) || (len(tagValues) == 3 && (tagValues[1] == omitEmpty || tagValues[2] == omitEmpty)),
149-
// Considering, omitzero will always coexist with omitemtpy and it can be either second or third tag.
150-
OmitZero: len(tagValues) == 3 && (tagValues[1] == omitZero || tagValues[2] == omitZero),
151-
RawValue: tagValue,
152-
Pos: pos,
153-
End: end,
149+
OmitEmpty: tagSet.Has(omitEmpty),
150+
OmitZero: tagSet.Has(omitZero),
151+
RawValue: tagValue,
152+
Pos: pos,
153+
End: end,
154154
}
155155
}
156156

pkg/analysis/optionalfields/analyzer.go

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func (a *analyzer) checkFieldProperties(pass *analysis.Pass, field *ast.Field, f
141141
if a.pointerPreference == config.OptionalFieldsPointerPreferenceAlways {
142142
// The field must always be a pointer, pointers require omitempty, so enforce that too.
143143
a.handleFieldShouldBePointer(pass, field, fieldName, isPointer, underlying)
144-
a.handleFieldShouldHaveOmitEmpty(pass, field, fieldName, hasOmitEmpty, jsonTags)
144+
a.handleFieldShouldHaveOmitEmpty(pass, field, fieldName, hasOmitEmpty, hasOmitZero, hasValidZeroValue, isStruct, jsonTags)
145145

146146
return
147147
}
@@ -153,17 +153,17 @@ func (a *analyzer) checkFieldProperties(pass *analysis.Pass, field *ast.Field, f
153153
a.checkFieldPropertiesWithOmitEmptyRequired(pass, field, fieldName, jsonTags, underlying, hasOmitEmpty, hasOmitZero, hasValidZeroValue, completeValidation, isPointer, isStruct)
154154
} else {
155155
// The field does not have omitempty, and does not require it.
156-
a.checkFieldPropertiesWithoutOmitEmpty(pass, field, fieldName, jsonTags, underlying, hasValidZeroValue, completeValidation, isPointer, isStruct)
156+
a.checkFieldPropertiesWithoutOmitEmpty(pass, field, fieldName, jsonTags, underlying, hasValidZeroValue, completeValidation, isPointer, isStruct, hasOmitZero)
157157
}
158158
}
159159

160160
func (a *analyzer) checkFieldPropertiesWithOmitEmptyRequired(pass *analysis.Pass, field *ast.Field, fieldName string, jsonTags extractjsontags.FieldTagInfo, underlying ast.Expr, hasOmitEmpty, hasOmitZero, hasValidZeroValue, completeValidation, isPointer, isStruct bool) {
161161
// In this case, we should always add the omitempty if it isn't present.
162-
a.handleFieldShouldHaveOmitEmpty(pass, field, fieldName, hasOmitEmpty, jsonTags)
162+
a.handleFieldShouldHaveOmitEmpty(pass, field, fieldName, hasOmitEmpty, hasOmitZero, hasValidZeroValue, isStruct, jsonTags)
163163

164164
switch {
165165
case !hasValidZeroValue && isStruct && hasOmitZero:
166-
// The struct field need not be pointer if it does not have valid zero value and has omitzero tag.
166+
// The struct field need not be pointer if it does not have a valid zero value and has omitzero tag.
167167
return
168168
case hasValidZeroValue && !completeValidation:
169169
a.handleIncompleteFieldValidation(pass, field, fieldName, isPointer, underlying)
@@ -178,16 +178,21 @@ func (a *analyzer) checkFieldPropertiesWithOmitEmptyRequired(pass *analysis.Pass
178178
}
179179
}
180180

181-
func (a *analyzer) checkFieldPropertiesWithoutOmitEmpty(pass *analysis.Pass, field *ast.Field, fieldName string, jsonTags extractjsontags.FieldTagInfo, underlying ast.Expr, hasValidZeroValue, completeValidation, isPointer, isStruct bool) {
181+
func (a *analyzer) checkFieldPropertiesWithoutOmitEmpty(pass *analysis.Pass, field *ast.Field, fieldName string, jsonTags extractjsontags.FieldTagInfo, underlying ast.Expr, hasValidZeroValue, completeValidation, isPointer, isStruct, hasOmitZero bool) {
182182
switch {
183183
case hasValidZeroValue:
184184
// The field is not omitempty, and the zero value is valid, the field does not need to be a pointer.
185185
a.handleFieldShouldNotBePointer(pass, field, fieldName, isPointer, "field %s is optional, without omitempty and allows the zero value. The field does not need to be a pointer.")
186186
case !hasValidZeroValue:
187-
// The zero value would not be accepted, so the field needs to have omitempty.
188-
// Force the omitempty policy to suggest a fix. We can only get to this function when the policy is configured to Ignore.
189-
// Since we absolutely have to add the omitempty tag, we can report it as a suggestion.
190-
reportShouldAddOmitEmpty(pass, field, config.OptionalFieldsOmitEmptyPolicySuggestFix, fieldName, "field %s is optional and does not allow the zero value. It must have the omitempty tag.", jsonTags)
187+
if isStruct && hasOmitZero {
188+
// The struct field need not have omitempty tag if it does not have a valid zero value and has omitzero tag.
189+
return
190+
} else {
191+
// The zero value would not be accepted, so the field needs to have omitempty.
192+
// Force the omitempty policy to suggest a fix. We can only get to this function when the policy is configured to Ignore.
193+
// Since we absolutely have to add the omitempty tag, we can report it as a suggestion.
194+
reportShouldAddOmitEmpty(pass, field, config.OptionalFieldsOmitEmptyPolicySuggestFix, fieldName, "field %s is optional and does not allow the zero value. It must have the omitempty tag.", jsonTags)
195+
}
191196
// Once it has the omitempty tag, it will also need to be a pointer in some cases.
192197
// Now handle it as if it had the omitempty already.
193198
// We already handle the omitempty tag above, so force the `hasOmitEmpty` to true.
@@ -229,11 +234,16 @@ func (a *analyzer) handleFieldShouldNotBePointer(pass *analysis.Pass, field *ast
229234
reportShouldRemovePointer(pass, field, a.pointerPolicy, fieldName, message)
230235
}
231236

232-
func (a *analyzer) handleFieldShouldHaveOmitEmpty(pass *analysis.Pass, field *ast.Field, fieldName string, hasOmitEmpty bool, jsonTags extractjsontags.FieldTagInfo) {
237+
func (a *analyzer) handleFieldShouldHaveOmitEmpty(pass *analysis.Pass, field *ast.Field, fieldName string, hasOmitEmpty, hasOmitZero, hasValidZeroValue, isStruct bool, jsonTags extractjsontags.FieldTagInfo) {
233238
if hasOmitEmpty {
234239
return
235240
}
236241

242+
if !hasValidZeroValue && isStruct && hasOmitZero {
243+
// The struct field need not have omitempty tag if it does not have a valid zero value and has omitzero tag.
244+
return
245+
}
246+
237247
reportShouldAddOmitEmpty(pass, field, a.omitEmptyPolicy, fieldName, "field %s is optional and should have the omitempty tag", jsonTags)
238248
}
239249

pkg/analysis/optionalfields/testdata/src/b/a.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,15 @@ type A struct {
171171
// +optional
172172
StructWithMinProperties B `json:"structWithMinProperties,omitempty"` // want "field StructWithMinProperties is optional and should be a pointer"
173173

174-
// StructWithMinPropertiesAndOmitEmptyTag is a struct field with a minimum number of properties.
174+
// structWithMinPropertiesAndOmitEmptyAndOmitZero is a struct field with a minimum number of properties and has both omitzero and omitemtpy tag.
175175
// +kubebuilder:validation:MinProperties=1
176176
// +optional
177-
StructWithMinPropertiesAndOmitEmptyTag B `json:"structWithMinPropertiesAndOmitEmptyTag,omitempty,omitzero"`
177+
StructWithMinPropertiesAndOmitEmptyAndOmitZero B `json:"structWithMinPropertiesAndOmitEmptyAndOmitZero,omitempty,omitzero"`
178+
179+
// structWithOnlyOmitZeroTag is a struct field with a minimum number of properties and has only omitzero tag.
180+
// +kubebuilder:validation:MinProperties=1
181+
// +optional
182+
StructWithOnlyOmitZeroTag B `json:"structWithOnlyOmitZeroTag,omitzero"`
178183

179184
// structWithMinPropertiesOnStruct is a struct field with a minimum number of properties on the struct.
180185
// +optional

pkg/analysis/optionalfields/testdata/src/b/a.go.golden

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,15 @@ type A struct {
171171
// +optional
172172
StructWithMinProperties *B `json:"structWithMinProperties,omitempty"` // want "field StructWithMinProperties is optional and should be a pointer"
173173

174-
// StructWithMinPropertiesAndOmitEmptyTag is a struct field with a minimum number of properties.
174+
// structWithMinPropertiesAndOmitEmptyAndOmitZero is a struct field with a minimum number of properties and has both omitzero and omitemtpy tag.
175175
// +kubebuilder:validation:MinProperties=1
176176
// +optional
177-
StructWithMinPropertiesAndOmitEmptyTag B `json:"structWithMinPropertiesAndOmitEmptyTag,omitempty,omitzero"`
177+
StructWithMinPropertiesAndOmitEmptyAndOmitZero B `json:"structWithMinPropertiesAndOmitEmptyAndOmitZero,omitempty,omitzero"`
178+
179+
// structWithOnlyOmitZeroTag is a struct field with a minimum number of properties and has only omitzero tag.
180+
// +kubebuilder:validation:MinProperties=1
181+
// +optional
182+
StructWithOnlyOmitZeroTag B `json:"structWithOnlyOmitZeroTag,omitzero"`
178183

179184
// structWithMinPropertiesOnStruct is a struct field with a minimum number of properties on the struct.
180185
// +optional

0 commit comments

Comments
 (0)