in compiler/crates/graphql-ir/src/signatures.rs [178:382]
fn build_fragment_variable_definitions(
schema: &SDLSchema,
fragment: &graphql_syntax::FragmentDefinition,
directive: &graphql_syntax::Directive,
enable_provided_variables: &FeatureFlag,
) -> DiagnosticsResult<Vec<VariableDefinition>> {
if let Some(arguments) = &directive.arguments {
Ok(arguments
.items
.iter()
.map(|variable_arg| {
if let graphql_syntax::Value::Constant(graphql_syntax::ConstantValue::Object(
object,
)) = &variable_arg.value
{
let mut type_arg = None;
let mut default_arg = None;
let mut unused_local_variable_arg = None;
let mut provider_arg = None;
let mut directives_arg = None;
let mut extra_items = Vec::new();
for item in &object.items {
let name = item.name.value;
if name == *TYPE {
type_arg = Some(item);
} else if name == *DEFAULT_VALUE {
default_arg = Some(item);
} else if name == *UNUSED_LOCAL_VARIABLE_DEPRECATED {
unused_local_variable_arg = Some(item);
} else if name == *DIRECTIVES {
directives_arg = Some(item);
} else if name == *PROVIDER {
if !enable_provided_variables.is_enabled_for(fragment.name.value) {
return Err(vec![Diagnostic::error(
format!("Invalid usage of provided variable: this feature is gated and currently set to {}",
enable_provided_variables),
fragment.location.with_span(item.span),
)]);
}
provider_arg = Some(item);
} else {
extra_items.push(item);
}
}
// Check that no extraneous keys were supplied
if !extra_items.is_empty() {
return Err(extra_items
.iter()
.map(|item| {
Diagnostic::error(
ValidationMessage::InvalidArgumentDefinitionsKey(
item.name.value,
),
fragment.location.with_span(item.span),
)
})
.collect());
}
let variable_name = &variable_arg.name;
let mut directives = Vec::new();
// Convert variable type, validate that it's an input type
let type_ = get_argument_type(schema, fragment.location, type_arg, object)?;
if !type_.inner().is_input_type() {
return Err(Diagnostic::error(
ValidationMessage::ExpectedFragmentArgumentToHaveInputType(
schema.get_type_name(type_.inner()),
),
fragment.location.with_span(variable_arg.value.span()),
)
.into());
}
if let Some(unused_local_variable_arg) = unused_local_variable_arg {
if !matches!(
unused_local_variable_arg,
graphql_syntax::ConstantArgument {
value: graphql_syntax::ConstantValue::Boolean(
graphql_syntax::BooleanNode { value: true, .. }
),
..
}
) {
return Err(vec![Diagnostic::error(
ValidationMessage::InvalidUnusedFragmentVariableSuppressionArg,
fragment
.location
.with_span(unused_local_variable_arg.value.span()),
)]);
}
directives.push(crate::Directive {
name: WithLocation::new(
fragment.location.with_span(unused_local_variable_arg.span),
*UNUSED_LOCAL_VARIABLE_DEPRECATED,
),
arguments: Vec::new(),
data: None,
});
}
if let Some(provider_arg) = provider_arg {
let provider_module_name = provider_arg.value.get_string_literal().ok_or_else(|| {
vec![Diagnostic::error(
ValidationMessage::LiteralStringArgumentExpectedForDirective{arg_name: *PROVIDER, directive_name: *ARGUMENT_DEFINITION },
fragment
.location
.with_span(provider_arg.value.span()),
)]
})?;
if let Some(default_arg_) = default_arg {
return Err(vec![Diagnostic::error(
ValidationMessage::ProvidedVariableIncompatibleWithDefaultValue{argument_name: variable_name.value},
fragment
.location
.with_span(provider_arg.span),
).annotate("Default value declared here",
fragment
.location
.with_span(default_arg_.span))]);
}
directives.push(crate::Directive {
name: WithLocation::new(
fragment.location.with_span(provider_arg.span),
ProvidedVariableMetadata::directive_name(),
),
arguments: Vec::new(),
data: Some(Box::new(ProvidedVariableMetadata{
module_name: provider_module_name,
original_variable_name: variable_name.value
})),
});
}
if let Some(directives_arg) = directives_arg {
if let graphql_syntax::ConstantValue::List(items) = &directives_arg.value {
for item in &items.items {
if let graphql_syntax::ConstantValue::String(directive_string) = item {
let ast_directive = graphql_syntax::parse_directive(
directive_string.value.lookup(),
// We currently don't have the ability to pass offset locations
// to the parser call, so we first use a generated location and
// later override it with an approximation.
SourceLocationKey::generated(),
)
.map_err(|mut diagnostics| {
for diagnostic in &mut diagnostics {
diagnostic.override_location(fragment.location.with_span(directive_string.token.span));
}
diagnostics
})?;
let directive = build_directive(
schema,
&ast_directive,
graphql_syntax::DirectiveLocation::VariableDefinition,
// We currently don't have the ability to pass offset locations
// to the parser call, so we first use a generated location and
// later override it with an approximation.
Location::generated(),
)
.map_err(|mut diagnostics| {
for diagnostic in &mut diagnostics {
diagnostic.override_location(fragment.location.with_span(directive_string.token.span));
}
diagnostics
})?;
directives.push(directive);
} else {
return Err(vec![Diagnostic::error(
ValidationMessage::ArgumentDefinitionsDirectivesNotStringListLiteral,
fragment.location.with_span(item.span()),
)]);
}
}
} else {
return Err(vec![Diagnostic::error(
ValidationMessage::ArgumentDefinitionsDirectivesNotStringListLiteral,
fragment.location.with_span(directives_arg.value.span()),
)]);
}
}
let default_value =
get_default_value(schema, fragment.location, default_arg, &type_)?;
Ok(VariableDefinition {
name: variable_name
.name_with_location(fragment.location.source_location()),
type_,
directives,
default_value,
})
} else {
Err(Diagnostic::error(
ValidationMessage::ExpectedArgumentDefinitionToBeObject(),
fragment.location.with_span(variable_arg.value.span()),
)
.into())
}
})
.collect::<DiagnosticsResult<Vec<VariableDefinition>>>()?)
} else {
Ok(Default::default())
}
}