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