@@ -16,10 +16,7 @@ limitations under the License.
16
16
package optionalfields
17
17
18
18
import (
19
- "errors"
20
- "fmt"
21
19
"go/ast"
22
- "strings"
23
20
24
21
"golang.org/x/tools/go/analysis"
25
22
kalerrors "sigs.k8s.io/kube-api-linter/pkg/analysis/errors"
@@ -48,10 +45,6 @@ func init() {
48
45
)
49
46
}
50
47
51
- var (
52
- errMarkerMissingValue = errors .New ("marker does not have a value" )
53
- )
54
-
55
48
type analyzer struct {
56
49
pointerPolicy OptionalFieldsPointerPolicy
57
50
pointerPreference OptionalFieldsPointerPreference
@@ -135,10 +128,10 @@ func defaultConfig(cfg *OptionalFieldsConfig) {
135
128
}
136
129
137
130
func (a * analyzer ) checkFieldProperties (pass * analysis.Pass , field * ast.Field , fieldName string , markersAccess markershelper.Markers , jsonTags extractjsontags.FieldTagInfo ) {
138
- hasValidZeroValue , completeValidation := isZeroValueValid (pass , field , field .Type , markersAccess )
131
+ hasValidZeroValue , completeValidation := utils . IsZeroValueValid (pass , field , field .Type , markersAccess )
139
132
hasOmitEmpty := jsonTags .OmitEmpty
140
133
isPointer , underlying := isStarExpr (field .Type )
141
- isStruct := isStructType (pass , field .Type )
134
+ isStruct := utils . IsStructType (pass , field .Type )
142
135
143
136
if a .pointerPreference == OptionalFieldsPointerPreferenceAlways {
144
137
// The field must always be a pointer, pointers require omitempty, so enforce that too.
@@ -244,135 +237,8 @@ func (a *analyzer) handleIncompleteFieldValidation(pass *analysis.Pass, field *a
244
237
return
245
238
}
246
239
247
- zeroValue := getTypedZeroValue (pass , underlying )
248
- validationHint := getTypedValidationHint (pass , underlying )
240
+ zeroValue := utils . GetTypedZeroValue (pass , underlying )
241
+ validationHint := utils . GetTypedValidationHint (pass , underlying )
249
242
250
243
pass .Reportf (field .Pos (), "field %s is optional and has a valid zero value (%s), but the validation is not complete (e.g. %s). The field should be a pointer to allow the zero value to be set. If the zero value is not a valid use case, complete the validation and remove the pointer." , fieldName , zeroValue , validationHint )
251
244
}
252
-
253
- // getTypedZeroValue returns the zero value for a given type as a string representation.
254
- func getTypedZeroValue (pass * analysis.Pass , expr ast.Expr ) string {
255
- switch t := expr .(type ) {
256
- case * ast.Ident :
257
- return getIdentZeroValue (pass , t )
258
- case * ast.StructType :
259
- return getStructZeroValue (pass , t )
260
- case * ast.ArrayType :
261
- return "[]"
262
- case * ast.MapType :
263
- return "{}"
264
- default :
265
- return ""
266
- }
267
- }
268
-
269
- // getIdentZeroValue returns the zero value for a given identifier as a string representation.
270
- // Where the ident is an alias for a type, it will look up the type spec to get the underlying type
271
- // and return the zero value for that type.
272
- func getIdentZeroValue (pass * analysis.Pass , ident * ast.Ident ) string {
273
- switch {
274
- case isIntegerIdent (ident ):
275
- return "0"
276
- case isStringIdent (ident ):
277
- return `""`
278
- case isBoolIdent (ident ):
279
- return "false"
280
- case isFloatIdent (ident ):
281
- return "0.0"
282
- }
283
-
284
- typeSpec , ok := utils .LookupTypeSpec (pass , ident )
285
- if ! ok {
286
- return ""
287
- }
288
-
289
- return getTypedZeroValue (pass , typeSpec .Type )
290
- }
291
-
292
- // getStructZeroValue returns the zero value for a struct type as a string representation.
293
- // It constructs a json-like representation of the struct's zero value,
294
- // including only the fields that are not omitted (i.e., do not have the omitempty tag).
295
- func getStructZeroValue (pass * analysis.Pass , structType * ast.StructType ) string {
296
- value := "{"
297
-
298
- jsonTagInfo , ok := pass .ResultOf [extractjsontags .Analyzer ].(extractjsontags.StructFieldTags )
299
- if ! ok {
300
- panic ("could not get struct field tags from pass result" )
301
- }
302
-
303
- for _ , field := range structType .Fields .List {
304
- fieldTagInfo := jsonTagInfo .FieldTags (field )
305
-
306
- if fieldTagInfo .OmitEmpty {
307
- // If the field is omitted, we can use a zero value.
308
- // For structs, if they aren't a pointer another error will be raised.
309
- continue
310
- }
311
-
312
- value += fmt .Sprintf ("%q: %s, " , fieldTagInfo .Name , getTypedZeroValue (pass , field .Type ))
313
- }
314
-
315
- value = strings .TrimSuffix (value , ", " )
316
- value += "}"
317
-
318
- return value
319
- }
320
-
321
- // getTypedValidationHint returns a string hint for the validation that should be applied to a given type.
322
- // This is used to suggest which markers should be applied to the field to complete the validation.
323
- func getTypedValidationHint (pass * analysis.Pass , expr ast.Expr ) string {
324
- switch t := expr .(type ) {
325
- case * ast.Ident :
326
- return getIdentValidationHint (pass , t )
327
- case * ast.StructType :
328
- return "min properties/adding required fields"
329
- case * ast.ArrayType :
330
- return "min items"
331
- case * ast.MapType :
332
- return "min properties"
333
- default :
334
- return ""
335
- }
336
- }
337
-
338
- // getIdentValidationHint returns a string hint for the validation that should be applied to a given identifier.
339
- func getIdentValidationHint (pass * analysis.Pass , ident * ast.Ident ) string {
340
- switch {
341
- case isIntegerIdent (ident ):
342
- return "minimum/maximum"
343
- case isStringIdent (ident ):
344
- return "minimum length"
345
- case isBoolIdent (ident ):
346
- return ""
347
- case isFloatIdent (ident ):
348
- return "minimum/maximum"
349
- }
350
-
351
- typeSpec , ok := utils .LookupTypeSpec (pass , ident )
352
- if ! ok {
353
- return ""
354
- }
355
-
356
- return getTypedValidationHint (pass , typeSpec .Type )
357
- }
358
-
359
- // isStructType checks if the given expression is a struct type.
360
- func isStructType (pass * analysis.Pass , expr ast.Expr ) bool {
361
- _ , underlying := isStarExpr (expr )
362
-
363
- if _ , ok := underlying .(* ast.StructType ); ok {
364
- return true
365
- }
366
-
367
- // Where there's an ident, recurse to find the underlying type.
368
- if ident , ok := underlying .(* ast.Ident ); ok {
369
- typeSpec , ok := utils .LookupTypeSpec (pass , ident )
370
- if ! ok {
371
- return false
372
- }
373
-
374
- return isStructType (pass , typeSpec .Type )
375
- }
376
-
377
- return false
378
- }
0 commit comments