diff --git a/Directory.Build.props b/Directory.Build.props
index be8fc71..c08cbcf 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,36 +1,31 @@
-
-
-
- enable
- 1.11.4-preview1
- False
-
-
-
- domn1995
-
- A simple source generator for discriminated unions in C#.
- https://github.com/domn1995/dunet
- Readme.md
- https://github.com/domn1995/dunet
- source; generator; discriminated; union; functional; tagged;
- MIT
- https://github.com/domn1995/dunet/releases
- git
- favicon.png
-
-
-
- true
-
-
-
-
-
-
-
-
-
-
+
+
+ enable
+ 1.11.4-preview1
+ False
+
+
+ domn1995
+
+ A simple source generator for discriminated unions in C#.
+ https://github.com/domn1995/dunet
+ Readme.md
+ https://github.com/domn1995/dunet
+ source; generator; discriminated; union; functional; tagged;
+ MIT
+ https://github.com/domn1995/dunet/releases
+ git
+ favicon.png
+
+
+ true
+
+
+
+
+
+
+
+
diff --git a/benchmark/Dunet.Benchmark/Dunet.Benchmark.csproj b/benchmark/Dunet.Benchmark/Dunet.Benchmark.csproj
index fff40a3..a189558 100644
--- a/benchmark/Dunet.Benchmark/Dunet.Benchmark.csproj
+++ b/benchmark/Dunet.Benchmark/Dunet.Benchmark.csproj
@@ -1,25 +1,21 @@
-
-
- Exe
- net8.0
- enable
- enable
- false
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
-
-
-
-
-
-
-
+
+ Exe
+ net8.0
+ enable
+ enable
+ false
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
diff --git a/benchmark/Dunet.Benchmark/SourceGeneratorBenchmark.cs b/benchmark/Dunet.Benchmark/SourceGeneratorBenchmark.cs
index 1f76fa6..982c28c 100644
--- a/benchmark/Dunet.Benchmark/SourceGeneratorBenchmark.cs
+++ b/benchmark/Dunet.Benchmark/SourceGeneratorBenchmark.cs
@@ -99,7 +99,7 @@ private static CSharpCompilation CreateCompilation(params string[] sources) =>
),
MetadataReference.CreateFromFile(
typeof(UnionAttribute).GetTypeInfo().Assembly.Location
- )
+ ),
],
options: new CSharpCompilationOptions(OutputKind.ConsoleApplication)
);
diff --git a/integration/Dunet.Integration.csproj b/integration/Dunet.Integration.csproj
index 4e7bd0a..d540d26 100644
--- a/integration/Dunet.Integration.csproj
+++ b/integration/Dunet.Integration.csproj
@@ -1,25 +1,22 @@
-
-
- net8.0
- enable
- enable
- false
-
-
-
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
-
+
+ net8.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
diff --git a/samples/AreaCalculator/AreaCalculator.csproj b/samples/AreaCalculator/AreaCalculator.csproj
index 52af506..d2e7eab 100644
--- a/samples/AreaCalculator/AreaCalculator.csproj
+++ b/samples/AreaCalculator/AreaCalculator.csproj
@@ -1,15 +1,12 @@
-
-
- Exe
- net8.0
- enable
- enable
- false
-
-
-
-
-
-
+
+ Exe
+ net8.0
+ enable
+ enable
+ false
+
+
+
+
diff --git a/samples/ExpressionCalculator/ExpressionCalculator.csproj b/samples/ExpressionCalculator/ExpressionCalculator.csproj
index 52af506..d2e7eab 100644
--- a/samples/ExpressionCalculator/ExpressionCalculator.csproj
+++ b/samples/ExpressionCalculator/ExpressionCalculator.csproj
@@ -1,15 +1,12 @@
-
-
- Exe
- net8.0
- enable
- enable
- false
-
-
-
-
-
-
+
+ Exe
+ net8.0
+ enable
+ enable
+ false
+
+
+
+
diff --git a/samples/ExpressionCalculatorWithState/ExpressionCalculatorWithState.csproj b/samples/ExpressionCalculatorWithState/ExpressionCalculatorWithState.csproj
index 52af506..d2e7eab 100644
--- a/samples/ExpressionCalculatorWithState/ExpressionCalculatorWithState.csproj
+++ b/samples/ExpressionCalculatorWithState/ExpressionCalculatorWithState.csproj
@@ -1,15 +1,12 @@
-
-
- Exe
- net8.0
- enable
- enable
- false
-
-
-
-
-
-
+
+ Exe
+ net8.0
+ enable
+ enable
+ false
+
+
+
+
diff --git a/samples/OptionMonad/OptionMonad.csproj b/samples/OptionMonad/OptionMonad.csproj
index 52af506..d2e7eab 100644
--- a/samples/OptionMonad/OptionMonad.csproj
+++ b/samples/OptionMonad/OptionMonad.csproj
@@ -1,15 +1,12 @@
-
-
- Exe
- net8.0
- enable
- enable
- false
-
-
-
-
-
-
+
+ Exe
+ net8.0
+ enable
+ enable
+ false
+
+
+
+
diff --git a/samples/PokemonClient/PokemonClient.csproj b/samples/PokemonClient/PokemonClient.csproj
index 52af506..d2e7eab 100644
--- a/samples/PokemonClient/PokemonClient.csproj
+++ b/samples/PokemonClient/PokemonClient.csproj
@@ -1,15 +1,12 @@
-
-
- Exe
- net8.0
- enable
- enable
- false
-
-
-
-
-
-
+
+ Exe
+ net8.0
+ enable
+ enable
+ false
+
+
+
+
diff --git a/samples/Serialization/Serialization.csproj b/samples/Serialization/Serialization.csproj
index 7b44c45..27b6d2a 100644
--- a/samples/Serialization/Serialization.csproj
+++ b/samples/Serialization/Serialization.csproj
@@ -1,16 +1,13 @@
-
-
- Exe
- net8.0
- enable
- enable
- false
-
-
-
-
-
-
-
+
+ Exe
+ net8.0
+ enable
+ enable
+ false
+
+
+
+
+
diff --git a/src/Dunet.Generator/UnionExtensionsGeneration/UnionExtensionsSourceBuilder.cs b/src/Dunet.Generator/UnionExtensionsGeneration/UnionExtensionsSourceBuilder.cs
index db0230d..0c2fa9b 100644
--- a/src/Dunet.Generator/UnionExtensionsGeneration/UnionExtensionsSourceBuilder.cs
+++ b/src/Dunet.Generator/UnionExtensionsGeneration/UnionExtensionsSourceBuilder.cs
@@ -45,7 +45,7 @@ private static StringBuilder AppendExtensionClassDeclaration(
UnionDeclaration union
) =>
builder.AppendLine(
- $"{union.Accessibility.ToKeyword()} static class {union.Name}MatchExtensions"
+ $"{union.Accessibility.ToKeyword()} static class {union.Name}{union.TypeParameters.Count}MatchExtensions"
);
private static StringBuilder AppendUsingStatements(
diff --git a/src/Dunet.Generator/UnionGeneration/UnionGenerator.cs b/src/Dunet.Generator/UnionGeneration/UnionGenerator.cs
index 57f1f72..afee0e5 100644
--- a/src/Dunet.Generator/UnionGeneration/UnionGenerator.cs
+++ b/src/Dunet.Generator/UnionGeneration/UnionGenerator.cs
@@ -44,9 +44,7 @@ private static void Emit(SourceProductionContext context, UnionDeclaration union
var union = UnionSourceBuilder.Build(unionRecord);
context.AddSource(
- unionRecord.TypeParameters.Count == 0
- ? $"{unionRecord.Namespace}.{unionRecord.Name}.g.cs"
- : $"{unionRecord.Namespace}.{unionRecord.Name}.{unionRecord.TypeParameters.Count}.g.cs",
+ $"{unionRecord.Namespace}.{unionRecord.Name}{unionRecord.TypeParameters.Count}.g.cs",
SourceText.From(union, Encoding.UTF8)
);
@@ -59,7 +57,7 @@ private static void Emit(SourceProductionContext context, UnionDeclaration union
{
var matchExtensions = UnionExtensionsSourceBuilder.GenerateExtensions(unionRecord);
context.AddSource(
- $"{unionRecord.Namespace}.{unionRecord.Name}MatchExtensions.g.cs",
+ $"{unionRecord.Namespace}.{unionRecord.Name}{unionRecord.TypeParameters.Count}MatchExtensions.g.cs",
SourceText.From(matchExtensions, Encoding.UTF8)
);
}
diff --git a/test/Dunet.Test.csproj b/test/Dunet.Test.csproj
index 29b2845..f1b3de9 100644
--- a/test/Dunet.Test.csproj
+++ b/test/Dunet.Test.csproj
@@ -1,29 +1,25 @@
-
-
- net8.0
- enable
- enable
- false
-
-
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
-
-
-
-
-
-
+
+ net8.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
diff --git a/test/UnionExtensionsGeneration/MultipleGenericUnionsExtensionsTests.cs b/test/UnionExtensionsGeneration/MultipleGenericUnionsExtensionsTests.cs
new file mode 100644
index 0000000..73db80a
--- /dev/null
+++ b/test/UnionExtensionsGeneration/MultipleGenericUnionsExtensionsTests.cs
@@ -0,0 +1,390 @@
+namespace Dunet.Test.UnionExtensionsGeneration;
+
+public sealed class MultipleGenericUnionsExtensionsTests
+{
+ [Fact]
+ public void CanGenerateMatchExtensionsForTwoGenericUnionsWithSameName()
+ {
+ // Arrange.
+ var resultCs = """
+ using Dunet;
+
+ namespace Results;
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error();
+ }
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error(TError ErrorValue);
+ }
+ """;
+
+ var programCs = """
+ using Results;
+
+ var result1 = new Result.Ok(42);
+ var value1 = result1.Match(
+ ok => ok.Value * 2,
+ error => 0
+ );
+
+ var result2 = new Result.Ok("success");
+ var value2 = result2.Match(
+ ok => ok.Value.Length,
+ error => -1
+ );
+ """;
+
+ // Act.
+ var result = Compiler.Compile(resultCs, programCs);
+
+ // Assert.
+ using var scope = new AssertionScope();
+ result.CompilationErrors.Should().BeEmpty();
+ result.GenerationErrors.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void CanGenerateMatchExtensionsForThreeGenericUnionsWithSameName()
+ {
+ // Arrange.
+ var responseCs = """
+ using Dunet;
+
+ namespace Responses;
+
+ [Union]
+ public partial record Response
+ {
+ public partial record Success(T Data);
+ public partial record Failure();
+ }
+
+ [Union]
+ public partial record Response
+ {
+ public partial record Success(T Data);
+ public partial record Failure(TError Error);
+ }
+
+ [Union]
+ public partial record Response
+ {
+ public partial record Success(T Data);
+ public partial record Failure(TError Error);
+ public partial record Pending(TMetadata Metadata);
+ }
+ """;
+
+ var programCs = """
+ using Responses;
+
+ var resp1 = new Response.Success(10);
+ var val1 = resp1.Match(
+ success => success.Data + 5,
+ failure => 0
+ );
+
+ var resp2 = new Response.Success("hello");
+ var val2 = resp2.Match(
+ success => success.Data.Length,
+ failure => -failure.Error
+ );
+
+ var resp3 = new Response.Success(true);
+ var val3 = resp3.Match(
+ success => success.Data ? 1 : 0,
+ failure => -1,
+ pending => (int)pending.Metadata
+ );
+ """;
+
+ // Act.
+ var result = Compiler.Compile(responseCs, programCs);
+
+ // Assert.
+ using var scope = new AssertionScope();
+ result.CompilationErrors.Should().BeEmpty();
+ result.GenerationErrors.Should().BeEmpty();
+ }
+
+ [Theory]
+ [InlineData("Task")]
+ [InlineData("ValueTask")]
+ public void CanGenerateMatchAsyncExtensionsForMultipleGenericUnionsWithSameName(string taskType)
+ {
+ // Arrange.
+ var resultCs = """
+ using Dunet;
+
+ namespace Results;
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error();
+ }
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error(TError ErrorValue);
+ }
+ """;
+
+ var programCs = $$"""
+ using System.Threading.Tasks;
+ using Results;
+
+ async Task Test1()
+ {
+ {{taskType}}> task1 = {{taskType}}.FromResult>(new Result.Ok(42));
+ var value1 = await task1.MatchAsync(
+ ok => Task.FromResult(ok.Value * 2),
+ error => Task.FromResult(0)
+ );
+ }
+
+ async Task Test2()
+ {
+ {{taskType}}> task2 = {{taskType}}.FromResult>(new Result.Ok("success"));
+ var value2 = await task2.MatchAsync(
+ ok => Task.FromResult(ok.Value.Length),
+ error => Task.FromResult(-1)
+ );
+ }
+ """;
+
+ // Act.
+ var result = Compiler.Compile(resultCs, programCs);
+
+ // Assert.
+ using var scope = new AssertionScope();
+ result.CompilationErrors.Should().BeEmpty();
+ result.GenerationErrors.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void MatchExtensionsCorrectlyRouteToDifferentVariants()
+ {
+ // Arrange.
+ var resultCs = """
+ using Dunet;
+
+ namespace Results;
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error();
+ }
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error(TError ErrorValue);
+ }
+ """;
+
+ var programCs = """
+ using Results;
+
+ // Test Result - Ok variant
+ Result r1 = new Result.Ok(100);
+ var r1_result = r1.Match(
+ ok => ok.Value * 10,
+ error => -1
+ );
+ System.Console.WriteLine(r1_result == 1000);
+
+ // Test Result - Error variant
+ Result r2 = new Result.Error();
+ var r2_result = r2.Match(
+ ok => ok.Value * 10,
+ error => -1
+ );
+ System.Console.WriteLine(r2_result == -1);
+
+ // Test Result - Ok variant
+ Result r3 = new Result.Ok("hello");
+ var r3_result = r3.Match(
+ ok => ok.Value.Length,
+ error => -error.ErrorValue.Length
+ );
+ System.Console.WriteLine(r3_result == 5);
+
+ // Test Result - Error variant
+ Result r4 = new Result.Error("failure");
+ var r4_result = r4.Match(
+ ok => ok.Value.Length,
+ error => -error.ErrorValue.Length
+ );
+ System.Console.WriteLine(r4_result == -7);
+ """;
+
+ // Act.
+ var result = Compiler.Compile(resultCs, programCs);
+
+ // Assert.
+ using var scope = new AssertionScope();
+ result.CompilationErrors.Should().BeEmpty();
+ result.GenerationErrors.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void MatchExtensionsWorkWithVoidReturnType()
+ {
+ // Arrange.
+ var resultCs = """
+ using Dunet;
+
+ namespace Results;
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error();
+ }
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error(TError ErrorValue);
+ }
+ """;
+
+ var programCs = """
+ using Results;
+
+ Result r1 = new Result.Ok(42);
+ r1.Match(
+ ok => { System.Console.WriteLine($"Success: {ok.Value}"); },
+ error => { System.Console.WriteLine("Error"); }
+ );
+
+ Result r2 = new Result.Error("oops");
+ r2.Match(
+ ok => { System.Console.WriteLine($"Success: {ok.Value}"); },
+ error => { System.Console.WriteLine($"Error: {error.ErrorValue}"); }
+ );
+ """;
+
+ // Act.
+ var result = Compiler.Compile(resultCs, programCs);
+
+ // Assert.
+ using var scope = new AssertionScope();
+ result.CompilationErrors.Should().BeEmpty();
+ result.GenerationErrors.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void MultipleGenericUnionsWithSameName_EachGetUniqueExtensions()
+ {
+ // Arrange.
+ var statusCs = """
+ using Dunet;
+
+ namespace Status;
+
+ [Union]
+ public partial record Status
+ {
+ public partial record Active(T Info);
+ public partial record Inactive();
+ }
+
+ [Union]
+ public partial record Status
+ {
+ public partial record Active(T Info);
+ public partial record Inactive(TReason Reason);
+ }
+ """;
+
+ var programCs = """
+ using Status;
+
+ var s1 = new Status.Active(10);
+ // Each should have unique Match extension based on variant count
+ var r1 = s1.Match(a => a.Info, i => 0);
+
+ var s2 = new Status.Inactive("paused");
+ // This should have different Match extension signature
+ var r2 = s2.Match(a => a.Info.Length, i => i.Reason.Length);
+ """;
+
+ // Act.
+ var result = Compiler.Compile(statusCs, programCs);
+
+ // Assert.
+ using var scope = new AssertionScope();
+ result.CompilationErrors.Should().BeEmpty();
+ result.GenerationErrors.Should().BeEmpty();
+ }
+
+ [Theory]
+ [InlineData("Task")]
+ [InlineData("ValueTask")]
+ public void MatchAsyncExtensionsWorkWithAsyncLambdas(string taskType)
+ {
+ // Arrange.
+ var resultCs = """
+ using Dunet;
+
+ namespace Results;
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error();
+ }
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error(TError ErrorValue);
+ }
+ """;
+
+ var programCs = $$"""
+ using System.Threading.Tasks;
+ using Results;
+
+ {{taskType}}> task1 = {{taskType}}.FromResult>(new Result.Ok(42));
+ var value1 = await task1.MatchAsync(
+ async ok => { await Task.Delay(0); return ok.Value * 2; },
+ async error => { await Task.Delay(0); return 0; }
+ );
+
+ {{taskType}}> task2 = {{taskType}}.FromResult>(new Result.Ok("hello"));
+ var value2 = await task2.MatchAsync(
+ async ok => { await Task.Delay(0); return ok.Value.Length; },
+ async error => { await Task.Delay(0); return -1; }
+ );
+ """;
+
+ // Act.
+ var result = Compiler.Compile(resultCs, programCs);
+
+ // Assert.
+ using var scope = new AssertionScope();
+ result.CompilationErrors.Should().BeEmpty();
+ result.GenerationErrors.Should().BeEmpty();
+ }
+}
diff --git a/test/UnionGeneration/GenerationTests.cs b/test/UnionGeneration/GenerationTests.cs
index 62dbc7b..b75fd57 100644
--- a/test/UnionGeneration/GenerationTests.cs
+++ b/test/UnionGeneration/GenerationTests.cs
@@ -214,13 +214,13 @@ partial record Failure(Exception Error);
}
[Fact]
- public void GenericUnionTypesMayHaveRequiredProperties()
+ public void GenericUnionsWithTheSameName()
{
// Arrange.
var programCs = """
using Dunet;
using System;
-
+
Result result1 = Result.Ok(Guid.NewGuid());
Result result2 = Result.Error(new Exception("Boom!"));
diff --git a/test/UnionGeneration/MultipleGenericUnionsGenerationTests.cs b/test/UnionGeneration/MultipleGenericUnionsGenerationTests.cs
new file mode 100644
index 0000000..f7db348
--- /dev/null
+++ b/test/UnionGeneration/MultipleGenericUnionsGenerationTests.cs
@@ -0,0 +1,231 @@
+namespace Dunet.Test.UnionGeneration;
+
+public sealed class MultipleGenericUnionsGenerationTests
+{
+ [Fact]
+ public void TwoGenericUnionsWithSameNameDifferentTypeParameters()
+ {
+ // Arrange.
+ var programCs = """
+ using Dunet;
+
+ Result result1 = new Result.Ok("Success");
+ Result result2 = new Result.Ok(42);
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error();
+ }
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error(TError ErrorValue);
+ }
+ """;
+
+ // Act.
+ var result = Compiler.Compile(programCs);
+
+ // Assert.
+ using var scope = new AssertionScope();
+ result.CompilationErrors.Should().BeEmpty();
+ result.GenerationDiagnostics.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void ThreeGenericUnionsWithSameNameIncreasingTypeParameters()
+ {
+ // Arrange.
+ var programCs = """
+ using Dunet;
+
+ Response response1 = new Response.Success("data");
+ Response response2 = new Response.Success("data");
+ Response response3 = new Response.Success("data");
+
+ [Union]
+ public partial record Response
+ {
+ public partial record Success(T Data);
+ public partial record Failure();
+ }
+
+ [Union]
+ public partial record Response
+ {
+ public partial record Success(T Data);
+ public partial record Failure(TError Error);
+ }
+
+ [Union]
+ public partial record Response
+ {
+ public partial record Success(T Data);
+ public partial record Failure(TError Error);
+ public partial record Pending(TMetadata Metadata);
+ }
+ """;
+
+ // Act.
+ var result = Compiler.Compile(programCs);
+
+ // Assert.
+ using var scope = new AssertionScope();
+ result.CompilationErrors.Should().BeEmpty();
+ result.GenerationDiagnostics.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void MultipleGenericUnionsWithSameNameInDifferentNamespaces()
+ {
+ // Arrange.
+ var resultCS = """
+ using Dunet;
+
+ namespace Results;
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error();
+ }
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error(TError ErrorValue);
+ }
+ """;
+
+ var programCs = """
+ using Results;
+
+ var result1 = new Result.Ok("Success");
+ var result2 = new Result.Ok(42);
+ """;
+
+ // Act.
+ var result = Compiler.Compile(resultCS, programCs);
+
+ // Assert.
+ using var scope = new AssertionScope();
+ result.CompilationErrors.Should().BeEmpty();
+ result.GenerationDiagnostics.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void MultipleGenericUnionsWithSameNameWithComplexVariants()
+ {
+ // Arrange.
+ var programCs = """
+ using Dunet;
+ using System;
+
+ Operation op1 = new Operation.Success(42);
+ Operation op2 = new Operation.Success("done");
+
+ [Union]
+ public partial record Operation
+ {
+ public partial record Success(T Result);
+ public partial record InProgress(double PercentComplete);
+ public partial record Cancelled();
+ }
+
+ [Union]
+ public partial record Operation
+ {
+ public partial record Success(T Result);
+ public partial record Failed(TError Error);
+ public partial record Cancelled();
+ }
+ """;
+
+ // Act.
+ var result = Compiler.Compile(programCs);
+
+ // Assert.
+ using var scope = new AssertionScope();
+ result.CompilationErrors.Should().BeEmpty();
+ result.GenerationDiagnostics.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void MultipleGenericUnionsWithSameNameCanInstantiateAllVariants()
+ {
+ // Arrange.
+ var programCs = """
+ using Dunet;
+
+ // Result
+ Result r1_ok = new Result.Ok(42);
+ Result r1_error = new Result.Error();
+
+ // Result
+ Result r2_ok = new Result.Ok("success");
+ Result r2_error = new Result.Error("failed");
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error();
+ }
+
+ [Union]
+ public partial record Result
+ {
+ public partial record Ok(T Value);
+ public partial record Error(TError ErrorValue);
+ }
+ """;
+
+ // Act.
+ var result = Compiler.Compile(programCs);
+
+ // Assert.
+ using var scope = new AssertionScope();
+ result.CompilationErrors.Should().BeEmpty();
+ result.GenerationDiagnostics.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void MultipleGenericUnionsWithSameNameWithConstraints()
+ {
+ // Arrange.
+ var programCs = """
+ using Dunet;
+
+ Container c1 = new Container.Filled("data");
+ Container c2 = new Container.Filled(42);
+
+ [Union]
+ public partial record Container where T : class
+ {
+ public partial record Filled(T Data);
+ public partial record Empty();
+ }
+
+ [Union]
+ public partial record Container where T : struct
+ {
+ public partial record Filled(T Data);
+ public partial record Empty(TMetadata Metadata);
+ }
+ """;
+
+ // Act.
+ var result = Compiler.Compile(programCs);
+
+ // Assert.
+ using var scope = new AssertionScope();
+ result.CompilationErrors.Should().BeEmpty();
+ result.GenerationDiagnostics.Should().BeEmpty();
+ }
+}