Skip to content

Commit cc3f8e5

Browse files
committed
Implement optional enforcement of omitempty on optional fields
1 parent 80957da commit cc3f8e5

File tree

7 files changed

+1065
-25
lines changed

7 files changed

+1065
-25
lines changed

pkg/analysis/optionalfields/analyzer.go

Lines changed: 220 additions & 25 deletions
Large diffs are not rendered by default.

pkg/analysis/optionalfields/analyzer_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,23 @@ func TestWhenRequiredPreferenceConfiguration(t *testing.T) {
3535

3636
analysistest.RunWithSuggestedFixes(t, testdata, a, "b")
3737
}
38+
39+
func TestWhenRequiredWithOmitEmptyIgnorePreferenceConfiguration(t *testing.T) {
40+
testdata := analysistest.TestData()
41+
42+
a, err := requiredfields.Initializer().Init(config.LintersConfig{
43+
OptionalFields: config.OptionalFieldsConfig{
44+
Pointers: config.OptionalFieldsPointers{
45+
Preference: config.OptionalFieldsPointerPreferenceWhenRequired,
46+
},
47+
OmitEmpty: config.OptionalFieldsOmitEmpty{
48+
Policy: config.OptionalFieldsOmitEmptyPolicyIgnore,
49+
},
50+
},
51+
})
52+
if err != nil {
53+
t.Fatal(err)
54+
}
55+
56+
analysistest.RunWithSuggestedFixes(t, testdata, a, "c")
57+
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,26 @@ type A struct {
2121
// +optional
2222
String string `json:"string,omitempty"` // want "field String is optional and should be a pointer"
2323

24+
// NonOmittedString is a string field without omitempty
25+
// +optional
26+
NonOmittedString string `json:"nonOmittedString"` // want "field NonOmittedString is optional and should be a pointer" "field NonOmittedString is optional and should be omitempty"
27+
2428
// int is an int field.
2529
// +optional
2630
Int int `json:"int,omitempty"` // want "field Int is optional and should be a pointer"
2731

32+
// nonOmittedInt is an int field without omitempty
33+
// +optional
34+
NonOmittedInt int `json:"nonOmittedInt"` // want "field NonOmittedInt is optional and should be a pointer" "field NonOmittedInt is optional and should be omitempty"
35+
2836
// struct is a struct field.
2937
// +optional
3038
Struct B `json:"struct,omitempty"` // want "field Struct is optional and should be a pointer"
3139

40+
// nonOmittedStruct is a struct field without omitempty.
41+
// +optional
42+
NonOmittedStruct B `json:"nonOmittedStruct"` // want "field NonOmittedStruct is optional and should be a pointer" "field NonOmittedStruct is optional and should be omitempty"
43+
3244
// slice is a slice field.
3345
// +optional
3446
Slice []string `json:"slice,omitempty"`

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,26 @@ type A struct {
2121
// +optional
2222
String *string `json:"string,omitempty"` // want "field String is optional and should be a pointer"
2323

24+
// NonOmittedString is a string field without omitempty
25+
// +optional
26+
NonOmittedString *string `json:"nonOmittedString,omitempty"` // want "field NonOmittedString is optional and should be a pointer" "field NonOmittedString is optional and should be omitempty"
27+
2428
// int is an int field.
2529
// +optional
2630
Int *int `json:"int,omitempty"` // want "field Int is optional and should be a pointer"
2731

32+
// nonOmittedInt is an int field without omitempty
33+
// +optional
34+
NonOmittedInt *int `json:"nonOmittedInt,omitempty"` // want "field NonOmittedInt is optional and should be a pointer" "field NonOmittedInt is optional and should be omitempty"
35+
2836
// struct is a struct field.
2937
// +optional
3038
Struct *B `json:"struct,omitempty"` // want "field Struct is optional and should be a pointer"
3139

40+
// nonOmittedStruct is a struct field without omitempty.
41+
// +optional
42+
NonOmittedStruct *B `json:"nonOmittedStruct,omitempty"` // want "field NonOmittedStruct is optional and should be a pointer" "field NonOmittedStruct is optional and should be omitempty"
43+
3244
// slice is a slice field.
3345
// +optional
3446
Slice []string `json:"slice,omitempty"`

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

Lines changed: 393 additions & 0 deletions
Large diffs are not rendered by default.

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

Lines changed: 383 additions & 0 deletions
Large diffs are not rendered by default.

pkg/config/linters_config.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ type OptionalFieldsConfig struct {
151151
// This defines how the linter should handle optional fields, and whether they should be pointers or not.
152152
// By default, all fields will be expected to be pointers, and the linter will suggest fixes if they are not.
153153
Pointers OptionalFieldsPointers `json:"pointers"`
154+
155+
// omitempty is the policy for the `omitempty` tag within the json tag for fields.
156+
// This defines how the linter should handle optional fields, and whether they should have the omitempty tag or not.
157+
// By default, all fields will be expected to have the `omitempty` tag.
158+
OmitEmpty OptionalFieldsOmitEmpty `json:"omitempty"`
154159
}
155160

156161
type OptionalFieldsPointers struct {
@@ -170,6 +175,15 @@ type OptionalFieldsPointers struct {
170175
Policy OptionalFieldsPointerPolicy `json:"policy"`
171176
}
172177

178+
type OptionalFieldsOmitEmpty struct {
179+
// policy determines whether the linter should require omitempty for all optional fields.
180+
// Valid values are "SuggestFix" and "Ignore".
181+
// When set to "SuggestFix", the linter will suggest adding the `omitempty` tag when an optional field does not have it.
182+
// When set to "Ignore", and optional field missing the `omitempty` tag will be ignored.
183+
// Note, when set to "Ignore", and a field does not have the `omitempty` tag, this may affect whether the field should be a pointer or not.
184+
Policy OptionalFieldsOmitEmptyPolicy `json:"policy"`
185+
}
186+
173187
// OptionalFieldsPointerPreference is the preference for pointers in optional fields.
174188
type OptionalFieldsPointerPreference string
175189

@@ -192,6 +206,17 @@ const (
192206
OptionalFieldsPointerPolicyWarn OptionalFieldsPointerPolicy = "Warn"
193207
)
194208

209+
// OptionalFieldsOmitEmptyPolicy is the policy for the omitempty tag on optional fields.
210+
type OptionalFieldsOmitEmptyPolicy string
211+
212+
const (
213+
// OptionalFieldsOmitEmptyPolicySuggestFix indicates that the linter will emit a warning if the field does not have omitempty, and suggest a fix.
214+
OptionalFieldsOmitEmptyPolicySuggestFix OptionalFieldsOmitEmptyPolicy = "SuggestFix"
215+
216+
// OptionalFieldsOmitEmptyPolicyIgnore indicates that the linter will ignore any field missing the omitempty tag.
217+
OptionalFieldsOmitEmptyPolicyIgnore OptionalFieldsOmitEmptyPolicy = "Ignore"
218+
)
219+
195220
// OptionalOrRequiredConfig contains configuration for the optionalorrequired linter.
196221
type OptionalOrRequiredConfig struct {
197222
// preferredOptionalMarker is the preferred marker to use for optional fields.

0 commit comments

Comments
 (0)