Skip to content

Commit a285833

Browse files
committed
Implement optional enforcement of omitempty on optional fields
1 parent 149a51e commit a285833

File tree

7 files changed

+1064
-25
lines changed

7 files changed

+1064
-25
lines changed

pkg/analysis/optionalfields/analyzer.go

Lines changed: 219 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
@@ -148,6 +148,11 @@ type OptionalFieldsConfig struct {
148148
// This defines how the linter should handle optional fields, and whether they should be pointers or not.
149149
// By default, all fields will be expected to be pointers, and the linter will suggest fixes if they are not.
150150
Pointers OptionalFieldsPointers `json:"pointers"`
151+
152+
// omitempty is the policy for the `omitempty` tag within the json tag for fields.
153+
// This defines how the linter should handle optional fields, and whether they should have the omitempty tag or not.
154+
// By default, all fields will be expected to have the `omitempty` tag.
155+
OmitEmpty OptionalFieldsOmitEmpty `json:"omitempty"`
151156
}
152157

153158
type OptionalFieldsPointers struct {
@@ -167,6 +172,15 @@ type OptionalFieldsPointers struct {
167172
Policy OptionalFieldsPointerPolicy `json:"policy"`
168173
}
169174

175+
type OptionalFieldsOmitEmpty struct {
176+
// policy determines whether the linter should require omitempty for all optional fields.
177+
// Valid values are "SuggestFix" and "Ignore".
178+
// When set to "SuggestFix", the linter will suggest adding the `omitempty` tag when an optional field does not have it.
179+
// When set to "Ignore", and optional field missing the `omitempty` tag will be ignored.
180+
// 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.
181+
Policy OptionalFieldsOmitEmptyPolicy `json:"policy"`
182+
}
183+
170184
// OptionalFieldsPointerPreference is the preference for pointers in optional fields.
171185
type OptionalFieldsPointerPreference string
172186

@@ -189,6 +203,17 @@ const (
189203
OptionalFieldsPointerPolicyWarn OptionalFieldsPointerPolicy = "Warn"
190204
)
191205

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

0 commit comments

Comments
 (0)