Skip to content

Commit a390e46

Browse files
glen-84michaelstaib
authored andcommitted
Fixed issue with non-deterministic order of automatic type registrations (#7708)
1 parent ae1557d commit a390e46

17 files changed

+468
-4
lines changed

src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ private static void WriteConfiguration(
6363
var hasInterfaceTypes = false;
6464
var hasConfigurations = false;
6565

66-
foreach (var syntaxInfo in syntaxInfos)
66+
foreach (var syntaxInfo in syntaxInfos.OrderBy(s => s.OrderByKey))
6767
{
6868
if(syntaxInfo.Diagnostics.Length > 0)
6969
{

src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderDefaultsInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public sealed class DataLoaderDefaultsInfo(
1818

1919
public bool GenerateInterfaces { get; } = generateInterfaces;
2020

21+
public override string OrderByKey => string.Empty;
22+
2123
public override bool Equals(object? obj)
2224
=> obj is DataLoaderDefaultsInfo other && Equals(other);
2325

src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ public DataLoaderInfo(
7878

7979
public ImmutableArray<DataLoaderParameterInfo> Parameters { get; }
8080

81+
public override string OrderByKey => FullName;
82+
8183
public ImmutableArray<CacheLookup> GetLookups(ITypeSymbol keyType, ITypeSymbol valueType)
8284
{
8385
if (_lookups.Length > 0)

src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderModuleInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ public sealed class DataLoaderModuleInfo(string moduleName) : SyntaxInfo
44
{
55
public string ModuleName { get; } = moduleName;
66

7+
public override string OrderByKey => ModuleName;
8+
79
public override bool Equals(object? obj)
810
=> obj is DataLoaderModuleInfo other && Equals(other);
911

src/HotChocolate/Core/src/Types.Analyzers/Models/InterfaceTypeExtensionInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public sealed class InterfaceTypeExtensionInfo(
2323

2424
public ImmutableArray<Resolver> Resolvers { get; } = resolvers;
2525

26+
public override string OrderByKey => Name;
27+
2628
public override bool Equals(object? obj)
2729
=> obj is ObjectTypeExtensionInfo other && Equals(other);
2830

src/HotChocolate/Core/src/Types.Analyzers/Models/ModuleInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ public sealed class ModuleInfo(string moduleName, ModuleOptions options) : Synta
66

77
public ModuleOptions Options { get; } = options;
88

9+
public override string OrderByKey => ModuleName;
10+
911
public override bool Equals(object? obj)
1012
=> obj is ModuleInfo other && Equals(other);
1113

src/HotChocolate/Core/src/Types.Analyzers/Models/ObjectTypeExtensionInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public sealed class ObjectTypeExtensionInfo(
2626

2727
public ImmutableArray<Resolver> Resolvers { get; } = resolvers;
2828

29+
public override string OrderByKey => Name;
30+
2931
public override bool Equals(object? obj)
3032
=> obj is ObjectTypeExtensionInfo other && Equals(other);
3133

src/HotChocolate/Core/src/Types.Analyzers/Models/OperationInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ public sealed class OperationInfo(OperationType type, string typeName, string me
88

99
public string MethodName { get; } = methodName;
1010

11+
public override string OrderByKey => TypeName;
12+
1113
public override bool Equals(object? obj)
1214
=> obj is OperationInfo other && Equals(other);
1315

src/HotChocolate/Core/src/Types.Analyzers/Models/OperationRegistrationInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ public sealed class OperationRegistrationInfo(OperationType type, string typeNam
66

77
public string TypeName { get; } = typeName;
88

9+
public override string OrderByKey => TypeName;
10+
911
public override bool Equals(object? obj)
1012
=> obj is OperationRegistrationInfo other && Equals(other);
1113

src/HotChocolate/Core/src/Types.Analyzers/Models/RegisterDataLoaderInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ public sealed class RegisterDataLoaderInfo(string name) : SyntaxInfo
44
{
55
public string Name { get; } = name;
66

7+
public override string OrderByKey => Name;
8+
79
public override bool Equals(object? obj)
810
=> obj is RegisterDataLoaderInfo other && Equals(other);
911

src/HotChocolate/Core/src/Types.Analyzers/Models/RequestMiddlewareInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ public sealed class RequestMiddlewareInfo(
2121

2222
public List<RequestMiddlewareParameterInfo> InvokeParameters { get; } = invokeParameters;
2323

24+
public override string OrderByKey => Name;
25+
2426
public override bool Equals(object? obj)
2527
=> obj is RequestMiddlewareInfo other && Equals(other);
2628

src/HotChocolate/Core/src/Types.Analyzers/Models/SyntaxInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace HotChocolate.Types.Analyzers.Models;
55

66
public abstract class SyntaxInfo : IEquatable<SyntaxInfo>
77
{
8+
public abstract string OrderByKey { get; }
9+
810
public ImmutableArray<Diagnostic> Diagnostics { get; private set; } = ImmutableArray<Diagnostic>.Empty;
911

1012
public void AddDiagnostic(Diagnostic diagnostic)

src/HotChocolate/Core/src/Types.Analyzers/Models/TypeExtensionInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ public sealed class TypeExtensionInfo(string name, bool isStatic, OperationType
88

99
public OperationType Type { get; } = type;
1010

11+
public override string OrderByKey => Name;
12+
1113
public override bool Equals(object? obj)
1214
=> obj is TypeExtensionInfo other && Equals(other);
1315

src/HotChocolate/Core/src/Types.Analyzers/Models/TypeInfo.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ public sealed class TypeInfo(string name) : SyntaxInfo
44
{
55
public string Name { get; } = name;
66

7+
public override string OrderByKey => Name;
8+
79
public override bool Equals(object? obj)
810
=> obj is TypeInfo other && Equals(other);
911

src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ internal static partial class TestHelper
2121

2222
public static Snapshot GetGeneratedSourceSnapshot([StringSyntax("csharp")] string sourceText)
2323
{
24-
// Parse the provided string into a C# syntax tree.
25-
var syntaxTree = CSharpSyntaxTree.ParseText(sourceText);
24+
return GetGeneratedSourceSnapshot([sourceText]);
25+
}
2626

27+
public static Snapshot GetGeneratedSourceSnapshot(string[] sourceTexts)
28+
{
2729
IEnumerable<PortableExecutableReference> references =
2830
[
2931
#if NET8_0
@@ -48,7 +50,7 @@ public static Snapshot GetGeneratedSourceSnapshot([StringSyntax("csharp")] strin
4850
// Create a Roslyn compilation for the syntax tree.
4951
var compilation = CSharpCompilation.Create(
5052
assemblyName: "Tests",
51-
syntaxTrees: [syntaxTree],
53+
syntaxTrees: sourceTexts.Select(s => CSharpSyntaxTree.ParseText(s)),
5254
references);
5355

5456
// Create an instance of our GraphQLServerGenerator incremental source generator.
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
namespace HotChocolate.Types;
2+
3+
public class TypeModuleSyntaxGeneratorTests
4+
{
5+
[Fact]
6+
public async Task GenerateSource_TypeModuleOrdering_MatchesSnapshot()
7+
{
8+
await TestHelper.GetGeneratedSourceSnapshot(
9+
[
10+
"""
11+
using HotChocolate.Types;
12+
13+
namespace TestNamespace;
14+
15+
internal class ATestBType: ObjectType<ATestB>;
16+
internal record ATestB(int Id);
17+
""",
18+
"""
19+
using HotChocolate.Types;
20+
21+
namespace TestNamespace;
22+
23+
internal class ATestAType : ObjectType<ATestA>;
24+
internal record ATestA(int Id);
25+
""",
26+
"""
27+
using HotChocolate.Types;
28+
29+
namespace TestNamespace;
30+
31+
[ObjectType<ATestBAttr>]
32+
internal static partial class ATestBAttrType;
33+
internal record ATestBAttr(int Id);
34+
""",
35+
"""
36+
using HotChocolate.Types;
37+
38+
namespace TestNamespace;
39+
40+
[ObjectType<ATestAAttr>]
41+
internal static partial class ATestAAttrType;
42+
internal record ATestAAttr(int Id);
43+
""",
44+
"""
45+
using HotChocolate.Types;
46+
47+
namespace TestNamespace;
48+
49+
internal class ATestBExtType : ObjectTypeExtension<ATestBExt>;
50+
internal record ATestBExt(int Id);
51+
""",
52+
"""
53+
using HotChocolate.Types;
54+
55+
namespace TestNamespace;
56+
57+
internal class ATestAExtType : ObjectTypeExtension<ATestAExt>;
58+
internal record ATestAExt(int Id);
59+
""",
60+
"""
61+
using HotChocolate.Types;
62+
63+
namespace TestNamespace;
64+
65+
[ExtendObjectType<ATestBExtAttr>]
66+
internal class ATestBExtAttrType;
67+
internal record ATestBExtAttr(int Id);
68+
""",
69+
"""
70+
using HotChocolate.Types;
71+
72+
namespace TestNamespace;
73+
74+
[ExtendObjectType<ATestAExtAttr>]
75+
internal class ATestAExtAttrType;
76+
internal record ATestAExtAttr(int Id);
77+
""",
78+
"""
79+
using System.Collections.Generic;
80+
using System.Threading;
81+
using System.Threading.Tasks;
82+
using GreenDonut;
83+
84+
namespace TestNamespace;
85+
86+
internal class TestBDataLoader(
87+
IBatchScheduler batchScheduler,
88+
DataLoaderOptions options)
89+
: BatchDataLoader<int, object>(batchScheduler, options)
90+
{
91+
protected override async Task<IReadOnlyDictionary<int, object>> LoadBatchAsync(
92+
IReadOnlyList<int> ids,
93+
CancellationToken cancellationToken)
94+
{
95+
return await Task.FromResult(new Dictionary<int, object>());
96+
}
97+
}
98+
""",
99+
"""
100+
using System.Collections.Generic;
101+
using System.Threading;
102+
using System.Threading.Tasks;
103+
using GreenDonut;
104+
105+
namespace TestNamespace;
106+
107+
internal class TestADataLoader(
108+
IBatchScheduler batchScheduler,
109+
DataLoaderOptions options)
110+
: BatchDataLoader<int, object>(batchScheduler, options)
111+
{
112+
protected override async Task<IReadOnlyDictionary<int, object>> LoadBatchAsync(
113+
IReadOnlyList<int> ids,
114+
CancellationToken cancellationToken)
115+
{
116+
return await Task.FromResult(new Dictionary<int, object>());
117+
}
118+
}
119+
""",
120+
"""
121+
using System.Collections.Generic;
122+
using System.Threading;
123+
using System.Threading.Tasks;
124+
using GreenDonut;
125+
126+
namespace TestNamespace;
127+
128+
internal static class TestBDataLoaderAttr
129+
{
130+
[DataLoader]
131+
public static async Task<IReadOnlyDictionary<int, object>> GetObjectByIdBAsync(
132+
IReadOnlyList<int> ids,
133+
CancellationToken cancellationToken)
134+
{
135+
return await Task.FromResult(new Dictionary<int, object>());
136+
}
137+
}
138+
""",
139+
"""
140+
using System.Collections.Generic;
141+
using System.Threading;
142+
using System.Threading.Tasks;
143+
using GreenDonut;
144+
145+
namespace TestNamespace;
146+
147+
internal static class TestADataLoaderAttr
148+
{
149+
[DataLoader]
150+
public static async Task<IReadOnlyDictionary<int, object>> GetObjectByIdAAsync(
151+
IReadOnlyList<int> ids,
152+
CancellationToken cancellationToken)
153+
{
154+
return await Task.FromResult(new Dictionary<int, object>());
155+
}
156+
}
157+
"""
158+
]).MatchMarkdownAsync();
159+
}
160+
}

0 commit comments

Comments
 (0)