fn build_fragment_spread()

in compiler/crates/graphql-ir/src/build.rs [686:829]


    fn build_fragment_spread(
        &mut self,
        spread: &graphql_syntax::FragmentSpread,
        parent_types: &[TypeReference],
    ) -> DiagnosticsResult<FragmentSpread> {
        let spread_name_with_location = spread
            .name
            .name_with_location(self.location.source_location());

        // Exit early if the fragment does not exist
        let signature = match self.signatures.get(&spread.name.value) {
            Some(fragment) => fragment,
            None if self.options.allow_undefined_fragment_spreads => {
                let directives = if self.options.relay_mode.is_some() {
                    self.build_directives(
                        spread.directives.iter().filter(|directive| {
                            directive.name.value != *DIRECTIVE_ARGUMENTS
                                && directive.name.value != *DIRECTIVE_UNCHECKED_ARGUMENTS
                        }),
                        DirectiveLocation::FragmentSpread,
                    )?
                } else {
                    self.build_directives(&spread.directives, DirectiveLocation::FragmentSpread)?
                };
                return Ok(FragmentSpread {
                    fragment: spread_name_with_location,
                    arguments: Vec::new(),
                    directives,
                });
            }
            None => {
                let fragment_names = self
                    .signatures
                    .values()
                    .filter(|signature| {
                        self.find_conflicting_parent_type(parent_types, signature.type_condition)
                            .is_none()
                    })
                    .map(|signature| signature.name.item)
                    .collect::<Vec<StringKey>>();
                let suggestions =
                    suggestion_list::suggestion_list(spread.name.value, &fragment_names, 5);
                return Err(vec![Diagnostic::error_with_data(
                    ValidationMessageWithData::UndefinedFragment {
                        fragment_name: spread.name.value,
                        suggestions,
                    },
                    self.location.with_span(spread.name.span),
                )]);
            }
        };

        if let Some(parent_type) =
            self.find_conflicting_parent_type(parent_types, signature.type_condition)
        {
            // no possible overlap
            return Err(vec![Diagnostic::error(
                ValidationMessage::InvalidFragmentSpreadType {
                    fragment_name: spread.name.value,
                    parent_type: self.schema.get_type_name(parent_type.inner()),
                    type_condition: self.schema.get_type_name(signature.type_condition),
                },
                self.location.with_span(spread.span),
            )]);
        }

        if self.options.fragment_variables_semantic != FragmentVariablesSemantic::PassedValue {
            let directives =
                self.build_directives(spread.directives.iter(), DirectiveLocation::FragmentSpread)?;
            return Ok(FragmentSpread {
                fragment: spread_name_with_location,
                arguments: Vec::new(),
                directives,
            });
        }

        let (mut argument_directives, other_directives) = spread
            .directives
            .iter()
            .partition::<Vec<_>, _>(|directive| {
                directive.name.value == *DIRECTIVE_ARGUMENTS
                    || directive.name.value == *DIRECTIVE_UNCHECKED_ARGUMENTS
            });

        if argument_directives.len() > 1 {
            let mut locations = argument_directives
                .iter()
                .map(|x| self.location.with_span(x.span));
            let mut error = Diagnostic::error(
                ValidationMessage::ExpectedOneArgumentsDirective(),
                locations.next().unwrap(),
            );
            for location in locations {
                error = error.annotate("duplicate definition", location);
            }
            return Err(vec![error]);
        }

        let arguments = if let Some(graphql_syntax::Directive {
            name,
            arguments: Some(arg_list),
            ..
        }) = argument_directives.pop()
        {
            let validation_level = if name.value == *DIRECTIVE_UNCHECKED_ARGUMENTS {
                ValidationLevel::Loose
            } else {
                ValidationLevel::Strict
            };
            self.build_fragment_spread_arguments(signature, arg_list, validation_level)
        } else {
            let errors: Vec<_> = signature
                .variable_definitions
                .iter()
                .filter(|variable_definition| {
                    variable_definition_requires_argument(variable_definition)
                })
                .map(|variable_definition| {
                    Diagnostic::error(
                        ValidationMessage::MissingRequiredFragmentArgument {
                            argument_name: variable_definition.name.item,
                        },
                        self.location.with_span(spread.span),
                    )
                    .annotate(
                        "defined on the fragment here",
                        variable_definition.name.location,
                    )
                })
                .collect();
            if !errors.is_empty() {
                return Err(errors);
            }
            Ok(Default::default())
        };

        let directives = self.build_directives(other_directives, DirectiveLocation::FragmentSpread);
        let (arguments, directives) = try2(arguments, directives)?;
        Ok(FragmentSpread {
            fragment: spread_name_with_location,
            arguments,
            directives,
        })
    }