diff --git a/src/HotChocolate/Core/src/Types/IReadOnlySchemaOptions.cs b/src/HotChocolate/Core/src/Types/IReadOnlySchemaOptions.cs index 4c831fba6c5..2d70e9bacb8 100644 --- a/src/HotChocolate/Core/src/Types/IReadOnlySchemaOptions.cs +++ b/src/HotChocolate/Core/src/Types/IReadOnlySchemaOptions.cs @@ -87,10 +87,10 @@ public interface IReadOnlySchemaOptions /// bool StripLeadingIFromInterface { get; } - + /// bool EnableTrueNullability { get; } - + /// bool EnableTag { get; } } diff --git a/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs index 6fec956d0b8..c2c954c7dc2 100644 --- a/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs @@ -354,7 +354,7 @@ internal static bool IsType(this IType type, TypeKind kind) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsType(this IType type, TypeKind kind1, TypeKind kind2) + internal static bool IsType(this IType type, TypeKind kind1, TypeKind kind2) { if (type.Kind == kind1 || type.Kind == kind2) { diff --git a/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs b/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs index a7ceb5338da..91b3e1fff39 100644 --- a/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs +++ b/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs @@ -106,6 +106,12 @@ public IOutputType NonNullString public IDictionary ContextData { get; set; } = default!; + public List CurrentFieldPairs { get; } = new(); + + public List NextFieldPairs { get; } = new(); + + public HashSet ProcessedFieldPairs { get; } = new(); + public IList RentFieldInfoList() { var buffer = _buffers.Peek(); @@ -159,6 +165,9 @@ public void Clear() InputFields.Clear(); _errors.Clear(); List.Clear(); + CurrentFieldPairs.Clear(); + NextFieldPairs.Clear(); + ProcessedFieldPairs.Clear(); UnexpectedErrorsDetected = false; Count = 0; Max = 0; diff --git a/src/HotChocolate/Core/src/Validation/FieldInfo.cs b/src/HotChocolate/Core/src/Validation/FieldInfo.cs index 80e770d06b1..85e73310a3d 100644 --- a/src/HotChocolate/Core/src/Validation/FieldInfo.cs +++ b/src/HotChocolate/Core/src/Validation/FieldInfo.cs @@ -1,3 +1,4 @@ +using System; using HotChocolate.Language; using HotChocolate.Types; @@ -7,7 +8,7 @@ namespace HotChocolate.Validation; /// The validation field info provides access to the field node and the type /// information of the referenced field. /// -public readonly struct FieldInfo +public readonly struct FieldInfo : IEquatable { /// /// Initializes a new instance of @@ -41,4 +42,39 @@ public FieldInfo(IType declaringType, IType type, FieldNode field) /// Gets the field selection. /// public FieldNode Field { get; } -} + + /// + /// Compares this field info to another field info. + /// + /// + /// The other field info. + /// + /// + /// true if the field infos are equal. + /// + public bool Equals(FieldInfo other) + => Field.Equals(other.Field) && + DeclaringType.Equals(other.DeclaringType) && + Type.Equals(other.Type); + + /// + /// Compares this field info to another object. + /// + /// + /// The other object. + /// + /// + /// true if the field infos are equal. + /// + public override bool Equals(object? obj) + => obj is FieldInfo other && Equals(other); + + /// + /// Returns the hash code of this instance. + /// + /// + /// The hash code of this instance. + /// + public override int GetHashCode() + => HashCode.Combine(Field, DeclaringType, Type); +} \ No newline at end of file diff --git a/src/HotChocolate/Core/src/Validation/FieldInfoPair.cs b/src/HotChocolate/Core/src/Validation/FieldInfoPair.cs new file mode 100644 index 00000000000..d51ca737651 --- /dev/null +++ b/src/HotChocolate/Core/src/Validation/FieldInfoPair.cs @@ -0,0 +1,67 @@ +using System; + +namespace HotChocolate.Validation; + +/// +/// Represents a pair of field infos. +/// +public readonly struct FieldInfoPair : IEquatable +{ + /// + /// Initializes a new instance of . + /// + /// + /// The first field info. + /// + /// + /// The second field info. + /// + public FieldInfoPair(FieldInfo fieldA, FieldInfo fieldB) + { + FieldA = fieldA; + FieldB = fieldB; + } + + /// + /// Gets the first field info. + /// + public FieldInfo FieldA { get; } + + /// + /// Gets the second field info. + /// + public FieldInfo FieldB { get; } + + /// + /// Compares this field info pair to another field info pair. + /// + /// + /// The other field info pair. + /// + /// + /// true if the field info pairs are equal. + /// + public bool Equals(FieldInfoPair other) + => FieldA.Equals(other.FieldA) && FieldB.Equals(other.FieldB); + + /// + /// Compares this field info pair to another object. + /// + /// + /// The other object. + /// + /// + /// true if the field info pairs are equal. + /// + public override bool Equals(object? obj) + => obj is FieldInfoPair other && Equals(other); + + /// + /// Returns the hash code for this field info pair. + /// + /// + /// The hash code for this field info pair. + /// + public override int GetHashCode() + => HashCode.Combine(FieldA, FieldB); +} \ No newline at end of file diff --git a/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs b/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs index db5e5503937..ec0062cc053 100644 --- a/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs +++ b/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs @@ -160,6 +160,21 @@ public interface IDocumentValidatorContext : ISyntaxVisitorContext /// IDictionary ContextData { get; } + /// + /// When processing field merging this list holds the field pairs that are processed. + /// + List CurrentFieldPairs { get; } + + /// + /// When processing field merging this list holds the field pairs that are processed next. + /// + List NextFieldPairs { get; } + + /// + /// When processing field merging this set represents the already processed field pairs. + /// + HashSet ProcessedFieldPairs { get; } + /// /// Rents a list of field infos. /// diff --git a/src/HotChocolate/Core/src/Validation/Rules/FieldVisitor.cs b/src/HotChocolate/Core/src/Validation/Rules/FieldVisitor.cs index de582a560cb..f25120a0ebe 100644 --- a/src/HotChocolate/Core/src/Validation/Rules/FieldVisitor.cs +++ b/src/HotChocolate/Core/src/Validation/Rules/FieldVisitor.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using HotChocolate.Language; using HotChocolate.Language.Visitors; using HotChocolate.Types; using HotChocolate.Types.Introspection; using HotChocolate.Utilities; -using static System.StringComparison; using static HotChocolate.Language.SyntaxComparer; namespace HotChocolate.Validation.Rules; @@ -45,9 +45,17 @@ protected override ISyntaxVisitorAction Leave( { if (context.FieldSets.Count > 0) { - foreach (var selectionSet in context.FieldSets.Keys) + foreach (var item in context.FieldSets) { - TryMergeFieldsInSet(context, context.FieldSets[selectionSet]); + TryMergeFieldsInSet(context, item.Value); + } + + var next = context.NextFieldPairs; + + while (next.Count > 0) + { + FillCurrentFieldPairs(context); + ProcessCurrentFieldPairs(context); } } @@ -147,16 +155,14 @@ protected override ISyntaxVisitorAction Enter( type.NamedType() is { Kind: TypeKind.Union } unionType && HasFields(node)) { - context.ReportError(context.UnionFieldError(node, (UnionType)unionType)); + context.ReportError(context.UnionFieldError(node, (UnionType) unionType)); return Skip; } - if (context.Path.TryPeek(out var parent)) + if (context.Path.TryPeek(out var parent) && + parent.Kind is SyntaxKind.OperationDefinition or SyntaxKind.Field) { - if (parent.Kind is SyntaxKind.OperationDefinition or SyntaxKind.Field) - { - context.SelectionSets.Push(node); - } + context.SelectionSets.Push(node); } return Continue; @@ -166,12 +172,14 @@ protected override ISyntaxVisitorAction Leave( SelectionSetNode node, IDocumentValidatorContext context) { - if (context.Path.TryPeek(out var parent)) + if (!context.Path.TryPeek(out var parent)) { - if (parent.Kind is SyntaxKind.OperationDefinition or SyntaxKind.Field) - { - context.SelectionSets.Pop(); - } + return Continue; + } + + if (parent.Kind is SyntaxKind.OperationDefinition or SyntaxKind.Field) + { + context.SelectionSets.Pop(); } return Continue; @@ -181,9 +189,7 @@ protected override ISyntaxVisitorAction VisitChildren( FragmentSpreadNode node, IDocumentValidatorContext context) { - if (context.Fragments.TryGetValue( - node.Name.Value, - out var fragment) && + if (context.Fragments.TryGetValue(node.Name.Value, out var fragment) && context.VisitedFragments.Add(fragment.Name.Value)) { var result = Visit(fragment, node, context); @@ -206,7 +212,7 @@ private static bool HasFields(SelectionSetNode selectionSet) if (selection.Kind is SyntaxKind.Field) { - if (!IsTypeNameField(((FieldNode)selection).Name.Value)) + if (!IsTypeNameField(((FieldNode) selection).Name.Value)) { return true; } @@ -223,51 +229,62 @@ private static void TryMergeFieldsInSet( IDocumentValidatorContext context, IList fields) { - if (fields.Count == 1) + while (true) { - if (fields[0].Field.SelectionSet is { } selectionSet && - context.FieldSets.TryGetValue(selectionSet, out var fieldSet)) + if (fields.Count == 1) { - TryMergeFieldsInSet(context, fieldSet); + if (fields[0].Field.SelectionSet is { } selectionSet && + context.FieldSets.TryGetValue(selectionSet, out var fieldSet)) + { + fields = fieldSet; + continue; + } + return; } + break; } - else + + for (var i = 0; i < fields.Count - 1; i++) { - for (var i = 0; i < fields.Count - 1; i++) + var fieldA = fields[i]; + + for (var j = i + 1; j < fields.Count; j++) { - var fieldA = fields[i]; + var fieldB = fields[j]; - for (var j = i + 1; j < fields.Count; j++) + if (ReferenceEquals(fieldA.Field, fieldB.Field) || + !fieldA.ResponseName.EqualsOrdinal(fieldB.ResponseName)) { - var fieldB = fields[j]; + continue; + } - if (!ReferenceEquals(fieldA.Field, fieldB.Field) && - string.Equals(fieldA.ResponseName, fieldB.ResponseName, Ordinal)) + if (SameResponseShape( + fieldA.Type.RewriteNullability(fieldA.Field.Required), + fieldB.Type.RewriteNullability(fieldB.Field.Required)) && + SameStreamDirective(fieldA, fieldB)) + { + if (!IsParentTypeAligned(fieldA, fieldB)) { - if (SameResponseShape( - fieldA.Type.RewriteNullability(fieldA.Field.Required), - fieldB.Type.RewriteNullability(fieldB.Field.Required)) && - SameStreamDirective(fieldA, fieldB)) - { - if (IsParentTypeAligned(fieldA, fieldB)) - { - if (BySyntax.Equals(fieldA.Field.Name, fieldB.Field.Name) && - AreArgumentsIdentical(fieldA.Field, fieldB.Field)) - { - TryMergeFieldsInSet(context, fieldA, fieldB); - } - else if (context.FieldTuples.Add((fieldA.Field, fieldB.Field))) - { - context.ReportError( - context.FieldsAreNotMergeable(fieldA, fieldB)); - } - } - } - else if (context.FieldTuples.Add((fieldA.Field, fieldB.Field))) + continue; + } + + if (BySyntax.Equals(fieldA.Field.Name, fieldB.Field.Name) && + AreArgumentsIdentical(fieldA.Field, fieldB.Field)) + { + var pair = new FieldInfoPair(fieldA, fieldB); + if (context.ProcessedFieldPairs.Add(pair)) { - context.ReportError(context.FieldsAreNotMergeable(fieldA, fieldB)); + context.NextFieldPairs.Add(pair); } } + else if (context.FieldTuples.Add((fieldA.Field, fieldB.Field))) + { + context.ReportError(context.FieldsAreNotMergeable(fieldA, fieldB)); + } + } + else if (context.FieldTuples.Add((fieldA.Field, fieldB.Field))) + { + context.ReportError(context.FieldsAreNotMergeable(fieldA, fieldB)); } } } @@ -283,26 +300,38 @@ fieldB.Field.SelectionSet is { } b && context.FieldSets.TryGetValue(a, out var al) && context.FieldSets.TryGetValue(b, out var bl)) { - var mergedSet = context.RentFieldInfoList(); - CopyFieldInfos(al, mergedSet); - CopyFieldInfos(bl, mergedSet); + var mergedSet = Unsafe.As>(context.RentFieldInfoList()); +#if NET6_0_OR_GREATER + mergedSet.EnsureCapacity(al.Count + bl.Count); +#endif + CopyFieldInfos(Unsafe.As>(al), mergedSet); + CopyFieldInfos(Unsafe.As>(bl), mergedSet); TryMergeFieldsInSet(context, mergedSet); } } - private static void CopyFieldInfos(IList from, IList to) + private static void CopyFieldInfos(List from, List to) { - for (var i = 0; i < from.Count; i++) +#if NETSTANDARD2_0 + foreach (var field in from) + { + to.Add(field); + } +#else + ref var field = ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(from)); + ref var end = ref Unsafe.Add(ref field, from.Count); + + while (Unsafe.IsAddressLessThan(ref field, ref end)) { - to.Add(from[i]); + to.Add(field); + field = ref Unsafe.Add(ref field, 1); } +#endif } private static bool IsParentTypeAligned(FieldInfo fieldA, FieldInfo fieldB) - { - return ReferenceEquals(fieldA.DeclaringType, fieldB.DeclaringType) || + => ReferenceEquals(fieldA.DeclaringType, fieldB.DeclaringType) || !fieldA.DeclaringType.IsObjectType() && !fieldB.DeclaringType.IsObjectType(); - } private static bool AreArgumentsIdentical(FieldNode fieldA, FieldNode fieldB) { @@ -344,9 +373,9 @@ private static bool SameResponseShape(IType typeA, IType typeB) { while (!typeA.IsNamedType() && !typeB.IsNamedType()) { - if (typeA.IsNonNullType() || typeB.IsNonNullType()) + if (typeA.Kind is TypeKind.NonNull || typeB.Kind is TypeKind.NonNull) { - if (typeA.IsNullableType() || typeB.IsNullableType()) + if (typeA.Kind is not TypeKind.NonNull || typeB.Kind is not TypeKind.NonNull) { return false; } @@ -355,9 +384,9 @@ private static bool SameResponseShape(IType typeA, IType typeB) typeB = typeB.InnerType(); } - if (typeA.IsListType() || typeB.IsListType()) + if (typeA.IsType(TypeKind.List) || typeB.IsType(TypeKind.List)) { - if (!typeA.IsListType() || !typeB.IsListType()) + if (!typeA.IsType(TypeKind.List) || !typeB.IsType(TypeKind.List)) { return false; } @@ -367,12 +396,13 @@ private static bool SameResponseShape(IType typeA, IType typeB) } } - if (typeA.IsLeafType() || typeB.IsLeafType()) + if (typeA.IsType(TypeKind.Scalar, TypeKind.Enum) || typeB.IsType(TypeKind.Scalar, TypeKind.Enum)) { return ReferenceEquals(typeA, typeB); } - if (typeA.IsCompositeType() && typeB.IsCompositeType()) + if (typeA.IsType(TypeKind.Object, TypeKind.Interface, TypeKind.Union) && + typeB.IsType(TypeKind.Object, TypeKind.Interface, TypeKind.Union)) { return true; } @@ -400,4 +430,57 @@ private static bool SameStreamDirective(FieldInfo fieldA, FieldInfo fieldB) // if both fields have a stream directive we need to check if they are equal. return streamA.StreamDirectiveEquals(streamB); } -} + + private static void FillCurrentFieldPairs(IDocumentValidatorContext context) + { + var next = context.NextFieldPairs; + var current = context.CurrentFieldPairs; + +#if NETSTANDARD2_0 + foreach (var pair in next) + { + current.Add(pair); + } + + next.Clear(); +#else + ref var pair = ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(next)); + ref var end = ref Unsafe.Add(ref pair, next.Count); + + current.EnsureCapacity(next.Count); + + while (Unsafe.IsAddressLessThan(ref pair, ref end)) + { + current.Add(pair); + pair = ref Unsafe.Add(ref pair, 1); + } + + next.Clear(); +#endif + } + + private static void ProcessCurrentFieldPairs(IDocumentValidatorContext context) + { + var current = context.CurrentFieldPairs; + +#if NETSTANDARD2_0 + foreach (var pair in current) + { + TryMergeFieldsInSet(context, pair.FieldA, pair.FieldB); + } + + current.Clear(); +#else + ref var pair = ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(current)); + ref var end = ref Unsafe.Add(ref pair, current.Count); + + while (Unsafe.IsAddressLessThan(ref pair, ref end)) + { + TryMergeFieldsInSet(context, pair.FieldA, pair.FieldB); + pair = ref Unsafe.Add(ref pair, 1); + } + + current.Clear(); +#endif + } +} \ No newline at end of file diff --git a/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs index 08f74024dc8..214ed137f93 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs @@ -324,7 +324,7 @@ public void Field_Is_Visible_When_One_Selection_Is_Visible_1() .Create(); var document = Utf8GraphQLParser.Parse( - @"query foo($v: Boolean){ + @"query foo($v: Boolean!){ hero(episode: EMPIRE) { name ... abc @include(if: $v) @@ -363,7 +363,7 @@ public void Field_Is_Visible_When_One_Selection_Is_Visible_2() .Create(); var document = Utf8GraphQLParser.Parse( - @"query foo($v: Boolean){ + @"query foo($v: Boolean!){ hero(episode: EMPIRE) { name @include(if: $v) ... abc @@ -403,7 +403,7 @@ public void Field_Is_Visible_When_One_Selection_Is_Visible_3() .Create(); var document = Utf8GraphQLParser.Parse( - @"query foo($v: Boolean, $q: Boolean){ + @"query foo($v: Boolean!, $q: Boolean!){ hero(episode: EMPIRE) { name @include(if: $v) ... abc @include(if: $q) @@ -442,7 +442,7 @@ public void Field_Is_Visible_When_One_Selection_Is_Visible_4() .Create(); var document = Utf8GraphQLParser.Parse( - @"query foo($v: Boolean){ + @"query foo($v: Boolean!){ hero(episode: EMPIRE) { name @include(if: $v) ... abc @@ -487,7 +487,7 @@ public void Object_Field_Visibility_Is_Correctly_Inherited() .Create(); var document = Utf8GraphQLParser.Parse( - @"query foo($v: Boolean) { + @"query foo($v: Boolean!) { hero(episode: EMPIRE) @include(if: $v) { name } @@ -608,7 +608,7 @@ public void Object_Field_Visibility_Is_Correctly_Inherited_2() .Create(); var document = Utf8GraphQLParser.Parse( - @"query foo($v: Boolean, $q: Boolean) { + @"query foo($v: Boolean!, $q: Boolean!) { hero(episode: EMPIRE) @include(if: $v) { name @include(if: $q) } @@ -653,7 +653,7 @@ public void Object_Field_Visibility_Is_Correctly_Inherited_3() .Create(); var document = Utf8GraphQLParser.Parse( - @"query foo($v: Boolean, $q: Boolean) { + @"query foo($v: Boolean!, $q: Boolean!) { hero(episode: EMPIRE) @include(if: $v) { name @include(if: $q) } @@ -1162,14 +1162,14 @@ private static void MatchSnapshot(DocumentNode original, IOperation compiled) public class Foo { - public Bar Bar => new Bar(); + public Bar Bar => new(); } public class Bar { public string Text => "Bar"; - public Baz Baz => new Baz(); + public Baz Baz => new(); } public class Baz diff --git a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_1.snap b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_1.snap index 6cd0f2b738d..8d36b29707a 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_1.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_1.snap @@ -1,4 +1,4 @@ -query foo($v: Boolean) { +query foo($v: Boolean!) { hero(episode: EMPIRE) { name ... abc @include(if: $v) @@ -11,7 +11,7 @@ fragment abc on Droid { --------------------------------------------------------- -query foo($v: Boolean) @includeCondition(flag: 2) { +query foo($v: Boolean!) @includeCondition(flag: 2) { ... on Query { hero(episode: EMPIRE) @__execute(id: 0, kind: PURE, type: COMPOSITE) { ... on Human { diff --git a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_2.snap b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_2.snap index df34c2c38b4..2c7965c4d7e 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_2.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_2.snap @@ -1,4 +1,4 @@ -query foo($v: Boolean) { +query foo($v: Boolean!) { hero(episode: EMPIRE) { name @include(if: $v) ... abc @@ -11,7 +11,7 @@ fragment abc on Droid { --------------------------------------------------------- -query foo($v: Boolean) @includeCondition(flag: 2) { +query foo($v: Boolean!) @includeCondition(flag: 2) { ... on Query { hero(episode: EMPIRE) @__execute(id: 0, kind: PURE, type: COMPOSITE) { ... on Human { diff --git a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_3.snap b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_3.snap index 7dcdf88e876..b0e88db9614 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_3.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_3.snap @@ -1,4 +1,4 @@ -query foo($v: Boolean, $q: Boolean) { +query foo($v: Boolean!, $q: Boolean!) { hero(episode: EMPIRE) { name @include(if: $v) ... abc @include(if: $q) @@ -11,7 +11,7 @@ fragment abc on Droid { --------------------------------------------------------- -query foo($v: Boolean, $q: Boolean) @includeCondition(flag: 2) @includeCondition(flag: 3) { +query foo($v: Boolean!, $q: Boolean!) @includeCondition(flag: 2) @includeCondition(flag: 3) { ... on Query { hero(episode: EMPIRE) @__execute(id: 0, kind: PURE, type: COMPOSITE) { ... on Human { diff --git a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_4.snap b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_4.snap index 6411fc8392b..9a58cd5a551 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_4.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Field_Is_Visible_When_One_Selection_Is_Visible_4.snap @@ -1,4 +1,4 @@ -query foo($v: Boolean) { +query foo($v: Boolean!) { hero(episode: EMPIRE) { name @include(if: $v) ... abc @@ -14,7 +14,7 @@ fragment abc on Droid { --------------------------------------------------------- -query foo($v: Boolean) @includeCondition(flag: 2) { +query foo($v: Boolean!) @includeCondition(flag: 2) { ... on Query { hero(episode: EMPIRE) @__execute(id: 0, kind: PURE, type: COMPOSITE) { ... on Human { diff --git a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Object_Field_Visibility_Is_Correctly_Inherited.snap b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Object_Field_Visibility_Is_Correctly_Inherited.snap index 79070ef0441..d491047b689 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Object_Field_Visibility_Is_Correctly_Inherited.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Object_Field_Visibility_Is_Correctly_Inherited.snap @@ -1,4 +1,4 @@ -query foo($v: Boolean) { +query foo($v: Boolean!) { hero(episode: EMPIRE) @include(if: $v) { name } @@ -16,7 +16,7 @@ --------------------------------------------------------- -query foo($v: Boolean) @includeCondition(flag: 2) { +query foo($v: Boolean!) @includeCondition(flag: 2) { ... on Query { hero(episode: EMPIRE) @__execute(id: 0, kind: PURE, type: COMPOSITE) { ... on Human { diff --git a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Object_Field_Visibility_Is_Correctly_Inherited_2.snap b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Object_Field_Visibility_Is_Correctly_Inherited_2.snap index 17f1fc0ee4c..6ffba23a92a 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Object_Field_Visibility_Is_Correctly_Inherited_2.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Object_Field_Visibility_Is_Correctly_Inherited_2.snap @@ -1,4 +1,4 @@ -query foo($v: Boolean, $q: Boolean) { +query foo($v: Boolean!, $q: Boolean!) { hero(episode: EMPIRE) @include(if: $v) { name @include(if: $q) } @@ -16,7 +16,7 @@ --------------------------------------------------------- -query foo($v: Boolean, $q: Boolean) @includeCondition(flag: 2) @includeCondition(flag: 3) { +query foo($v: Boolean!, $q: Boolean!) @includeCondition(flag: 2) @includeCondition(flag: 3) { ... on Query { hero(episode: EMPIRE) @__execute(id: 0, kind: PURE, type: COMPOSITE) { ... on Human { diff --git a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Object_Field_Visibility_Is_Correctly_Inherited_3.snap b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Object_Field_Visibility_Is_Correctly_Inherited_3.snap index f878e77ddcd..8702b4b0fae 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Object_Field_Visibility_Is_Correctly_Inherited_3.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Object_Field_Visibility_Is_Correctly_Inherited_3.snap @@ -1,4 +1,4 @@ -query foo($v: Boolean, $q: Boolean) { +query foo($v: Boolean!, $q: Boolean!) { hero(episode: EMPIRE) @include(if: $v) { name @include(if: $q) } @@ -6,7 +6,7 @@ --------------------------------------------------------- -query foo($v: Boolean, $q: Boolean) @includeCondition(flag: 2) @includeCondition(flag: 3) { +query foo($v: Boolean!, $q: Boolean!) @includeCondition(flag: 2) @includeCondition(flag: 3) { ... on Query { hero(episode: EMPIRE) @conditional @__execute(id: 0, kind: PURE, type: COMPOSITE) { ... on Human { diff --git a/src/HotChocolate/Core/test/Validation.Tests/FieldSelectionMergingRuleTests.cs b/src/HotChocolate/Core/test/Validation.Tests/FieldSelectionMergingRuleTests.cs index ef54cfdbdb4..e4eb409ba68 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/FieldSelectionMergingRuleTests.cs +++ b/src/HotChocolate/Core/test/Validation.Tests/FieldSelectionMergingRuleTests.cs @@ -823,31 +823,32 @@ public void ReportsDeepConflictToNearestCommonAncestor() [Fact] public void ReportsDeepConflictToNearestCommonAncestorInFragments() { - ExpectErrors(@" - { - f1 { - ...F + ExpectErrors( + """ + { + f1 { + ...F + } + f1 { + ...F + } + } + fragment F on Query { + f2 { + f3 { + x: a } - f1 { - ...F + f3 { + x: b } } - fragment F on Query { - f2 { - f3 { - x: a - } - f3 { - x: b - } - }, - f2 { - f3 { - y - } + f2 { + f3 { + y } } - "); + } + """); } [Fact] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsDeepConflictToNearestCommonAncestorInFragments.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsDeepConflictToNearestCommonAncestorInFragments.snap index d412406b5b8..f5fb0b36f16 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsDeepConflictToNearestCommonAncestorInFragments.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/FieldSelectionMergingRuleTests.ReportsDeepConflictToNearestCommonAncestorInFragments.snap @@ -5,12 +5,12 @@ "Path": null, "Locations": [ { - "Line": 13, - "Column": 29 + "Line": 12, + "Column": 13 }, { - "Line": 16, - "Column": 29 + "Line": 15, + "Column": 13 } ], "Extensions": { @@ -30,10 +30,10 @@ "Alias": { "Kind": "Name", "Location": { - "Start": 397, - "End": 399, - "Line": 16, - "Column": 29 + "Start": 156, + "End": 158, + "Line": 15, + "Column": 13 }, "Value": "x" }, @@ -41,18 +41,18 @@ "Required": null, "SelectionSet": null, "Location": { - "Start": 397, - "End": 427, - "Line": 16, - "Column": 29 + "Start": 156, + "End": 170, + "Line": 15, + "Column": 13 }, "Name": { "Kind": "Name", "Location": { - "Start": 400, - "End": 427, - "Line": 16, - "Column": 32 + "Start": 159, + "End": 170, + "Line": 15, + "Column": 16 }, "Value": "b" }, @@ -65,12 +65,12 @@ "Path": null, "Locations": [ { - "Line": 16, - "Column": 29 + "Line": 15, + "Column": 13 }, { - "Line": 13, - "Column": 29 + "Line": 12, + "Column": 13 } ], "Extensions": { @@ -90,10 +90,10 @@ "Alias": { "Kind": "Name", "Location": { - "Start": 309, - "End": 311, - "Line": 13, - "Column": 29 + "Start": 116, + "End": 118, + "Line": 12, + "Column": 13 }, "Value": "x" }, @@ -101,18 +101,18 @@ "Required": null, "SelectionSet": null, "Location": { - "Start": 309, - "End": 339, - "Line": 13, - "Column": 29 + "Start": 116, + "End": 130, + "Line": 12, + "Column": 13 }, "Name": { "Kind": "Name", "Location": { - "Start": 312, - "End": 339, - "Line": 13, - "Column": 32 + "Start": 119, + "End": 130, + "Line": 12, + "Column": 16 }, "Value": "a" },