in compiler/crates/graphql-ir/src/build.rs [589:684]
fn build_fragment_spread_arguments(
&mut self,
signature: &FragmentSignature,
arg_list: &List<graphql_syntax::Argument>,
validation_level: ValidationLevel,
) -> DiagnosticsResult<Vec<Argument>> {
let mut has_invalid_arg = false;
let mut errors = Vec::new();
for variable_definition in &signature.variable_definitions {
if variable_definition_requires_argument(variable_definition)
&& arg_list
.items
.named(variable_definition.name.item)
.is_none()
{
errors.push(
Diagnostic::error(
ValidationMessage::MissingRequiredFragmentArgument {
argument_name: variable_definition.name.item,
},
self.location.with_span(arg_list.span),
)
.annotate(
"defined on the fragment here",
variable_definition.name.location,
),
);
}
}
if !errors.is_empty() {
return Err(errors);
}
let result: DiagnosticsResult<Vec<Argument>> = arg_list
.items
.iter()
.map(|arg| {
if let Some(argument_definition) =
signature.variable_definitions.named(arg.name.value)
{
// TODO: We didn't use to enforce types of @args/@argDefs properly, which resulted
// in a lot of code that is technically valid but doesn't type-check. Specifically,
// many fragment @argDefs are typed as non-null but used in places that accept a
// nullable value. Similarly, the corresponding @args pass nullable values. This
// works since ultimately a nullable T flows into a nullable T, but isn't
// technically correct. There are also @argDefs are typed with different types,
// but the persist query allowed them as the types are the same underlyingly.
// NOTE: We keep the same behavior as JS compiler for now, where we don't validate
// types of variables passed to @args at all
let arg_result = self.build_argument(
arg,
&argument_definition.type_,
ValidationLevel::Strict,
);
if arg_result.is_err() && validation_level == ValidationLevel::Loose {
has_invalid_arg = true;
self.build_argument(arg, &argument_definition.type_, ValidationLevel::Loose)
} else {
arg_result
}
} else if validation_level == ValidationLevel::Loose {
has_invalid_arg = true;
Ok(self.build_argument(
arg,
self.schema.unchecked_argument_type_sentinel(),
ValidationLevel::Loose,
)?)
} else {
let possible_argument_names = signature
.variable_definitions
.iter()
.map(|arg_def| arg_def.name.item)
.collect::<Vec<_>>();
let suggestions = suggestion_list::suggestion_list(
arg.name.value,
&possible_argument_names,
5,
);
Err(vec![Diagnostic::error_with_data(
ValidationMessageWithData::UnknownArgument {
argument_name: arg.name.value,
suggestions,
},
self.location.with_span(arg.span),
)])
}
})
.collect();
if validation_level == ValidationLevel::Loose && !has_invalid_arg {
Err(vec![Diagnostic::error(
ValidationMessage::UnnecessaryUncheckedArgumentsDirective,
self.location.with_span(arg_list.span),
)])
} else {
result
}
}