1pub(crate) mod argument;
5pub(crate) mod diagnostics;
6pub(crate) mod directive;
7pub(crate) mod enum_;
8pub(crate) mod field;
9pub(crate) mod fragment;
10pub(crate) mod input_object;
11pub(crate) mod interface;
12pub(crate) mod object;
13pub(crate) mod operation;
14pub(crate) mod scalar;
15pub(crate) mod schema;
16pub(crate) mod selection;
17pub(crate) mod union_;
18pub(crate) mod value;
19pub(crate) mod variable;
20
21use crate::collections::HashMap;
22use crate::collections::HashSet;
23use crate::collections::IndexSet;
24use crate::coordinate::SchemaCoordinate;
25use crate::diagnostic::CliReport;
26use crate::diagnostic::Diagnostic;
27use crate::diagnostic::ToCliReport;
28use crate::executable::BuildError as ExecutableBuildError;
29use crate::executable::ConflictingFieldArgument;
30use crate::executable::ConflictingFieldName;
31use crate::executable::ConflictingFieldType;
32#[cfg(doc)]
33use crate::executable::ExecutableDocument;
34use crate::executable::VariableDefinition;
35use crate::parser::SourceMap;
36use crate::parser::SourceSpan;
37use crate::response::GraphQLError;
38use crate::schema::BuildError as SchemaBuildError;
39use crate::schema::Implementers;
40use crate::Name;
41use crate::Node;
42use crate::Schema;
43use std::fmt;
44use std::sync::Arc;
45use std::sync::OnceLock;
46
47#[derive(Debug, Clone, Eq, PartialEq)]
64#[repr(transparent)]
65pub struct Valid<T>(pub(crate) T);
66
67impl<T> Valid<T> {
68 pub fn assume_valid(document: T) -> Self {
78 Self(document)
79 }
80
81 pub fn assume_valid_ref(document: &T) -> &Self {
91 let ptr: *const T = document;
92 let ptr: *const Valid<T> = ptr.cast();
93 unsafe { &*ptr }
96 }
97
98 pub fn into_inner(self) -> T {
100 self.0
101 }
102}
103
104impl<T> std::ops::Deref for Valid<T> {
105 type Target = T;
106
107 fn deref(&self) -> &Self::Target {
108 &self.0
109 }
110}
111
112impl<T> AsRef<T> for Valid<T> {
113 fn as_ref(&self) -> &T {
114 &self.0
115 }
116}
117
118impl<T: fmt::Display> fmt::Display for Valid<T> {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 self.0.fmt(f)
121 }
122}
123
124#[derive(Debug)]
126pub(crate) struct ExecutableValidationContext<'a> {
127 schema: Option<&'a Schema>,
129 implementers_map: OnceLock<HashMap<Name, Implementers>>,
131}
132
133impl<'a> ExecutableValidationContext<'a> {
134 pub(crate) fn new(schema: Option<&'a Schema>) -> Self {
135 Self {
136 schema,
137 implementers_map: Default::default(),
138 }
139 }
140
141 pub(crate) fn schema(&self) -> Option<&'a Schema> {
143 self.schema
144 }
145
146 pub(crate) fn implementers_map(&self) -> &HashMap<Name, Implementers> {
148 self.implementers_map.get_or_init(|| {
149 self.schema
150 .map(|schema| schema.implementers_map())
151 .unwrap_or_default()
152 })
153 }
154
155 pub(crate) fn operation_context<'o>(
157 &'o self,
158 variables: &'o [Node<VariableDefinition>],
159 ) -> OperationValidationContext<'o> {
160 OperationValidationContext {
161 executable: self,
162 variables,
163 validated_fragments: HashSet::default(),
164 }
165 }
166}
167
168#[derive(Debug)]
170pub(crate) struct OperationValidationContext<'a> {
171 executable: &'a ExecutableValidationContext<'a>,
174 pub(crate) variables: &'a [Node<VariableDefinition>],
176 pub(crate) validated_fragments: HashSet<Name>,
177}
178
179impl<'a> OperationValidationContext<'a> {
180 pub(crate) fn schema(&self) -> Option<&'a Schema> {
181 self.executable.schema
182 }
183
184 pub(crate) fn implementers_map(&self) -> &HashMap<Name, Implementers> {
186 self.executable.implementers_map()
187 }
188}
189
190pub struct WithErrors<T> {
197 pub partial: T,
201
202 pub errors: DiagnosticList,
205}
206
207impl<T> fmt::Debug for WithErrors<T> {
208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209 self.errors.fmt(f)
210 }
211}
212
213impl<T> fmt::Display for WithErrors<T> {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 self.errors.fmt(f)
216 }
217}
218
219#[derive(Debug, Clone)]
229pub(crate) struct SuspectedValidationBug {
230 pub message: String,
231 pub location: Option<SourceSpan>,
232}
233
234#[derive(Clone)]
236pub struct DiagnosticList {
237 pub(crate) sources: SourceMap,
238 diagnostics_data: Vec<DiagnosticData>,
239}
240
241#[derive(thiserror::Error, Debug, Clone)]
243#[error("{details}")]
244pub struct DiagnosticData {
245 location: Option<SourceSpan>,
246 details: Details,
247}
248
249#[derive(thiserror::Error, Debug, Clone)]
250pub(crate) enum Details {
251 #[error("{message}")]
252 ParserLimit { message: String },
253 #[error("syntax error: {message}")]
254 SyntaxError { message: String },
255 #[error("{0}")]
256 SchemaBuildError(SchemaBuildError),
257 #[error("{0}")]
258 ExecutableBuildError(ExecutableBuildError),
259 #[error(transparent)]
261 CompilerDiagnostic(diagnostics::DiagnosticData),
262 #[error("too much recursion")]
263 RecursionLimitError,
264}
265
266impl DiagnosticData {
267 #[doc(hidden)]
270 pub fn unstable_error_name(&self) -> Option<&'static str> {
271 match &self.details {
272 Details::CompilerDiagnostic(diagnostic) => {
273 use diagnostics::DiagnosticData::*;
274 Some(match diagnostic {
275 RecursionError { .. } => "RecursionError",
276 UniqueVariable { .. } => "UniqueVariable",
277 UniqueArgument { .. } => "UniqueArgument",
278 UniqueInputValue { .. } => "UniqueInputValue",
279 UndefinedArgument { .. } => "UndefinedArgument",
280 UndefinedDefinition { .. } => "UndefinedDefinition",
281 UndefinedDirective { .. } => "UndefinedDirective",
282 UndefinedVariable { .. } => "UndefinedVariable",
283 UndefinedFragment { .. } => "UndefinedFragment",
284 UndefinedEnumValue { .. } => "UndefinedEnumValue",
285 UndefinedInputValue { .. } => "UndefinedInputValue",
286 MissingInterfaceField { .. } => "MissingInterfaceField",
287 RequiredArgument { .. } => "RequiredArgument",
288 RequiredField { .. } => "RequiredField",
289 TransitiveImplementedInterfaces { .. } => "TransitiveImplementedInterfaces",
290 OutputType { .. } => "OutputType",
291 InputType { .. } => "InputType",
292 VariableInputType { .. } => "VariableInputType",
293 QueryRootOperationType => "QueryRootOperationType",
294 UnusedVariable { .. } => "UnusedVariable",
295 RootOperationObjectType { .. } => "RootOperationObjectType",
296 DuplicateRootOperationType { .. } => "DuplicateRootOperationType",
297 UnionMemberObjectType { .. } => "UnionMemberObjectType",
298 UnsupportedLocation { .. } => "UnsupportedLocation",
299 UnsupportedValueType { .. } => "UnsupportedValueType",
300 IntCoercionError { .. } => "IntCoercionError",
301 FloatCoercionError { .. } => "FloatCoercionError",
302 UniqueDirective { .. } => "UniqueDirective",
303 MissingSubselection { .. } => "MissingSubselection",
304 InvalidFragmentTarget { .. } => "InvalidFragmentTarget",
305 InvalidFragmentSpread { .. } => "InvalidFragmentSpread",
306 UnusedFragment { .. } => "UnusedFragment",
307 DisallowedVariableUsage { .. } => "DisallowedVariableUsage",
308 RecursiveDirectiveDefinition { .. } => "RecursiveDirectiveDefinition",
309 RecursiveInterfaceDefinition { .. } => "RecursiveInterfaceDefinition",
310 RecursiveInputObjectDefinition { .. } => "RecursiveInputObjectDefinition",
311 RecursiveFragmentDefinition { .. } => "RecursiveFragmentDefinition",
312 DeeplyNestedType { .. } => "DeeplyNestedType",
313 EmptyFieldSet { .. } => "EmptyFieldSet",
314 EmptyValueSet { .. } => "EmptyValueSet",
315 EmptyMemberSet { .. } => "EmptyMemberSet",
316 EmptyInputValueSet { .. } => "EmptyInputValueSet",
317 ReservedName { .. } => "ReservedName",
318 OneOfInputObjectFieldNonNull { .. } => "OneOfInputObjectFieldNonNull",
319 OneOfInputObjectFieldCount { .. } => "OneOfInputObjectFieldCount",
320 UnsupportedDefault { .. } => "UnsupportedDefault",
321 OneOfDirectiveOnExtension { .. } => "OneOfDirectiveOnExtension",
322 DeprecatedRequiredInputValue { .. } => "DeprecatedRequiredInputValue",
323 DeprecatedImplementationField { .. } => "DeprecatedImplementationField",
324 InvalidImplementationFieldType { .. } => "InvalidImplementationFieldType",
325 MissingInterfaceFieldArgument { .. } => "MissingInterfaceFieldArgument",
326 InvalidImplementationFieldArgumentType { .. } => {
327 "InvalidImplementationFieldArgumentType"
328 }
329 ExtraRequiredImplementationFieldArgument { .. } => {
330 "ExtraRequiredImplementationFieldArgument"
331 }
332 })
333 }
334 Details::ExecutableBuildError(error) => Some(match error {
335 ExecutableBuildError::UndefinedField { .. } => "UndefinedField",
336 ExecutableBuildError::TypeSystemDefinition { .. } => "TypeSystemDefinition",
337 ExecutableBuildError::AmbiguousAnonymousOperation => "AmbiguousAnonymousOperation",
338 ExecutableBuildError::OperationNameCollision { .. } => "OperationNameCollision",
339 ExecutableBuildError::FragmentNameCollision { .. } => "FragmentNameCollision",
340 ExecutableBuildError::UndefinedRootOperation { .. } => "UndefinedRootOperation",
341 ExecutableBuildError::UndefinedTypeInNamedFragmentTypeCondition { .. } => {
342 "UndefinedTypeInNamedFragmentTypeCondition"
343 }
344 ExecutableBuildError::UndefinedTypeInInlineFragmentTypeCondition { .. } => {
345 "UndefinedTypeInInlineFragmentTypeCondition"
346 }
347 ExecutableBuildError::SubselectionOnScalarType { .. } => "SubselectionOnScalarType",
348 ExecutableBuildError::SubselectionOnEnumType { .. } => "SubselectionOnEnumType",
349 ExecutableBuildError::SubscriptionUsesMultipleFields { .. } => {
350 "SubscriptionUsesMultipleFields"
351 }
352 ExecutableBuildError::SubscriptionUsesIntrospection { .. } => {
353 "SubscriptionUsesIntrospection"
354 }
355 ExecutableBuildError::SubscriptionUsesConditionalSelection { .. } => {
356 "SubscriptionUsesConditionalSelection"
357 }
358 ExecutableBuildError::DuplicateDeferLabel { .. } => "DuplicateDeferLabel",
359 ExecutableBuildError::DeferLabelMustNotBeVariable => "DeferLabelMustNotBeVariable",
360 ExecutableBuildError::DeferOnRootMutationOrSubscriptionField { .. } => {
361 "DeferOnRootMutationOrSubscriptionField"
362 }
363 ExecutableBuildError::DeferInSubscriptionMustBeConditional => {
364 "DeferInSubscriptionMustBeConditional"
365 }
366 ExecutableBuildError::ConflictingFieldType(_) => "ConflictingFieldType",
367 ExecutableBuildError::ConflictingFieldName(_) => "ConflictingFieldName",
368 ExecutableBuildError::ConflictingFieldArgument(_) => "ConflictingFieldArgument",
369 }),
370 Details::RecursionLimitError => Some("RecursionLimitError"),
371 _ => None,
372 }
373 }
374
375 #[doc(hidden)]
380 pub fn unstable_compat_message(&self) -> Option<String> {
381 match &self.details {
382 Details::CompilerDiagnostic(diagnostic) => {
383 use diagnostics::DiagnosticData::*;
384 match diagnostic {
385 RecursionError { .. } => None,
386 UniqueVariable { name, .. } => Some(format!(
387 r#"There can be only one variable named "${name}"."#
388 )),
389 UniqueArgument { name, .. } => {
390 Some(format!(r#"There can be only one argument named "{name}"."#))
391 }
392 UniqueInputValue { .. } => None,
393 UndefinedArgument {
394 name, coordinate, ..
395 } => Some(format!(
396 r#"Unknown argument "{name}" on field "{coordinate}"."#
397 )),
398 UndefinedDefinition { name } => Some(format!(r#"Unknown type "{name}"."#)),
399 UndefinedDirective { name } => Some(format!(r#"Unknown directive "@{name}"."#)),
400 UndefinedVariable { name } => {
401 Some(format!(r#"Variable "${name}" is not defined."#))
402 }
403 UndefinedFragment { name } => Some(format!(r#"Unknown fragment "{name}"."#)),
404 UndefinedEnumValue {
405 value, definition, ..
406 } => Some(format!(
407 r#"Value "{value}" does not exist in "{definition}" enum."#
408 )),
409 UndefinedInputValue {
410 value, definition, ..
411 } => Some(format!(
412 r#"Field "{value}" is not defined by type "{definition}"."#
413 )),
414 MissingInterfaceField { .. } => None,
415 RequiredArgument {
416 name,
417 coordinate,
418 expected_type,
419 ..
420 } => match coordinate {
421 SchemaCoordinate::FieldArgument(coordinate) => Some(format!(
422 r#"Field "{}" argument "{name}" of type "{expected_type}" is required, but it was not provided."#,
423 coordinate.field,
424 )),
425 SchemaCoordinate::DirectiveArgument(coordinate) => Some(format!(
426 r#"Directive "@{}" argument "{name}" of type "{expected_type}" is required, but it was not provided."#,
427 coordinate.directive,
428 )),
429 _ => None,
431 },
432 RequiredField {
433 coordinate,
434 expected_type,
435 ..
436 } => Some(format!(
437 r#"Field "{coordinate}" of required type "{expected_type}" was not provided."#
438 )),
439 TransitiveImplementedInterfaces { .. } => None,
440 OutputType { .. } => None,
441 InputType { .. } => None,
442 VariableInputType { name, ty, .. } => Some(format!(
443 r#"Variable "${name}" cannot be non-input type "{ty}"."#
444 )),
445 QueryRootOperationType => None,
446 UnusedVariable { name } => {
447 Some(format!(r#"Variable "${name}" is never used."#))
448 }
449 RootOperationObjectType { .. } => None,
450 DuplicateRootOperationType { .. } => None,
451 UnionMemberObjectType { .. } => None,
452 UnsupportedLocation { name, location, .. } => Some(format!(
453 r#"Directive "@{name}" may not be used on {location}."#
454 )),
455 UnsupportedValueType { ty, value, .. } => Some(format!(
456 r#"{} cannot represent value: {value}"#,
457 ty.inner_named_type()
458 )),
459 IntCoercionError { value } => {
460 let is_integer = value
461 .chars()
462 .all(|c| matches!(c, '-' | '+' | 'e' | '0'..='9'));
464 if is_integer {
465 Some(format!(
466 r#"Int cannot represent non 32-bit signed integer value: {value}"#
467 ))
468 } else {
469 Some(format!(
470 r#"Int cannot represent non-integer value: {value}"#
471 ))
472 }
473 }
474 FloatCoercionError { value } => Some(format!(
475 r#"Float cannot represent non numeric value: {value}"#
476 )),
477 UniqueDirective { name, .. } => Some(format!(
478 r#"The directive "@{name}" can only be used once at this location."#
479 )),
480 MissingSubselection { coordinate, .. } => Some(format!(
481 r#"Field "{field}" of type "{ty}" must have a selection of subfields. Did you mean "{field} {{ ... }}"?"#,
482 ty = coordinate.ty,
483 field = coordinate.attribute,
484 )),
485 InvalidFragmentTarget { name, ty } => {
486 if let Some(name) = name {
487 Some(format!(
488 r#"Fragment "{name}" cannot condition on non composite type "{ty}"."#
489 ))
490 } else {
491 Some(format!(
492 r#"Fragment cannot condition on non composite type "{ty}"."#
493 ))
494 }
495 }
496 InvalidFragmentSpread {
497 name,
498 type_name,
499 type_condition,
500 ..
501 } => {
502 if let Some(name) = name {
503 Some(format!(
504 r#"Fragment "{name}" cannot be spread here as objects of type "{type_name}" can never be of type "{type_condition}"."#
505 ))
506 } else {
507 Some(format!(
508 r#"Fragment cannot be spread here as objects of type "{type_name}" can never be of type "{type_condition}"."#
509 ))
510 }
511 }
512 UnusedFragment { name } => Some(format!(r#"Fragment "{name}" is never used."#)),
513 DisallowedVariableUsage {
514 variable,
515 variable_type,
516 argument_type,
517 ..
518 } => Some(format!(
519 r#"Variable "${variable}" of type "{variable_type}" used in position expecting type "{argument_type}"."#
520 )),
521 RecursiveDirectiveDefinition { .. } => None,
522 RecursiveInterfaceDefinition { .. } => None,
523 RecursiveInputObjectDefinition { .. } => None,
524 RecursiveFragmentDefinition { name, trace, .. } => Some(format!(
525 r#"Cannot spread fragment "{name}" within itself via {}"#,
526 trace
528 .iter()
529 .map(|spread| format!(r#""{}""#, spread.fragment_name))
530 .collect::<Vec<_>>()
531 .join(", "),
532 )),
533 DeeplyNestedType { .. } => None,
534 EmptyFieldSet { .. } => None,
535 EmptyValueSet { .. } => None,
536 EmptyMemberSet { .. } => None,
537 EmptyInputValueSet { .. } => None,
538 ReservedName { .. } => None,
539 OneOfInputObjectFieldNonNull { coordinate, .. } => Some(format!(
540 r#"OneOf input field "{coordinate}" must be nullable."#
541 )),
542 OneOfInputObjectFieldCount { name, .. } => Some(format!(
543 r#"OneOf Input Object "{name}" must specify exactly one key."#
544 )),
545 UnsupportedDefault { coordinate, .. } => Some(format!(
546 r#"OneOf input field "{coordinate}" cannot have a default value."#
547 )),
548 OneOfDirectiveOnExtension { type_name, .. } => Some(format!(
549 r#"The @oneOf directive must not be provided by an input object type extension on "{type_name}"."#
550 )),
551 DeprecatedRequiredInputValue { .. } => None,
552 DeprecatedImplementationField { .. } => None,
553 InvalidImplementationFieldType {
554 name,
555 interface,
556 field,
557 interface_type,
558 actual_type,
559 ..
560 } => Some(format!(
561 r#"Interface field {interface}.{field} expects type {interface_type} but {name}.{field} of type {actual_type} is not a proper subtype."#
562 )),
563 MissingInterfaceFieldArgument {
564 name,
565 interface,
566 field,
567 argument,
568 ..
569 } => Some(format!(
570 r#"Interface field argument {interface}.{field}({argument}:) expected but {name}.{field} does not provide it."#
571 )),
572 InvalidImplementationFieldArgumentType {
573 name,
574 interface,
575 field,
576 argument,
577 interface_type,
578 actual_type,
579 ..
580 } => Some(format!(
581 r#"Interface field {interface}.{field}({argument}:) expects type {interface_type} but {name}.{field}({argument}:) is type {actual_type}."#
582 )),
583 ExtraRequiredImplementationFieldArgument {
584 name,
585 interface,
586 field,
587 argument,
588 ..
589 } => Some(format!(
590 r#"Object field {name}.{field} includes required argument {argument} that is missing from the Interface field {interface}.{field}."#
591 )),
592 }
593 }
594 Details::ExecutableBuildError(error) => match error {
595 ExecutableBuildError::UndefinedField {
596 type_name,
597 field_name,
598 ..
599 } => Some(format!(
600 r#"Cannot query field "{field_name}" on type "{type_name}"."#
601 )),
602 ExecutableBuildError::TypeSystemDefinition { name, .. } => {
603 if let Some(name) = name {
604 Some(format!(r#"The "{name}" definition is not executable."#))
605 } else {
606 Some("The schema definition is not executable.".to_string())
608 }
609 }
610 ExecutableBuildError::AmbiguousAnonymousOperation => {
611 Some("This anonymous operation must be the only defined operation.".to_string())
612 }
613 ExecutableBuildError::OperationNameCollision {
614 name_at_previous_location,
615 } => Some(format!(
616 r#"There can be only one operation named "{name_at_previous_location}"."#
617 )),
618 ExecutableBuildError::FragmentNameCollision {
619 name_at_previous_location,
620 } => Some(format!(
621 r#"There can be only one fragment named "{name_at_previous_location}"."#
622 )),
623 ExecutableBuildError::UndefinedRootOperation { operation_type } => Some(format!(
624 r#"The schema has no "{operation_type}" root type defined"#
626 )),
627 ExecutableBuildError::UndefinedTypeInNamedFragmentTypeCondition {
628 type_name,
629 ..
630 }
631 | ExecutableBuildError::UndefinedTypeInInlineFragmentTypeCondition {
632 type_name,
633 ..
634 } => Some(format!(r#"Unknown type "{type_name}"."#)),
635 ExecutableBuildError::SubselectionOnScalarType { type_name, path }
636 | ExecutableBuildError::SubselectionOnEnumType { type_name, path } => {
637 #[allow(clippy::manual_map)]
638 if let Some(field) = path.nested_fields.last() {
639 Some(format!(
640 r#"Field "{field}" must not have a selection since type "{type_name}" has no subfields"#
641 ))
642 } else {
643 None }
645 }
646 ExecutableBuildError::SubscriptionUsesMultipleFields { name, .. } => {
647 if let Some(name) = name {
648 Some(format!(
649 r#"Subscription "{name}" must select only one top level field."#
650 ))
651 } else {
652 Some(
653 "Anonymous Subscription must select only one top level field."
654 .to_string(),
655 )
656 }
657 }
658 ExecutableBuildError::SubscriptionUsesIntrospection { name, .. } => {
659 if let Some(name) = name {
660 Some(format!(
661 r#"Subscription "{name}" must not select an introspection top level field."#
662 ))
663 } else {
664 Some("Anonymous Subscription must not select an introspection top level field."
665 .to_string())
666 }
667 }
668 ExecutableBuildError::SubscriptionUsesConditionalSelection { name, .. } => {
669 if let Some(name) = name {
670 Some(format!(
671 r#"Subscription "{name}" can not specify @skip or @include on root fields."#
672 ))
673 } else {
674 Some(
675 "Anonymous Subscription can not specify @skip or @include on root fields."
676 .to_string(),
677 )
678 }
679 }
680 ExecutableBuildError::ConflictingFieldType(inner) => {
681 let ConflictingFieldType {
682 alias,
683 original_type,
684 conflicting_type,
685 ..
686 } = &**inner;
687 Some(format!(
688 r#"Fields "{alias}" conflict because they return conflicting types "{original_type} and "{conflicting_type}". Use different aliases on the fields to fetch both if this was intentional."#
689 ))
690 }
691 ExecutableBuildError::ConflictingFieldName(inner) => {
692 let ConflictingFieldName {
693 alias,
694 original_selection,
695 conflicting_selection,
696 ..
697 } = &**inner;
698 Some(format!(
699 r#"Fields "{alias}" conflict because "{}" and "{}" are different fields. Use different aliases on the fields to fetch both if this was intentional."#,
700 original_selection.attribute, conflicting_selection.attribute
701 ))
702 }
703 ExecutableBuildError::ConflictingFieldArgument(inner) => {
704 let ConflictingFieldArgument { alias, .. } = &**inner;
705 Some(format!(
706 r#"Fields "{alias}" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional."#
707 ))
708 }
709 ExecutableBuildError::DuplicateDeferLabel { .. } => Some(
710 r#"Value for arguments "defer(label:)" and "stream(label:)" must be unique across all Defer/Stream directive usages."#
711 .to_string(),
712 ),
713 ExecutableBuildError::DeferLabelMustNotBeVariable => Some(
714 r#"Argument "@defer(label:)" must be a static string."#.to_string(),
715 ),
716 ExecutableBuildError::DeferOnRootMutationOrSubscriptionField {
717 operation_type,
718 } => Some(format!(
719 r#"Defer directive cannot be used on root {} type."#,
720 operation_type.name()
721 )),
722 ExecutableBuildError::DeferInSubscriptionMustBeConditional => Some(
723 "Defer directive not supported on subscription operations. Disable `@defer` by setting the `if` argument to `false`.".to_string(),
724 ),
725 },
726 _ => None,
727 }
728 }
729}
730
731impl ToCliReport for DiagnosticData {
732 fn location(&self) -> Option<SourceSpan> {
733 self.location
734 }
735
736 fn report(&self, report: &mut CliReport) {
737 if let Details::CompilerDiagnostic(diagnostic) = &self.details {
738 diagnostic.report(self.location, report);
739 return;
740 }
741
742 match &self.details {
748 Details::CompilerDiagnostic(_) => unreachable!(),
749 Details::ParserLimit { message, .. } => report.with_label_opt(self.location, message),
750 Details::SyntaxError { message, .. } => report.with_label_opt(self.location, message),
751 Details::SchemaBuildError(err) => match err {
752 SchemaBuildError::ExecutableDefinition { .. } => report.with_label_opt(
753 self.location,
754 "remove this definition, or use `parse_mixed()`",
755 ),
756 SchemaBuildError::SchemaDefinitionCollision {
757 previous_location, ..
758 } => {
759 report.with_label_opt(*previous_location, "previous `schema` definition here");
760 report.with_label_opt(self.location, "`schema` redefined here");
761 report.with_help(
762 "merge this definition with the previous one, or use `extend schema`",
763 );
764 }
765 SchemaBuildError::DirectiveDefinitionCollision {
766 previous_location,
767 name,
768 ..
769 } => {
770 report.with_label_opt(
771 *previous_location,
772 format_args!("previous definition of `@{name}` here"),
773 );
774 report.with_label_opt(self.location, format_args!("`@{name}` redefined here"));
775 report.with_help("remove or rename one of the definitions");
776 }
777 SchemaBuildError::TypeDefinitionCollision {
778 previous_location,
779 name,
780 ..
781 } => {
782 report.with_label_opt(
783 *previous_location,
784 format_args!("previous definition of `{name}` here"),
785 );
786 report.with_label_opt(self.location, format_args!("`{name}` redefined here"));
787 report.with_help("remove or rename one of the definitions, or use `extend`");
788 }
789 SchemaBuildError::BuiltInScalarTypeRedefinition => {
790 report.with_label_opt(self.location, "remove this scalar definition");
791 }
792 SchemaBuildError::OrphanSchemaExtension => {
793 report.with_label_opt(self.location, "extension here")
794 }
795 SchemaBuildError::OrphanTypeExtension { .. } => {
796 report.with_label_opt(self.location, "extension here")
797 }
798 SchemaBuildError::TypeExtensionKindMismatch { def_location, .. } => {
799 report.with_label_opt(*def_location, "type definition");
800 report.with_label_opt(self.location, "extension here")
801 }
802 SchemaBuildError::DuplicateRootOperation {
803 previous_location,
804 operation_type,
805 ..
806 } => {
807 report.with_label_opt(
808 *previous_location,
809 format_args!("previous definition of `{operation_type}` here"),
810 );
811 report.with_label_opt(
812 self.location,
813 format_args!("`{operation_type}` redefined here"),
814 );
815 }
816 SchemaBuildError::DuplicateImplementsInterfaceInObject {
817 name_at_previous_location,
818 ..
819 }
820 | SchemaBuildError::DuplicateImplementsInterfaceInInterface {
821 name_at_previous_location,
822 ..
823 } => {
824 let previous_location = &name_at_previous_location.location();
825 let name = name_at_previous_location;
826 report.with_label_opt(
827 *previous_location,
828 format_args!("previous implementation of `{name}` here"),
829 );
830 report.with_label_opt(
831 self.location,
832 format_args!("`{name}` implemented again here"),
833 );
834 }
835 SchemaBuildError::ObjectFieldNameCollision {
836 name_at_previous_location,
837 ..
838 }
839 | SchemaBuildError::InterfaceFieldNameCollision {
840 name_at_previous_location,
841 ..
842 }
843 | SchemaBuildError::EnumValueNameCollision {
844 name_at_previous_location,
845 ..
846 }
847 | SchemaBuildError::UnionMemberNameCollision {
848 name_at_previous_location,
849 ..
850 }
851 | SchemaBuildError::InputFieldNameCollision {
852 name_at_previous_location,
853 ..
854 } => {
855 let previous_location = &name_at_previous_location.location();
856 let name = name_at_previous_location;
857 report.with_label_opt(
858 *previous_location,
859 format_args!("previous definition of `{name}` here"),
860 );
861 report.with_label_opt(self.location, format_args!("`{name}` redefined here"));
862 }
863 },
864 Details::ExecutableBuildError(err) => match err {
865 ExecutableBuildError::TypeSystemDefinition { .. } => report.with_label_opt(
866 self.location,
867 "remove this definition, or use `parse_mixed()`",
868 ),
869 ExecutableBuildError::AmbiguousAnonymousOperation => {
870 report.with_label_opt(self.location, "provide a name for this definition");
871 report.with_help(
872 "GraphQL requires operations to be named if the document has more than one",
873 );
874 }
875 ExecutableBuildError::OperationNameCollision {
876 name_at_previous_location,
877 ..
878 }
879 | ExecutableBuildError::FragmentNameCollision {
880 name_at_previous_location,
881 ..
882 } => {
883 let previous_location = &name_at_previous_location.location();
884 let name = name_at_previous_location;
885 report.with_label_opt(
886 *previous_location,
887 format_args!("previous definition of `{name}` here"),
888 );
889 report.with_label_opt(self.location, format_args!("`{name}` redefined here"));
890 }
891 ExecutableBuildError::UndefinedRootOperation { operation_type, .. } => {
892 report.with_label_opt(
893 self.location,
894 format_args!(
895 "`{operation_type}` is not defined in the schema and is therefore not supported"
896 ),
897 );
898 report.with_help(format_args!(
899 "consider defining a `{operation_type}` root operation type in your schema"
900 ))
901 }
902 ExecutableBuildError::UndefinedTypeInNamedFragmentTypeCondition { .. } => {
903 report.with_label_opt(self.location, "type condition here")
904 }
905 ExecutableBuildError::UndefinedTypeInInlineFragmentTypeCondition {
906 path, ..
907 } => {
908 report.with_label_opt(self.location, "type condition here");
909 report.with_note(format_args!("path to the inline fragment: `{path} → ...`"))
910 }
911 ExecutableBuildError::SubselectionOnScalarType { path, .. }
912 | ExecutableBuildError::SubselectionOnEnumType { path, .. } => {
913 report.with_label_opt(self.location, "remove subselections here");
914 report.with_note(format_args!("path to the field: `{path}`"))
915 }
916 ExecutableBuildError::UndefinedField {
917 field_name,
918 type_name,
919 path,
920 ..
921 } => {
922 report.with_label_opt(
923 self.location,
924 format_args!("field `{field_name}` selected here"),
925 );
926 report.with_label_opt(
927 type_name.location(),
928 format_args!("type `{type_name}` defined here"),
929 );
930 report.with_note(format_args!("path to the field: `{path}`"))
931 }
932 ExecutableBuildError::SubscriptionUsesMultipleFields { fields, .. } => {
933 report.with_label_opt(
934 self.location,
935 format_args!("subscription with {} root fields", fields.len()),
936 );
937 report.with_help(format_args!(
938 "There are {} root fields: {}. This is not allowed.",
939 fields.len(),
940 CommaSeparated(fields)
941 ));
942 }
943 ExecutableBuildError::SubscriptionUsesIntrospection { field, .. } => {
944 report.with_label_opt(
945 self.location,
946 format_args!("{field} is an introspection field"),
947 );
948 }
949 ExecutableBuildError::SubscriptionUsesConditionalSelection { .. } => {
950 report.with_label_opt(self.location, "conditional directive used here");
951 }
952 ExecutableBuildError::DuplicateDeferLabel {
953 original_location, ..
954 } => {
955 report.with_label_opt(*original_location, "label first used here");
956 report.with_label_opt(self.location, "duplicate label used here");
957 }
958 ExecutableBuildError::DeferLabelMustNotBeVariable => {
959 report.with_label_opt(self.location, "variable used as label here");
960 }
961 ExecutableBuildError::DeferOnRootMutationOrSubscriptionField { operation_type } => {
962 report.with_label_opt(
963 self.location,
964 format_args!("`@defer` on a root {} selection", operation_type.name()),
965 );
966 }
967 ExecutableBuildError::DeferInSubscriptionMustBeConditional => {
968 report.with_label_opt(
969 self.location,
970 "`@defer` in a subscription must use `if: false` or `if: $variable`",
971 );
972 }
973 ExecutableBuildError::ConflictingFieldType(inner) => {
974 let ConflictingFieldType {
975 alias,
976 original_location,
977 original_coordinate,
978 original_type,
979 conflicting_location,
980 conflicting_coordinate,
981 conflicting_type,
982 } = &**inner;
983 report.with_label_opt(
984 *original_location,
985 format_args!(
986 "`{alias}` is selected from `{original_coordinate}: {original_type}` here"
987 ),
988 );
989 report.with_label_opt(
990 *conflicting_location,
991 format_args!("`{alias}` is selected from `{conflicting_coordinate}: {conflicting_type}` here"),
992 );
993 }
994 ExecutableBuildError::ConflictingFieldArgument(inner) => {
995 let ConflictingFieldArgument {
996 alias,
997 original_location,
998 original_coordinate,
999 original_value,
1000 conflicting_location,
1001 conflicting_coordinate: _,
1002 conflicting_value,
1003 } = &**inner;
1004 let argument = &original_coordinate.argument;
1005 match (original_value, conflicting_value) {
1006 (Some(_), Some(_)) => {
1007 report.with_label_opt(
1008 *original_location,
1009 format_args!(
1010 "`{original_coordinate}` is used with one argument value here"
1011 ),
1012 );
1013 report.with_label_opt(
1014 *conflicting_location,
1015 "but a different value here",
1016 );
1017 }
1018 (Some(_), None) => {
1019 report.with_label_opt(
1020 *original_location,
1021 format!("`{alias}` is selected with argument `{argument}` here",),
1022 );
1023 report.with_label_opt(
1024 *conflicting_location,
1025 format!("but argument `{argument}` is not provided here"),
1026 );
1027 }
1028 (None, Some(_)) => {
1029 report.with_label_opt(
1030 *conflicting_location,
1031 format!("`{alias}` is selected with argument `{argument}` here",),
1032 );
1033 report.with_label_opt(
1034 *original_location,
1035 format!("but argument `{argument}` is not provided here"),
1036 );
1037 }
1038 (None, None) => unreachable!(),
1039 }
1040 report.with_help("The same name cannot be selected multiple times with different arguments, because it's not clear which set of arguments should be used to fill the response. If you intend to use diverging arguments, consider adding an alias to differentiate");
1041 }
1042 ExecutableBuildError::ConflictingFieldName(inner) => {
1043 let ConflictingFieldName {
1044 alias: field,
1045 original_selection,
1046 original_location,
1047 conflicting_selection,
1048 conflicting_location,
1049 } = &**inner;
1050 report.with_label_opt(
1051 *original_location,
1052 format_args!("`{field}` is selected from `{original_selection}` here"),
1053 );
1054 report.with_label_opt(
1055 *conflicting_location,
1056 format_args!("`{field}` is selected from `{conflicting_selection}` here"),
1057 );
1058
1059 report.with_help("Both fields may be present on the schema type, so it's not clear which one should be used to fill the response");
1060 }
1061 },
1062 Details::RecursionLimitError => {}
1063 }
1064 }
1065}
1066
1067impl Diagnostic<'_, DiagnosticData> {
1068 #[doc(hidden)]
1073 pub fn unstable_to_json_compat(&self) -> GraphQLError {
1074 GraphQLError::new(
1075 self.error
1076 .unstable_compat_message()
1077 .unwrap_or_else(|| self.error.to_string()),
1078 self.error.location(),
1079 self.sources,
1080 )
1081 }
1082}
1083
1084impl DiagnosticList {
1085 pub fn new(sources: SourceMap) -> Self {
1087 Self {
1088 sources,
1089 diagnostics_data: Vec::new(),
1090 }
1091 }
1092
1093 pub fn is_empty(&self) -> bool {
1094 self.diagnostics_data.is_empty()
1095 }
1096
1097 pub fn len(&self) -> usize {
1098 self.diagnostics_data.len()
1099 }
1100
1101 pub fn iter(
1102 &self,
1103 ) -> impl DoubleEndedIterator<Item = Diagnostic<'_, DiagnosticData>> + ExactSizeIterator {
1104 self.diagnostics_data
1105 .iter()
1106 .map(|data| data.to_diagnostic(&self.sources))
1107 }
1108
1109 pub(crate) fn push(&mut self, location: Option<SourceSpan>, details: impl Into<Details>) {
1110 self.diagnostics_data.push(DiagnosticData {
1111 location,
1112 details: details.into(),
1113 })
1114 }
1115
1116 pub fn merge(&mut self, other: Self) {
1118 if !Arc::ptr_eq(&self.sources, &other.sources) {
1119 let sources = Arc::make_mut(&mut self.sources);
1120 for (&k, v) in &*other.sources {
1121 sources.entry(k).or_insert_with(|| v.clone());
1122 }
1123 }
1124 self.diagnostics_data.extend(other.diagnostics_data);
1125 self.sort()
1126 }
1127
1128 fn sort(&mut self) {
1129 self.diagnostics_data
1130 .sort_by_key(|err| err.location.map(|loc| (loc.file_id(), loc.offset())));
1131 }
1132
1133 pub(crate) fn into_result(mut self) -> Result<(), Self> {
1134 if self.diagnostics_data.is_empty() {
1135 Ok(())
1136 } else {
1137 self.sort();
1138 Err(self)
1139 }
1140 }
1141
1142 pub(crate) fn into_result_with<T>(self, value: T) -> Result<T, WithErrors<T>> {
1143 match self.into_result() {
1144 Ok(()) => Ok(value),
1145 Err(errors) => Err(WithErrors {
1146 partial: value,
1147 errors,
1148 }),
1149 }
1150 }
1151
1152 pub(crate) fn into_valid_result<T>(self, value: T) -> Result<Valid<T>, WithErrors<T>> {
1153 match self.into_result() {
1154 Ok(()) => Ok(Valid(value)),
1155 Err(errors) => Err(WithErrors {
1156 partial: value,
1157 errors,
1158 }),
1159 }
1160 }
1161}
1162
1163impl fmt::Display for DiagnosticList {
1165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1166 for diagnostic in self.iter() {
1167 fmt::Display::fmt(&diagnostic, f)?
1168 }
1169 Ok(())
1170 }
1171}
1172
1173impl fmt::Debug for DiagnosticList {
1175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1176 for diagnostic in self.iter() {
1177 fmt::Debug::fmt(&diagnostic, f)?
1178 }
1179 Ok(())
1180 }
1181}
1182
1183impl From<SchemaBuildError> for Details {
1184 fn from(value: SchemaBuildError) -> Self {
1185 Details::SchemaBuildError(value)
1186 }
1187}
1188
1189impl From<ExecutableBuildError> for Details {
1190 fn from(value: ExecutableBuildError) -> Self {
1191 Details::ExecutableBuildError(value)
1192 }
1193}
1194
1195impl From<diagnostics::DiagnosticData> for Details {
1196 fn from(value: diagnostics::DiagnosticData) -> Self {
1197 Details::CompilerDiagnostic(value)
1198 }
1199}
1200
1201const DEFAULT_RECURSION_LIMIT: usize = 32;
1202
1203#[derive(Debug, Clone, thiserror::Error)]
1204#[error("Recursion limit reached")]
1205#[non_exhaustive]
1206struct RecursionLimitError {}
1207
1208#[derive(Debug)]
1210struct DepthCounter {
1211 value: usize,
1212 high: usize,
1213 limit: usize,
1214}
1215
1216impl DepthCounter {
1217 fn new() -> Self {
1218 Self {
1219 value: 0,
1220 high: 0,
1221 limit: DEFAULT_RECURSION_LIMIT,
1222 }
1223 }
1224
1225 fn with_limit(mut self, limit: usize) -> Self {
1226 self.limit = limit;
1227 self
1228 }
1229
1230 pub(crate) fn guard(&mut self) -> DepthGuard<'_> {
1232 DepthGuard(self)
1233 }
1234}
1235
1236struct DepthGuard<'a>(&'a mut DepthCounter);
1241
1242impl DepthGuard<'_> {
1243 fn increment(&mut self) -> Result<DepthGuard<'_>, RecursionLimitError> {
1245 self.0.value += 1;
1246 self.0.high = self.0.high.max(self.0.value);
1247 if self.0.value > self.0.limit {
1248 Err(RecursionLimitError {})
1249 } else {
1250 Ok(DepthGuard(self.0))
1251 }
1252 }
1253}
1254
1255impl Drop for DepthGuard<'_> {
1256 fn drop(&mut self) {
1257 self.0.value = self.0.value.saturating_sub(1);
1259 }
1260}
1261
1262#[derive(Debug)]
1264struct RecursionStack {
1265 seen: IndexSet<Name>,
1266 high: usize,
1267 limit: usize,
1268}
1269
1270impl RecursionStack {
1271 fn new() -> Self {
1272 Self {
1273 seen: IndexSet::with_hasher(Default::default()),
1274 high: 0,
1275 limit: DEFAULT_RECURSION_LIMIT,
1276 }
1277 }
1278
1279 fn with_root(root: Name) -> Self {
1280 let mut stack = Self::new();
1281 stack.seen.insert(root);
1282 stack
1283 }
1284
1285 fn with_limit(mut self, limit: usize) -> Self {
1286 self.limit = limit;
1287 self
1288 }
1289
1290 pub(crate) fn guard(&mut self) -> RecursionGuard<'_> {
1292 RecursionGuard(self)
1293 }
1294}
1295
1296struct RecursionGuard<'a>(&'a mut RecursionStack);
1302
1303impl RecursionGuard<'_> {
1304 fn push(&mut self, name: &Name) -> Result<RecursionGuard<'_>, RecursionLimitError> {
1306 let new = self.0.seen.insert(name.clone());
1307 debug_assert!(
1308 new,
1309 "cannot push the same name twice to RecursionGuard, check contains() first"
1310 );
1311 self.0.high = self.0.high.max(self.0.seen.len());
1312 if self.0.seen.len() > self.0.limit {
1313 Err(RecursionLimitError {})
1314 } else {
1315 Ok(RecursionGuard(self.0))
1316 }
1317 }
1318
1319 fn contains(&self, name: &Name) -> bool {
1321 self.0.seen.contains(name)
1322 }
1323
1324 fn first(&self) -> Option<&Name> {
1326 self.0.seen.first()
1327 }
1328}
1329
1330impl Drop for RecursionGuard<'_> {
1331 fn drop(&mut self) {
1332 let _ = self.0.seen.pop();
1334 }
1335}
1336
1337#[derive(Debug, Clone, thiserror::Error)]
1339enum CycleError<T> {
1340 #[error("Cycle detected")]
1343 Recursed(Vec<Node<T>>),
1344 #[error(transparent)]
1346 Limit(#[from] RecursionLimitError),
1347}
1348
1349impl<T> CycleError<T> {
1350 fn trace(mut self, node: &Node<T>) -> Self {
1351 if let Self::Recursed(trace) = &mut self {
1352 trace.push(node.clone());
1353 }
1354 self
1355 }
1356}
1357
1358struct CommaSeparated<'a, It>(&'a It);
1359impl<'a, T, It> fmt::Display for CommaSeparated<'a, It>
1360where
1361 T: fmt::Display,
1362 &'a It: IntoIterator<Item = T>,
1363{
1364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1365 let mut it = self.0.into_iter();
1366 if let Some(element) = it.next() {
1367 element.fmt(f)?;
1368 }
1369 for element in it {
1370 f.write_str(", ")?;
1371 element.fmt(f)?;
1372 }
1373 Ok(())
1374 }
1375}