@@ -7,10 +7,12 @@ use crate::executable;
7
7
use crate :: schema;
8
8
use crate :: schema:: Implementers ;
9
9
use crate :: validation:: diagnostics:: DiagnosticData ;
10
+ use crate :: validation:: variable:: walk_selections_with_deduped_fragments;
10
11
use crate :: validation:: CycleError ;
11
12
use crate :: validation:: DiagnosticList ;
12
13
use crate :: validation:: OperationValidationContext ;
13
14
use crate :: validation:: RecursionGuard ;
15
+ use crate :: validation:: RecursionLimitError ;
14
16
use crate :: validation:: RecursionStack ;
15
17
use crate :: validation:: SourceSpan ;
16
18
use crate :: ExecutableDocument ;
@@ -376,49 +378,40 @@ pub(crate) fn validate_fragment_type_condition(
376
378
}
377
379
}
378
380
379
- pub ( crate ) fn validate_fragment_used (
380
- diagnostics : & mut DiagnosticList ,
381
+ fn collect_used_fragments (
381
382
document : & ExecutableDocument ,
382
- fragment : & Node < executable:: Fragment > ,
383
- ) {
384
- let fragment_name = & fragment. name ;
385
-
386
- let mut all_selections = document
387
- . operations
388
- . iter ( )
389
- . map ( |operation| & operation. selection_set )
390
- . chain (
391
- document
392
- . fragments
393
- . values ( )
394
- . map ( |fragment| & fragment. selection_set ) ,
395
- )
396
- . flat_map ( |set| & set. selections ) ;
397
-
398
- let is_used = all_selections. any ( |sel| selection_uses_fragment ( sel, fragment_name) ) ;
399
-
400
- // Fragments must be used within the schema
401
- //
402
- // Returns Unused Fragment error.
403
- if !is_used {
404
- diagnostics. push (
405
- fragment. location ( ) ,
406
- DiagnosticData :: UnusedFragment {
407
- name : fragment_name. clone ( ) ,
408
- } ,
409
- )
383
+ ) -> Result < HashSet < & Name > , RecursionLimitError > {
384
+ let mut names = HashSet :: default ( ) ;
385
+ for operation in document. operations . iter ( ) {
386
+ walk_selections_with_deduped_fragments ( document, & operation. selection_set , |selection| {
387
+ if let executable:: Selection :: FragmentSpread ( spread) = selection {
388
+ names. insert ( & spread. fragment_name ) ;
389
+ }
390
+ } ) ?;
410
391
}
392
+ Ok ( names)
411
393
}
412
394
413
- fn selection_uses_fragment ( sel : & executable:: Selection , name : & str ) -> bool {
414
- let sub_selections = match sel {
415
- executable:: Selection :: FragmentSpread ( fragment) => return fragment. fragment_name == name,
416
- executable:: Selection :: Field ( field) => & field. selection_set ,
417
- executable:: Selection :: InlineFragment ( inline) => & inline. selection_set ,
395
+ pub ( crate ) fn validate_fragments_used (
396
+ diagnostics : & mut DiagnosticList ,
397
+ document : & ExecutableDocument ,
398
+ ) {
399
+ let Ok ( used_fragments) = collect_used_fragments ( document) else {
400
+ diagnostics. push ( None , super :: Details :: RecursionLimitError ) ;
401
+ return ;
418
402
} ;
419
403
420
- sub_selections
421
- . selections
422
- . iter ( )
423
- . any ( |sel| selection_uses_fragment ( sel, name) )
404
+ for fragment in document. fragments . values ( ) {
405
+ // Fragments must be used within the schema
406
+ //
407
+ // Returns Unused Fragment error.
408
+ if !used_fragments. contains ( & fragment. name ) {
409
+ diagnostics. push (
410
+ fragment. location ( ) ,
411
+ DiagnosticData :: UnusedFragment {
412
+ name : fragment. name . clone ( ) ,
413
+ } ,
414
+ )
415
+ }
416
+ }
424
417
}
0 commit comments