Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions TUnit.Core.SourceGenerator.Tests/Bugs/TupleImplicitOperator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Collections.Generic;
using TUnit.Core;

namespace TUnit.Core.SourceGenerator.Tests;

public record Foo
{
public static implicit operator Foo((int Value1, int Value2) tuple) => new();
}

public record Bar
{
public static implicit operator Bar((int A, string B, double C) tuple) => new();
}

public class TupleImplicitOperatorBugTest
{
[Test]
[MethodDataSource(nameof(Data2))]
public void Test1(Foo data)
{
}

[Test]
[MethodDataSource(nameof(Data3))]
public void Test2(Bar data)
{
}

public static IEnumerable<(int, int)> Data2() => [(1, 2), (3, 4)];
public static IEnumerable<(int, string, double)> Data3() => [(1, "test", 3.14), (2, "hello", 2.71)];
}
15 changes: 15 additions & 0 deletions TUnit.Core.SourceGenerator.Tests/TupleImplicitOperatorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace TUnit.Core.SourceGenerator.Tests;

public class TupleImplicitOperatorTests : TestsBase
{
[Test]
public async Task Test()
{
await RunTest(Path.Combine(Git.RootDirectory.FullName,
"TUnit.Core.SourceGenerator.Tests", "Bugs", "TupleImplicitOperator.cs"),
async generatedFiles =>
{
// No verification needed, just that it compiles successfully
});
}
}
14 changes: 13 additions & 1 deletion TUnit.Core.SourceGenerator/Generators/AotConverterGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,19 @@ private void GenerateConverters(SourceProductionContext context, ImmutableArray<
writer.Indent();

writer.AppendLine("if (value == null) return null;");
writer.AppendLine($"if (value is {sourceTypeName} typedValue)");

// Handle tuple types specially to avoid positional pattern issues
var patternTypeName = sourceTypeName;
if (conversion.SourceType.IsTupleType && conversion.SourceType is INamedTypeSymbol namedSourceType)
{
// For tuple types, manually construct the ValueTuple<T1, T2, ...> type name
// to avoid positional pattern errors with tuple literal syntax
var typeArgs = namedSourceType.TypeArguments;
var argTypes = string.Join(", ", typeArgs.Select(t => t.GloballyQualified()));
patternTypeName = $"global::System.ValueTuple<{argTypes}>";
}

writer.AppendLine($"if (value is {patternTypeName} typedValue)");
writer.AppendLine("{");
writer.Indent();
writer.AppendLine($"return ({targetTypeName})typedValue;");
Expand Down
Loading