diff --git a/baked_in.go b/baked_in.go index 679729a5..5db6e7be 100644 --- a/baked_in.go +++ b/baked_in.go @@ -2059,15 +2059,27 @@ func requireCheckFieldValue( switch kind { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if value == "nil" { + return false + } return field.Int() == asInt(value) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + if value == "nil" { + return false + } return field.Uint() == asUint(value) case reflect.Float32: + if value == "nil" { + return false + } return field.Float() == asFloat32(value) case reflect.Float64: + if value == "nil" { + return false + } return field.Float() == asFloat64(value) case reflect.Slice, reflect.Map: @@ -2075,19 +2087,21 @@ func requireCheckFieldValue( return field.IsNil() } return int64(field.Len()) == asInt(value) + case reflect.Array: // Arrays can't be nil, so only compare lengths + if value == "nil" { + return false + } return int64(field.Len()) == asInt(value) case reflect.Bool: return field.Bool() == (value == "true") case reflect.Ptr: - if field.IsNil() { - return value == "nil" - } - // Handle non-nil pointers - return requireCheckFieldValue(fl, param, value, defaultNotFoundValue) + // extractTypeInternal dereferences non-nil pointers before reaching here, + // so this case is only reached when the pointer itself is nil. + return value == "nil" } // default reflect.String: diff --git a/validator_test.go b/validator_test.go index 67d3f83a..b6480855 100644 --- a/validator_test.go +++ b/validator_test.go @@ -13143,6 +13143,32 @@ func TestExcludedIf(t *testing.T) { } errs = validate.Struct(test12) Equal(t, errs, nil) + + // Issue #1320: excluded_if with "nil" param must not panic when comparing + // a non-nil pointer-to-numeric field against the literal "nil". + w13 := 10 + r13 := 0 + test13 := struct { + W *int + R *int `validate:"excluded_if=W nil"` + }{ + W: &w13, + R: &r13, + } + errs = validate.Struct(test13) + Equal(t, errs, nil) // W is non-nil → excluded_if condition false → R not excluded → no error + + // When W IS nil the excluded_if condition fires and R must be empty/nil to pass. + test14 := struct { + W *int + R *int `validate:"excluded_if=W nil"` + }{ + W: nil, + R: nil, + } + errs = validate.Struct(test14) + Equal(t, errs, nil) // W is nil → excluded_if condition true → R must be nil/absent → passes + // Checks number of params in struct tag is correct defer func() { if r := recover(); r == nil {