@@ -99,6 +99,8 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
9999
100100 current , kind , v .fldIsPointer = v .extractTypeInternal (current , false )
101101
102+ var isNestedStruct bool
103+
102104 switch kind {
103105 case reflect .Ptr , reflect .Interface , reflect .Invalid :
104106
@@ -161,85 +163,56 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
161163 }
162164
163165 case reflect .Struct :
164-
165- typ = current .Type ()
166-
167- if ! typ .ConvertibleTo (timeType ) {
168-
169- if ct != nil {
170-
171- if ct .typeof == typeStructOnly {
172- goto CONTINUE
173- } else if ct .typeof == typeIsDefault || ct .typeof == typeNestedStructLevel {
174- // set Field Level fields
175- v .slflParent = parent
176- v .flField = current
177- v .cf = cf
178- v .ct = ct
179-
180- if ! ct .fn (ctx , v ) {
181- v .str1 = string (append (ns , cf .altName ... ))
182-
183- if v .v .hasTagNameFunc {
184- v .str2 = string (append (structNs , cf .name ... ))
185- } else {
186- v .str2 = v .str1
187- }
188-
189- v .errs = append (v .errs ,
190- & fieldError {
191- v : v .v ,
192- tag : ct .aliasTag ,
193- actualTag : ct .tag ,
194- ns : v .str1 ,
195- structNs : v .str2 ,
196- fieldLen : uint8 (len (cf .altName )),
197- structfieldLen : uint8 (len (cf .name )),
198- value : current .Interface (),
199- param : ct .param ,
200- kind : kind ,
201- typ : typ ,
202- },
203- )
204- return
205- }
206- }
207-
208- ct = ct .next
209- }
210-
211- if ct != nil && ct .typeof == typeNoStructLevel {
212- return
213- }
214-
215- CONTINUE:
216- // if len == 0 then validating using 'Var' or 'VarWithValue'
217- // Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
218- // VarWithField - this allows for validating against each field within the struct against a specific value
219- // pretty handy in certain situations
220- if len (cf .name ) > 0 {
221- ns = append (append (ns , cf .altName ... ), '.' )
222- structNs = append (append (structNs , cf .name ... ), '.' )
223- }
224-
225- v .validateStruct (ctx , parent , current , typ , ns , structNs , ct )
226- return
166+ isNestedStruct = ! current .Type ().ConvertibleTo (timeType )
167+ // For backward compatibility before struct level validation tags were supported
168+ // as there were a number of projects relying on `required` not failing on non-pointer
169+ // structs. Since it's basically nonsensical to use `required` with a non-pointer struct
170+ // are explicitly skipping the required validation for it. This WILL be removed in the
171+ // next major version.
172+ if ! v .v .requiredStructEnabled && ct != nil && ct .tag == requiredTag {
173+ ct = ct .next
227174 }
228175 }
229176
230- if ct == nil || ! ct .hasTag {
231- return
232- }
233-
234177 typ = current .Type ()
235178
236179OUTER:
237180 for {
238- if ct == nil {
181+ if ct == nil || ! ct .hasTag || (isNestedStruct && len (cf .name ) == 0 ) {
182+ // isNestedStruct check here
183+ if isNestedStruct {
184+ // if len == 0 then validating using 'Var' or 'VarWithValue'
185+ // Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
186+ // VarWithField - this allows for validating against each field within the struct against a specific value
187+ // pretty handy in certain situations
188+ if len (cf .name ) > 0 {
189+ ns = append (append (ns , cf .altName ... ), '.' )
190+ structNs = append (append (structNs , cf .name ... ), '.' )
191+ }
192+
193+ v .validateStruct (ctx , parent , current , typ , ns , structNs , ct )
194+ }
239195 return
240196 }
241197
242198 switch ct .typeof {
199+ case typeNoStructLevel :
200+ return
201+
202+ case typeStructOnly :
203+ if isNestedStruct {
204+ // if len == 0 then validating using 'Var' or 'VarWithValue'
205+ // Var - doesn't make much sense to do it that way, should call 'Struct', but no harm...
206+ // VarWithField - this allows for validating against each field within the struct against a specific value
207+ // pretty handy in certain situations
208+ if len (cf .name ) > 0 {
209+ ns = append (append (ns , cf .altName ... ), '.' )
210+ structNs = append (append (structNs , cf .name ... ), '.' )
211+ }
212+
213+ v .validateStruct (ctx , parent , current , typ , ns , structNs , ct )
214+ }
215+ return
243216
244217 case typeOmitEmpty :
245218
@@ -366,7 +339,7 @@ OUTER:
366339 ct = ct .next
367340
368341 if ct == nil {
369- return
342+ continue OUTER
370343 }
371344
372345 if ct .typeof != typeOr {
0 commit comments