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 InvalidImplementationFieldType { .. } => "InvalidImplementationFieldType",
319 MissingInterfaceFieldArgument { .. } => "MissingInterfaceFieldArgument",
320 InvalidImplementationFieldArgumentType { .. } => {
321 "InvalidImplementationFieldArgumentType"
322 }
323 ExtraRequiredImplementationFieldArgument { .. } => {
324 "ExtraRequiredImplementationFieldArgument"
325 }
326 })
327 }
328 Details::ExecutableBuildError(error) => Some(match error {
329 ExecutableBuildError::UndefinedField { .. } => "UndefinedField",
330 ExecutableBuildError::TypeSystemDefinition { .. } => "TypeSystemDefinition",
331 ExecutableBuildError::AmbiguousAnonymousOperation => "AmbiguousAnonymousOperation",
332 ExecutableBuildError::OperationNameCollision { .. } => "OperationNameCollision",
333 ExecutableBuildError::FragmentNameCollision { .. } => "FragmentNameCollision",
334 ExecutableBuildError::UndefinedRootOperation { .. } => "UndefinedRootOperation",
335 ExecutableBuildError::UndefinedTypeInNamedFragmentTypeCondition { .. } => {
336 "UndefinedTypeInNamedFragmentTypeCondition"
337 }
338 ExecutableBuildError::UndefinedTypeInInlineFragmentTypeCondition { .. } => {
339 "UndefinedTypeInInlineFragmentTypeCondition"
340 }
341 ExecutableBuildError::SubselectionOnScalarType { .. } => "SubselectionOnScalarType",
342 ExecutableBuildError::SubselectionOnEnumType { .. } => "SubselectionOnEnumType",
343 ExecutableBuildError::SubscriptionUsesMultipleFields { .. } => {
344 "SubscriptionUsesMultipleFields"
345 }
346 ExecutableBuildError::SubscriptionUsesIntrospection { .. } => {
347 "SubscriptionUsesIntrospection"
348 }
349 ExecutableBuildError::SubscriptionUsesConditionalSelection { .. } => {
350 "SubscriptionUsesConditionalSelection"
351 }
352 ExecutableBuildError::DuplicateDeferLabel { .. } => "DuplicateDeferLabel",
353 ExecutableBuildError::DeferLabelMustNotBeVariable => "DeferLabelMustNotBeVariable",
354 ExecutableBuildError::DeferOnRootMutationOrSubscriptionField { .. } => {
355 "DeferOnRootMutationOrSubscriptionField"
356 }
357 ExecutableBuildError::DeferInSubscriptionMustBeConditional => {
358 "DeferInSubscriptionMustBeConditional"
359 }
360 ExecutableBuildError::ConflictingFieldType(_) => "ConflictingFieldType",
361 ExecutableBuildError::ConflictingFieldName(_) => "ConflictingFieldName",
362 ExecutableBuildError::ConflictingFieldArgument(_) => "ConflictingFieldArgument",
363 }),
364 Details::RecursionLimitError => Some("RecursionLimitError"),
365 _ => None,
366 }
367 }
368
369 #[doc(hidden)]
374 pub fn unstable_compat_message(&self) -> Option<String> {
375 match &self.details {
376 Details::CompilerDiagnostic(diagnostic) => {
377 use diagnostics::DiagnosticData::*;
378 match diagnostic {
379 RecursionError { .. } => None,
380 UniqueVariable { name, .. } => Some(format!(
381 r#"There can be only one variable named "${name}"."#
382 )),
383 UniqueArgument { name, .. } => {
384 Some(format!(r#"There can be only one argument named "{name}"."#))
385 }
386 UniqueInputValue { .. } => None,
387 UndefinedArgument {
388 name, coordinate, ..
389 } => Some(format!(
390 r#"Unknown argument "{name}" on field "{coordinate}"."#
391 )),
392 UndefinedDefinition { name } => Some(format!(r#"Unknown type "{name}"."#)),
393 UndefinedDirective { name } => Some(format!(r#"Unknown directive "@{name}"."#)),
394 UndefinedVariable { name } => {
395 Some(format!(r#"Variable "${name}" is not defined."#))
396 }
397 UndefinedFragment { name } => Some(format!(r#"Unknown fragment "{name}"."#)),
398 UndefinedEnumValue {
399 value, definition, ..
400 } => Some(format!(
401 r#"Value "{value}" does not exist in "{definition}" enum."#
402 )),
403 UndefinedInputValue {
404 value, definition, ..
405 } => Some(format!(
406 r#"Field "{value}" is not defined by type "{definition}"."#
407 )),
408 MissingInterfaceField { .. } => None,
409 RequiredArgument {
410 name,
411 coordinate,
412 expected_type,
413 ..
414 } => match coordinate {
415 SchemaCoordinate::FieldArgument(coordinate) => Some(format!(
416 r#"Field "{}" argument "{name}" of type "{expected_type}" is required, but it was not provided."#,
417 coordinate.field,
418 )),
419 SchemaCoordinate::DirectiveArgument(coordinate) => Some(format!(
420 r#"Directive "@{}" argument "{name}" of type "{expected_type}" is required, but it was not provided."#,
421 coordinate.directive,
422 )),
423 _ => None,
425 },
426 RequiredField {
427 coordinate,
428 expected_type,
429 ..
430 } => Some(format!(
431 r#"Field "{coordinate}" of required type "{expected_type}" was not provided."#
432 )),
433 TransitiveImplementedInterfaces { .. } => None,
434 OutputType { .. } => None,
435 InputType { .. } => None,
436 VariableInputType { name, ty, .. } => Some(format!(
437 r#"Variable "${name}" cannot be non-input type "{ty}"."#
438 )),
439 QueryRootOperationType => None,
440 UnusedVariable { name } => {
441 Some(format!(r#"Variable "${name}" is never used."#))
442 }
443 RootOperationObjectType { .. } => None,
444 DuplicateRootOperationType { .. } => None,
445 UnionMemberObjectType { .. } => None,
446 UnsupportedLocation { name, location, .. } => Some(format!(
447 r#"Directive "@{name}" may not be used on {location}."#
448 )),
449 UnsupportedValueType { ty, value, .. } => Some(format!(
450 r#"{} cannot represent value: {value}"#,
451 ty.inner_named_type()
452 )),
453 IntCoercionError { value } => {
454 let is_integer = value
455 .chars()
456 .all(|c| matches!(c, '-' | '+' | 'e' | '0'..='9'));
458 if is_integer {
459 Some(format!(
460 r#"Int cannot represent non 32-bit signed integer value: {value}"#
461 ))
462 } else {
463 Some(format!(
464 r#"Int cannot represent non-integer value: {value}"#
465 ))
466 }
467 }
468 FloatCoercionError { value } => Some(format!(
469 r#"Float cannot represent non numeric value: {value}"#
470 )),
471 UniqueDirective { name, .. } => Some(format!(
472 r#"The directive "@{name}" can only be used once at this location."#
473 )),
474 MissingSubselection { coordinate, .. } => Some(format!(
475 r#"Field "{field}" of type "{ty}" must have a selection of subfields. Did you mean "{field} {{ ... }}"?"#,
476 ty = coordinate.ty,
477 field = coordinate.attribute,
478 )),
479 InvalidFragmentTarget { name, ty } => {
480 if let Some(name) = name {
481 Some(format!(
482 r#"Fragment "{name}" cannot condition on non composite type "{ty}"."#
483 ))
484 } else {
485 Some(format!(
486 r#"Fragment cannot condition on non composite type "{ty}"."#
487 ))
488 }
489 }
490 InvalidFragmentSpread {
491 name,
492 type_name,
493 type_condition,
494 ..
495 } => {
496 if let Some(name) = name {
497 Some(format!(
498 r#"Fragment "{name}" cannot be spread here as objects of type "{type_name}" can never be of type "{type_condition}"."#
499 ))
500 } else {
501 Some(format!(
502 r#"Fragment cannot be spread here as objects of type "{type_name}" can never be of type "{type_condition}"."#
503 ))
504 }
505 }
506 UnusedFragment { name } => Some(format!(r#"Fragment "{name}" is never used."#)),
507 DisallowedVariableUsage {
508 variable,
509 variable_type,
510 argument_type,
511 ..
512 } => Some(format!(
513 r#"Variable "${variable}" of type "{variable_type}" used in position expecting type "{argument_type}"."#
514 )),
515 RecursiveDirectiveDefinition { .. } => None,
516 RecursiveInterfaceDefinition { .. } => None,
517 RecursiveInputObjectDefinition { .. } => None,
518 RecursiveFragmentDefinition { name, trace, .. } => Some(format!(
519 r#"Cannot spread fragment "{name}" within itself via {}"#,
520 trace
522 .iter()
523 .map(|spread| format!(r#""{}""#, spread.fragment_name))
524 .collect::<Vec<_>>()
525 .join(", "),
526 )),
527 DeeplyNestedType { .. } => None,
528 EmptyFieldSet { .. } => None,
529 EmptyValueSet { .. } => None,
530 EmptyMemberSet { .. } => None,
531 EmptyInputValueSet { .. } => None,
532 ReservedName { .. } => None,
533 InvalidImplementationFieldType {
534 name,
535 interface,
536 field,
537 interface_type,
538 actual_type,
539 ..
540 } => Some(format!(
541 r#"Interface field {interface}.{field} expects type {interface_type} but {name}.{field} of type {actual_type} is not a proper subtype."#
542 )),
543 MissingInterfaceFieldArgument {
544 name,
545 interface,
546 field,
547 argument,
548 ..
549 } => Some(format!(
550 r#"Interface field argument {interface}.{field}({argument}:) expected but {name}.{field} does not provide it."#
551 )),
552 InvalidImplementationFieldArgumentType {
553 name,
554 interface,
555 field,
556 argument,
557 interface_type,
558 actual_type,
559 ..
560 } => Some(format!(
561 r#"Interface field {interface}.{field}({argument}:) expects type {interface_type} but {name}.{field}({argument}:) is type {actual_type}."#
562 )),
563 ExtraRequiredImplementationFieldArgument {
564 name,
565 interface,
566 field,
567 argument,
568 ..
569 } => Some(format!(
570 r#"Object field {name}.{field} includes required argument {argument} that is missing from the Interface field {interface}.{field}."#
571 )),
572 }
573 }
574 Details::ExecutableBuildError(error) => match error {
575 ExecutableBuildError::UndefinedField {
576 type_name,
577 field_name,
578 ..
579 } => Some(format!(
580 r#"Cannot query field "{field_name}" on type "{type_name}"."#
581 )),
582 ExecutableBuildError::TypeSystemDefinition { name, .. } => {
583 if let Some(name) = name {
584 Some(format!(r#"The "{name}" definition is not executable."#))
585 } else {
586 Some("The schema definition is not executable.".to_string())
588 }
589 }
590 ExecutableBuildError::AmbiguousAnonymousOperation => {
591 Some("This anonymous operation must be the only defined operation.".to_string())
592 }
593 ExecutableBuildError::OperationNameCollision {
594 name_at_previous_location,
595 } => Some(format!(
596 r#"There can be only one operation named "{name_at_previous_location}"."#
597 )),
598 ExecutableBuildError::FragmentNameCollision {
599 name_at_previous_location,
600 } => Some(format!(
601 r#"There can be only one fragment named "{name_at_previous_location}"."#
602 )),
603 ExecutableBuildError::UndefinedRootOperation { operation_type } => Some(format!(
604 r#"The schema has no "{operation_type}" root type defined"#
606 )),
607 ExecutableBuildError::UndefinedTypeInNamedFragmentTypeCondition {
608 type_name,
609 ..
610 }
611 | ExecutableBuildError::UndefinedTypeInInlineFragmentTypeCondition {
612 type_name,
613 ..
614 } => Some(format!(r#"Unknown type "{type_name}"."#)),
615 ExecutableBuildError::SubselectionOnScalarType { type_name, path }
616 | ExecutableBuildError::SubselectionOnEnumType { type_name, path } => {
617 #[allow(clippy::manual_map)]
618 if let Some(field) = path.nested_fields.last() {
619 Some(format!(
620 r#"Field "{field}" must not have a selection since type "{type_name}" has no subfields"#
621 ))
622 } else {
623 None }
625 }
626 ExecutableBuildError::SubscriptionUsesMultipleFields { name, .. } => {
627 if let Some(name) = name {
628 Some(format!(
629 r#"Subscription "{name}" must select only one top level field."#
630 ))
631 } else {
632 Some(
633 "Anonymous Subscription must select only one top level field."
634 .to_string(),
635 )
636 }
637 }
638 ExecutableBuildError::SubscriptionUsesIntrospection { name, .. } => {
639 if let Some(name) = name {
640 Some(format!(
641 r#"Subscription "{name}" must not select an introspection top level field."#
642 ))
643 } else {
644 Some("Anonymous Subscription must not select an introspection top level field."
645 .to_string())
646 }
647 }
648 ExecutableBuildError::SubscriptionUsesConditionalSelection { name, .. } => {
649 if let Some(name) = name {
650 Some(format!(
651 r#"Subscription "{name}" can not specify @skip or @include on root fields."#
652 ))
653 } else {
654 Some(
655 "Anonymous Subscription can not specify @skip or @include on root fields."
656 .to_string(),
657 )
658 }
659 }
660 ExecutableBuildError::ConflictingFieldType(inner) => {
661 let ConflictingFieldType {
662 alias,
663 original_type,
664 conflicting_type,
665 ..
666 } = &**inner;
667 Some(format!(
668 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."#
669 ))
670 }
671 ExecutableBuildError::ConflictingFieldName(inner) => {
672 let ConflictingFieldName {
673 alias,
674 original_selection,
675 conflicting_selection,
676 ..
677 } = &**inner;
678 Some(format!(
679 r#"Fields "{alias}" conflict because "{}" and "{}" are different fields. Use different aliases on the fields to fetch both if this was intentional."#,
680 original_selection.attribute, conflicting_selection.attribute
681 ))
682 }
683 ExecutableBuildError::ConflictingFieldArgument(inner) => {
684 let ConflictingFieldArgument { alias, .. } = &**inner;
685 Some(format!(
686 r#"Fields "{alias}" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional."#
687 ))
688 }
689 ExecutableBuildError::DuplicateDeferLabel { .. } => Some(
690 r#"Value for arguments "defer(label:)" and "stream(label:)" must be unique across all Defer/Stream directive usages."#
691 .to_string(),
692 ),
693 ExecutableBuildError::DeferLabelMustNotBeVariable => Some(
694 r#"Argument "@defer(label:)" must be a static string."#.to_string(),
695 ),
696 ExecutableBuildError::DeferOnRootMutationOrSubscriptionField {
697 operation_type,
698 } => Some(format!(
699 r#"Defer directive cannot be used on root {} type."#,
700 operation_type.name()
701 )),
702 ExecutableBuildError::DeferInSubscriptionMustBeConditional => Some(
703 "Defer directive not supported on subscription operations. Disable `@defer` by setting the `if` argument to `false`.".to_string(),
704 ),
705 },
706 _ => None,
707 }
708 }
709}
710
711impl ToCliReport for DiagnosticData {
712 fn location(&self) -> Option<SourceSpan> {
713 self.location
714 }
715
716 fn report(&self, report: &mut CliReport) {
717 if let Details::CompilerDiagnostic(diagnostic) = &self.details {
718 diagnostic.report(self.location, report);
719 return;
720 }
721
722 match &self.details {
728 Details::CompilerDiagnostic(_) => unreachable!(),
729 Details::ParserLimit { message, .. } => report.with_label_opt(self.location, message),
730 Details::SyntaxError { message, .. } => report.with_label_opt(self.location, message),
731 Details::SchemaBuildError(err) => match err {
732 SchemaBuildError::ExecutableDefinition { .. } => report.with_label_opt(
733 self.location,
734 "remove this definition, or use `parse_mixed()`",
735 ),
736 SchemaBuildError::SchemaDefinitionCollision {
737 previous_location, ..
738 } => {
739 report.with_label_opt(*previous_location, "previous `schema` definition here");
740 report.with_label_opt(self.location, "`schema` redefined here");
741 report.with_help(
742 "merge this definition with the previous one, or use `extend schema`",
743 );
744 }
745 SchemaBuildError::DirectiveDefinitionCollision {
746 previous_location,
747 name,
748 ..
749 } => {
750 report.with_label_opt(
751 *previous_location,
752 format_args!("previous definition of `@{name}` here"),
753 );
754 report.with_label_opt(self.location, format_args!("`@{name}` redefined here"));
755 report.with_help("remove or rename one of the definitions");
756 }
757 SchemaBuildError::TypeDefinitionCollision {
758 previous_location,
759 name,
760 ..
761 } => {
762 report.with_label_opt(
763 *previous_location,
764 format_args!("previous definition of `{name}` here"),
765 );
766 report.with_label_opt(self.location, format_args!("`{name}` redefined here"));
767 report.with_help("remove or rename one of the definitions, or use `extend`");
768 }
769 SchemaBuildError::BuiltInScalarTypeRedefinition => {
770 report.with_label_opt(self.location, "remove this scalar definition");
771 }
772 SchemaBuildError::OrphanSchemaExtension => {
773 report.with_label_opt(self.location, "extension here")
774 }
775 SchemaBuildError::OrphanTypeExtension { .. } => {
776 report.with_label_opt(self.location, "extension here")
777 }
778 SchemaBuildError::TypeExtensionKindMismatch { def_location, .. } => {
779 report.with_label_opt(*def_location, "type definition");
780 report.with_label_opt(self.location, "extension here")
781 }
782 SchemaBuildError::DuplicateRootOperation {
783 previous_location,
784 operation_type,
785 ..
786 } => {
787 report.with_label_opt(
788 *previous_location,
789 format_args!("previous definition of `{operation_type}` here"),
790 );
791 report.with_label_opt(
792 self.location,
793 format_args!("`{operation_type}` redefined here"),
794 );
795 }
796 SchemaBuildError::DuplicateImplementsInterfaceInObject {
797 name_at_previous_location,
798 ..
799 }
800 | SchemaBuildError::DuplicateImplementsInterfaceInInterface {
801 name_at_previous_location,
802 ..
803 } => {
804 let previous_location = &name_at_previous_location.location();
805 let name = name_at_previous_location;
806 report.with_label_opt(
807 *previous_location,
808 format_args!("previous implementation of `{name}` here"),
809 );
810 report.with_label_opt(
811 self.location,
812 format_args!("`{name}` implemented again here"),
813 );
814 }
815 SchemaBuildError::ObjectFieldNameCollision {
816 name_at_previous_location,
817 ..
818 }
819 | SchemaBuildError::InterfaceFieldNameCollision {
820 name_at_previous_location,
821 ..
822 }
823 | SchemaBuildError::EnumValueNameCollision {
824 name_at_previous_location,
825 ..
826 }
827 | SchemaBuildError::UnionMemberNameCollision {
828 name_at_previous_location,
829 ..
830 }
831 | SchemaBuildError::InputFieldNameCollision {
832 name_at_previous_location,
833 ..
834 } => {
835 let previous_location = &name_at_previous_location.location();
836 let name = name_at_previous_location;
837 report.with_label_opt(
838 *previous_location,
839 format_args!("previous definition of `{name}` here"),
840 );
841 report.with_label_opt(self.location, format_args!("`{name}` redefined here"));
842 }
843 },
844 Details::ExecutableBuildError(err) => match err {
845 ExecutableBuildError::TypeSystemDefinition { .. } => report.with_label_opt(
846 self.location,
847 "remove this definition, or use `parse_mixed()`",
848 ),
849 ExecutableBuildError::AmbiguousAnonymousOperation => {
850 report.with_label_opt(self.location, "provide a name for this definition");
851 report.with_help(
852 "GraphQL requires operations to be named if the document has more than one",
853 );
854 }
855 ExecutableBuildError::OperationNameCollision {
856 name_at_previous_location,
857 ..
858 }
859 | ExecutableBuildError::FragmentNameCollision {
860 name_at_previous_location,
861 ..
862 } => {
863 let previous_location = &name_at_previous_location.location();
864 let name = name_at_previous_location;
865 report.with_label_opt(
866 *previous_location,
867 format_args!("previous definition of `{name}` here"),
868 );
869 report.with_label_opt(self.location, format_args!("`{name}` redefined here"));
870 }
871 ExecutableBuildError::UndefinedRootOperation { operation_type, .. } => {
872 report.with_label_opt(
873 self.location,
874 format_args!(
875 "`{operation_type}` is not defined in the schema and is therefore not supported"
876 ),
877 );
878 report.with_help(format_args!(
879 "consider defining a `{operation_type}` root operation type in your schema"
880 ))
881 }
882 ExecutableBuildError::UndefinedTypeInNamedFragmentTypeCondition { .. } => {
883 report.with_label_opt(self.location, "type condition here")
884 }
885 ExecutableBuildError::UndefinedTypeInInlineFragmentTypeCondition {
886 path, ..
887 } => {
888 report.with_label_opt(self.location, "type condition here");
889 report.with_note(format_args!("path to the inline fragment: `{path} → ...`"))
890 }
891 ExecutableBuildError::SubselectionOnScalarType { path, .. }
892 | ExecutableBuildError::SubselectionOnEnumType { path, .. } => {
893 report.with_label_opt(self.location, "remove subselections here");
894 report.with_note(format_args!("path to the field: `{path}`"))
895 }
896 ExecutableBuildError::UndefinedField {
897 field_name,
898 type_name,
899 path,
900 ..
901 } => {
902 report.with_label_opt(
903 self.location,
904 format_args!("field `{field_name}` selected here"),
905 );
906 report.with_label_opt(
907 type_name.location(),
908 format_args!("type `{type_name}` defined here"),
909 );
910 report.with_note(format_args!("path to the field: `{path}`"))
911 }
912 ExecutableBuildError::SubscriptionUsesMultipleFields { fields, .. } => {
913 report.with_label_opt(
914 self.location,
915 format_args!("subscription with {} root fields", fields.len()),
916 );
917 report.with_help(format_args!(
918 "There are {} root fields: {}. This is not allowed.",
919 fields.len(),
920 CommaSeparated(fields)
921 ));
922 }
923 ExecutableBuildError::SubscriptionUsesIntrospection { field, .. } => {
924 report.with_label_opt(
925 self.location,
926 format_args!("{field} is an introspection field"),
927 );
928 }
929 ExecutableBuildError::SubscriptionUsesConditionalSelection { .. } => {
930 report.with_label_opt(self.location, "conditional directive used here");
931 }
932 ExecutableBuildError::DuplicateDeferLabel {
933 original_location, ..
934 } => {
935 report.with_label_opt(*original_location, "label first used here");
936 report.with_label_opt(self.location, "duplicate label used here");
937 }
938 ExecutableBuildError::DeferLabelMustNotBeVariable => {
939 report.with_label_opt(self.location, "variable used as label here");
940 }
941 ExecutableBuildError::DeferOnRootMutationOrSubscriptionField { operation_type } => {
942 report.with_label_opt(
943 self.location,
944 format_args!("`@defer` on a root {} selection", operation_type.name()),
945 );
946 }
947 ExecutableBuildError::DeferInSubscriptionMustBeConditional => {
948 report.with_label_opt(
949 self.location,
950 "`@defer` in a subscription must use `if: false` or `if: $variable`",
951 );
952 }
953 ExecutableBuildError::ConflictingFieldType(inner) => {
954 let ConflictingFieldType {
955 alias,
956 original_location,
957 original_coordinate,
958 original_type,
959 conflicting_location,
960 conflicting_coordinate,
961 conflicting_type,
962 } = &**inner;
963 report.with_label_opt(
964 *original_location,
965 format_args!(
966 "`{alias}` is selected from `{original_coordinate}: {original_type}` here"
967 ),
968 );
969 report.with_label_opt(
970 *conflicting_location,
971 format_args!("`{alias}` is selected from `{conflicting_coordinate}: {conflicting_type}` here"),
972 );
973 }
974 ExecutableBuildError::ConflictingFieldArgument(inner) => {
975 let ConflictingFieldArgument {
976 alias,
977 original_location,
978 original_coordinate,
979 original_value,
980 conflicting_location,
981 conflicting_coordinate: _,
982 conflicting_value,
983 } = &**inner;
984 let argument = &original_coordinate.argument;
985 match (original_value, conflicting_value) {
986 (Some(_), Some(_)) => {
987 report.with_label_opt(
988 *original_location,
989 format_args!(
990 "`{original_coordinate}` is used with one argument value here"
991 ),
992 );
993 report.with_label_opt(
994 *conflicting_location,
995 "but a different value here",
996 );
997 }
998 (Some(_), None) => {
999 report.with_label_opt(
1000 *original_location,
1001 format!("`{alias}` is selected with argument `{argument}` here",),
1002 );
1003 report.with_label_opt(
1004 *conflicting_location,
1005 format!("but argument `{argument}` is not provided here"),
1006 );
1007 }
1008 (None, Some(_)) => {
1009 report.with_label_opt(
1010 *conflicting_location,
1011 format!("`{alias}` is selected with argument `{argument}` here",),
1012 );
1013 report.with_label_opt(
1014 *original_location,
1015 format!("but argument `{argument}` is not provided here"),
1016 );
1017 }
1018 (None, None) => unreachable!(),
1019 }
1020 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");
1021 }
1022 ExecutableBuildError::ConflictingFieldName(inner) => {
1023 let ConflictingFieldName {
1024 alias: field,
1025 original_selection,
1026 original_location,
1027 conflicting_selection,
1028 conflicting_location,
1029 } = &**inner;
1030 report.with_label_opt(
1031 *original_location,
1032 format_args!("`{field}` is selected from `{original_selection}` here"),
1033 );
1034 report.with_label_opt(
1035 *conflicting_location,
1036 format_args!("`{field}` is selected from `{conflicting_selection}` here"),
1037 );
1038
1039 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");
1040 }
1041 },
1042 Details::RecursionLimitError => {}
1043 }
1044 }
1045}
1046
1047impl Diagnostic<'_, DiagnosticData> {
1048 #[doc(hidden)]
1053 pub fn unstable_to_json_compat(&self) -> GraphQLError {
1054 GraphQLError::new(
1055 self.error
1056 .unstable_compat_message()
1057 .unwrap_or_else(|| self.error.to_string()),
1058 self.error.location(),
1059 self.sources,
1060 )
1061 }
1062}
1063
1064impl DiagnosticList {
1065 pub fn new(sources: SourceMap) -> Self {
1067 Self {
1068 sources,
1069 diagnostics_data: Vec::new(),
1070 }
1071 }
1072
1073 pub fn is_empty(&self) -> bool {
1074 self.diagnostics_data.is_empty()
1075 }
1076
1077 pub fn len(&self) -> usize {
1078 self.diagnostics_data.len()
1079 }
1080
1081 pub fn iter(
1082 &self,
1083 ) -> impl DoubleEndedIterator<Item = Diagnostic<'_, DiagnosticData>> + ExactSizeIterator {
1084 self.diagnostics_data
1085 .iter()
1086 .map(|data| data.to_diagnostic(&self.sources))
1087 }
1088
1089 pub(crate) fn push(&mut self, location: Option<SourceSpan>, details: impl Into<Details>) {
1090 self.diagnostics_data.push(DiagnosticData {
1091 location,
1092 details: details.into(),
1093 })
1094 }
1095
1096 pub fn merge(&mut self, other: Self) {
1098 if !Arc::ptr_eq(&self.sources, &other.sources) {
1099 let sources = Arc::make_mut(&mut self.sources);
1100 for (&k, v) in &*other.sources {
1101 sources.entry(k).or_insert_with(|| v.clone());
1102 }
1103 }
1104 self.diagnostics_data.extend(other.diagnostics_data);
1105 self.sort()
1106 }
1107
1108 fn sort(&mut self) {
1109 self.diagnostics_data
1110 .sort_by_key(|err| err.location.map(|loc| (loc.file_id(), loc.offset())));
1111 }
1112
1113 pub(crate) fn into_result(mut self) -> Result<(), Self> {
1114 if self.diagnostics_data.is_empty() {
1115 Ok(())
1116 } else {
1117 self.sort();
1118 Err(self)
1119 }
1120 }
1121
1122 pub(crate) fn into_result_with<T>(self, value: T) -> Result<T, WithErrors<T>> {
1123 match self.into_result() {
1124 Ok(()) => Ok(value),
1125 Err(errors) => Err(WithErrors {
1126 partial: value,
1127 errors,
1128 }),
1129 }
1130 }
1131
1132 pub(crate) fn into_valid_result<T>(self, value: T) -> Result<Valid<T>, WithErrors<T>> {
1133 match self.into_result() {
1134 Ok(()) => Ok(Valid(value)),
1135 Err(errors) => Err(WithErrors {
1136 partial: value,
1137 errors,
1138 }),
1139 }
1140 }
1141}
1142
1143impl fmt::Display for DiagnosticList {
1145 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1146 for diagnostic in self.iter() {
1147 fmt::Display::fmt(&diagnostic, f)?
1148 }
1149 Ok(())
1150 }
1151}
1152
1153impl fmt::Debug for DiagnosticList {
1155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1156 for diagnostic in self.iter() {
1157 fmt::Debug::fmt(&diagnostic, f)?
1158 }
1159 Ok(())
1160 }
1161}
1162
1163impl From<SchemaBuildError> for Details {
1164 fn from(value: SchemaBuildError) -> Self {
1165 Details::SchemaBuildError(value)
1166 }
1167}
1168
1169impl From<ExecutableBuildError> for Details {
1170 fn from(value: ExecutableBuildError) -> Self {
1171 Details::ExecutableBuildError(value)
1172 }
1173}
1174
1175impl From<diagnostics::DiagnosticData> for Details {
1176 fn from(value: diagnostics::DiagnosticData) -> Self {
1177 Details::CompilerDiagnostic(value)
1178 }
1179}
1180
1181const DEFAULT_RECURSION_LIMIT: usize = 32;
1182
1183#[derive(Debug, Clone, thiserror::Error)]
1184#[error("Recursion limit reached")]
1185#[non_exhaustive]
1186struct RecursionLimitError {}
1187
1188#[derive(Debug)]
1190struct DepthCounter {
1191 value: usize,
1192 high: usize,
1193 limit: usize,
1194}
1195
1196impl DepthCounter {
1197 fn new() -> Self {
1198 Self {
1199 value: 0,
1200 high: 0,
1201 limit: DEFAULT_RECURSION_LIMIT,
1202 }
1203 }
1204
1205 fn with_limit(mut self, limit: usize) -> Self {
1206 self.limit = limit;
1207 self
1208 }
1209
1210 pub(crate) fn guard(&mut self) -> DepthGuard<'_> {
1212 DepthGuard(self)
1213 }
1214}
1215
1216struct DepthGuard<'a>(&'a mut DepthCounter);
1221
1222impl DepthGuard<'_> {
1223 fn increment(&mut self) -> Result<DepthGuard<'_>, RecursionLimitError> {
1225 self.0.value += 1;
1226 self.0.high = self.0.high.max(self.0.value);
1227 if self.0.value > self.0.limit {
1228 Err(RecursionLimitError {})
1229 } else {
1230 Ok(DepthGuard(self.0))
1231 }
1232 }
1233}
1234
1235impl Drop for DepthGuard<'_> {
1236 fn drop(&mut self) {
1237 self.0.value = self.0.value.saturating_sub(1);
1239 }
1240}
1241
1242#[derive(Debug)]
1244struct RecursionStack {
1245 seen: IndexSet<Name>,
1246 high: usize,
1247 limit: usize,
1248}
1249
1250impl RecursionStack {
1251 fn new() -> Self {
1252 Self {
1253 seen: IndexSet::with_hasher(Default::default()),
1254 high: 0,
1255 limit: DEFAULT_RECURSION_LIMIT,
1256 }
1257 }
1258
1259 fn with_root(root: Name) -> Self {
1260 let mut stack = Self::new();
1261 stack.seen.insert(root);
1262 stack
1263 }
1264
1265 fn with_limit(mut self, limit: usize) -> Self {
1266 self.limit = limit;
1267 self
1268 }
1269
1270 pub(crate) fn guard(&mut self) -> RecursionGuard<'_> {
1272 RecursionGuard(self)
1273 }
1274}
1275
1276struct RecursionGuard<'a>(&'a mut RecursionStack);
1282
1283impl RecursionGuard<'_> {
1284 fn push(&mut self, name: &Name) -> Result<RecursionGuard<'_>, RecursionLimitError> {
1286 let new = self.0.seen.insert(name.clone());
1287 debug_assert!(
1288 new,
1289 "cannot push the same name twice to RecursionGuard, check contains() first"
1290 );
1291 self.0.high = self.0.high.max(self.0.seen.len());
1292 if self.0.seen.len() > self.0.limit {
1293 Err(RecursionLimitError {})
1294 } else {
1295 Ok(RecursionGuard(self.0))
1296 }
1297 }
1298
1299 fn contains(&self, name: &Name) -> bool {
1301 self.0.seen.contains(name)
1302 }
1303
1304 fn first(&self) -> Option<&Name> {
1306 self.0.seen.first()
1307 }
1308}
1309
1310impl Drop for RecursionGuard<'_> {
1311 fn drop(&mut self) {
1312 let _ = self.0.seen.pop();
1314 }
1315}
1316
1317#[derive(Debug, Clone, thiserror::Error)]
1319enum CycleError<T> {
1320 #[error("Cycle detected")]
1323 Recursed(Vec<Node<T>>),
1324 #[error(transparent)]
1326 Limit(#[from] RecursionLimitError),
1327}
1328
1329impl<T> CycleError<T> {
1330 fn trace(mut self, node: &Node<T>) -> Self {
1331 if let Self::Recursed(trace) = &mut self {
1332 trace.push(node.clone());
1333 }
1334 self
1335 }
1336}
1337
1338struct CommaSeparated<'a, It>(&'a It);
1339impl<'a, T, It> fmt::Display for CommaSeparated<'a, It>
1340where
1341 T: fmt::Display,
1342 &'a It: IntoIterator<Item = T>,
1343{
1344 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1345 let mut it = self.0.into_iter();
1346 if let Some(element) = it.next() {
1347 element.fmt(f)?;
1348 }
1349 for element in it {
1350 f.write_str(", ")?;
1351 element.fmt(f)?;
1352 }
1353 Ok(())
1354 }
1355}