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,
})
}