Skip to content
Merged
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
575 changes: 0 additions & 575 deletions TUnit.Core.SourceGenerator.Tests/!newname!

This file was deleted.

1,248 changes: 1,248 additions & 0 deletions TUnit.Core.SourceGenerator.Tests/DecimalArgumentTests.Test.verified.txt

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions TUnit.Core.SourceGenerator.Tests/DecimalArgumentTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using TUnit.Core.SourceGenerator.Tests.Options;

namespace TUnit.Core.SourceGenerator.Tests;

internal class DecimalArgumentTests : TestsBase
{
[Test]
public Task Test() => RunTest(Path.Combine(Git.RootDirectory.FullName,
"TUnit.TestProject",
"DecimalArgumentTests.cs"), _ => Task.CompletedTask);
}
32 changes: 29 additions & 3 deletions TUnit.Core.SourceGenerator/CodeGenerationHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ public static string GenerateAttributeInstantiation(AttributeData attr, Immutabl
if (isDecimalType &&
syntaxArguments != null && syntaxIndex < syntaxArguments.Count)
{
var originalText = syntaxArguments[syntaxIndex].Expression.ToString();
var syntaxExpression = syntaxArguments[syntaxIndex].Expression;
var originalText = syntaxExpression.ToString();
syntaxIndex++;

// Skip special handling for null values
Expand All @@ -127,6 +128,15 @@ public static string GenerateAttributeInstantiation(AttributeData attr, Immutabl
return TypedConstantParser.GetRawTypedConstantValue(v, paramType);
}

// Check if it's a constant reference (identifier) rather than a literal
// Identifiers don't contain dots, parentheses, or other operators
if (syntaxExpression is NameSyntax)
{
// For constant references, use the actual value from TypedConstant
elementIndex++;
return TypedConstantParser.GetRawTypedConstantValue(v, paramType);
}

// For numeric literals, remove any suffix and add 'm' for decimal
originalText = originalText.TrimEnd('d', 'D', 'f', 'F', 'l', 'L', 'u', 'U', 'm', 'M');
return $"{originalText}m";
Expand All @@ -151,7 +161,8 @@ public static string GenerateAttributeInstantiation(AttributeData attr, Immutabl
if (isDecimalType &&
syntaxArguments != null && syntaxIndex < syntaxArguments.Count)
{
var originalText = syntaxArguments[syntaxIndex].Expression.ToString();
var syntaxExpression = syntaxArguments[syntaxIndex].Expression;
var originalText = syntaxExpression.ToString();
syntaxIndex++;

// Skip special handling for null values
Expand All @@ -166,6 +177,13 @@ public static string GenerateAttributeInstantiation(AttributeData attr, Immutabl
syntaxIndex--; // Back up so normal processing can handle it
argStrings.Add(TypedConstantParser.GetRawTypedConstantValue(arg, paramType));
}
// Check if it's a constant reference (identifier) rather than a literal
// Identifiers don't contain dots, parentheses, or other operators
else if (syntaxExpression is NameSyntax)
{
// For constant references, use the actual value from TypedConstant
argStrings.Add(TypedConstantParser.GetRawTypedConstantValue(arg, paramType));
}
else
{
// For numeric literals, remove any suffix and add 'm' for decimal
Expand All @@ -188,7 +206,8 @@ public static string GenerateAttributeInstantiation(AttributeData attr, Immutabl
if (paramType?.SpecialType == SpecialType.System_Decimal &&
syntaxArguments != null && syntaxIndex < syntaxArguments.Count)
{
var originalText = syntaxArguments[syntaxIndex].Expression.ToString();
var syntaxExpression = syntaxArguments[syntaxIndex].Expression;
var originalText = syntaxExpression.ToString();
syntaxIndex++;
// Check if it's a string literal (starts and ends with quotes)
if (originalText.StartsWith("\"") && originalText.EndsWith("\""))
Expand All @@ -197,6 +216,13 @@ public static string GenerateAttributeInstantiation(AttributeData attr, Immutabl
syntaxIndex--; // Back up so normal processing can handle it
argStrings.Add(TypedConstantParser.GetRawTypedConstantValue(arg, paramType));
}
// Check if it's a constant reference (identifier) rather than a literal
// Identifiers don't contain dots, parentheses, or other operators
else if (syntaxExpression is NameSyntax)
{
// For constant references, use the actual value from TypedConstant
argStrings.Add(TypedConstantParser.GetRawTypedConstantValue(arg, paramType));
}
else
{
// For numeric literals, remove any suffix and add 'm' for decimal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ public static class AotConversionHelper
public static bool HasConversionOperators(ITypeSymbol type)
{
var members = type.GetMembers();
return members.Any(m => m is IMethodSymbol method &&
method.Name is "op_Implicit" or "op_Explicit" &&
method.IsStatic);
return members.Any(m => m is IMethodSymbol { Name: "op_Implicit" or "op_Explicit", IsStatic: true });
}

/// <summary>
Expand All @@ -62,9 +60,7 @@ method.Name is "op_Implicit" or "op_Explicit" &&
var members = type.GetMembers();
foreach (var member in members)
{
if (member is IMethodSymbol method &&
method.Name is "op_Implicit" or "op_Explicit" &&
method is { IsStatic: true, Parameters.Length: 1 })
if (member is IMethodSymbol { Name: "op_Implicit" or "op_Explicit" } method and { IsStatic: true, Parameters.Length: 1 })
{
yield return (method, method.ReturnType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,13 @@ public override SyntaxNode VisitNamespaceDeclaration(NamespaceDeclarationSyntax
var symbol = node.GetSymbolInfo(semanticModel);

// Special handling for double/float special constants (NaN, PositiveInfinity, NegativeInfinity)
if (symbol is IFieldSymbol fieldSymbol && fieldSymbol.IsConst)
if (symbol is IFieldSymbol { IsConst: true, ContainingType.SpecialType: SpecialType.System_Double or SpecialType.System_Single, HasConstantValue: true } fieldSymbol)
// Get the constant value and use the helper to create the appropriate syntax
{
var containingType = fieldSymbol.ContainingType;
if (containingType?.SpecialType is SpecialType.System_Double or SpecialType.System_Single)
var specialSyntax = SpecialFloatingPointValuesHelper.TryCreateSpecialFloatingPointSyntax(fieldSymbol.ConstantValue);
if (specialSyntax != null)
{
// Get the constant value and use the helper to create the appropriate syntax
if (fieldSymbol.HasConstantValue)
{
var specialSyntax = SpecialFloatingPointValuesHelper.TryCreateSpecialFloatingPointSyntax(fieldSymbol.ConstantValue);
if (specialSyntax != null)
{
return specialSyntax;
}
}
return specialSyntax;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)

// Also skip if the target type of the conversion is not publicly accessible
// (unless it's a built-in type)
if (targetType is INamedTypeSymbol namedTargetType &&
namedTargetType.SpecialType == SpecialType.None &&
if (targetType is INamedTypeSymbol { SpecialType: SpecialType.None } namedTargetType &&
namedTargetType.DeclaredAccessibility != Accessibility.Public)
{
return null;
Expand Down
27 changes: 17 additions & 10 deletions TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ private static void GenerateInheritedTestSources(SourceProductionContext context
var (filePath, lineNumber) = GetTestMethodSourceLocation(method, testAttribute, classInfo);

// If the method is from a generic base class, use the constructed version from the inheritance hierarchy
INamedTypeSymbol typeForMetadata = classInfo.TypeSymbol;
var typeForMetadata = classInfo.TypeSymbol;
if (method.ContainingType.IsGenericType && method.ContainingType.IsDefinition)
{
// Find the constructed generic type in the inheritance chain
Expand Down Expand Up @@ -711,10 +711,13 @@ private static void GenerateArgumentsAttributeWithParameterTypes(CodeWriter writ
var formatter = new TypedConstantFormatter();
writer.Append($"new {attrTypeName}(");

if (attr.ConstructorArguments.Length == 1 && attr.ConstructorArguments[0].Kind == TypedConstantKind.Array)
if (attr.ConstructorArguments is
[
{ Kind: TypedConstantKind.Array } _
])
{
var arrayValues = attr.ConstructorArguments[0].Values;
for (int i = 0; i < arrayValues.Length; i++)
for (var i = 0; i < arrayValues.Length; i++)
{
var targetType = i < testMethodParameters.Length ? testMethodParameters[i].Type : null;
writer.Append(formatter.FormatForCode(arrayValues[i], targetType));
Expand All @@ -723,7 +726,7 @@ private static void GenerateArgumentsAttributeWithParameterTypes(CodeWriter writ
}
else
{
for (int i = 0; i < attr.ConstructorArguments.Length; i++)
for (var i = 0; i < attr.ConstructorArguments.Length; i++)
{
var targetType = i < testMethodParameters.Length ? testMethodParameters[i].Type : null;
writer.Append(formatter.FormatForCode(attr.ConstructorArguments[i], targetType));
Expand Down Expand Up @@ -751,18 +754,19 @@ private static void GenerateArgumentsAttributeWithParameterTypes(CodeWriter writ
// Only process positional arguments (exclude named arguments)
var positionalArgs = argumentList.Arguments.Where(a => a.NameEquals == null).ToList();

for (int i = 0; i < positionalArgs.Count; i++)
for (var i = 0; i < positionalArgs.Count; i++)
{
var argumentSyntax = positionalArgs[i];
var expression = argumentSyntax.Expression;

// Get target parameter type
ITypeSymbol? targetParameterType = i < testMethodParameters.Length
var targetParameterType = i < testMethodParameters.Length
? testMethodParameters[i].Type
: null;

// For decimal parameters, preserve source text and add 'm' suffix (only for numeric literals)
if (targetParameterType?.SpecialType == SpecialType.System_Decimal
if (expression is not IdentifierNameSyntax
&& targetParameterType?.SpecialType == SpecialType.System_Decimal
&& expression.Kind() != SyntaxKind.StringLiteralExpression
&& expression.Kind() != SyntaxKind.NullLiteralExpression)
{
Expand Down Expand Up @@ -792,7 +796,7 @@ private static void GenerateArgumentsAttributeWithParameterTypes(CodeWriter writ
writer.AppendLine("{");
writer.Indent();

for (int i = 0; i < namedArgs.Count; i++)
for (var i = 0; i < namedArgs.Count; i++)
{
var namedArg = namedArgs[i];
var propertyName = namedArg.NameEquals!.Name.ToString();
Expand Down Expand Up @@ -1805,7 +1809,7 @@ private static void GenerateTypedInvokers(CodeWriter writer, TestMethodMetadata

// Generate InvokeTypedTest for non-generic tests
var isAsync = IsAsyncMethod(testMethod.MethodSymbol);
if (!testMethod.IsGenericType && !testMethod.IsGenericMethod)
if (testMethod is { IsGenericType: false, IsGenericMethod: false })
{
GenerateConcreteTestInvoker(writer, testMethod, className, methodName, isAsync, hasCancellationToken, parametersFromArgs);
}
Expand All @@ -1828,7 +1832,10 @@ private static void GenerateConcreteTestInvoker(CodeWriter writer, TestMethodMet
// Special case: Single tuple parameter (same as in TestInvoker)
// If we have exactly one parameter that's a tuple type, we need to handle it specially
// In source-generated mode, tuples are always unwrapped into their elements
if (parametersFromArgs.Length == 1 && parametersFromArgs[0].Type is INamedTypeSymbol { IsTupleType: true } singleTupleParam)
if (parametersFromArgs is
[
{ Type: INamedTypeSymbol { IsTupleType: true } singleTupleParam }
])
{
writer.AppendLine("// Special handling for single tuple parameter");
writer.AppendLine($"if (args.Length == {singleTupleParam.TupleElements.Length})");
Expand Down
9 changes: 9 additions & 0 deletions TUnit.TestProject/DecimalArgumentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,13 @@ public async Task Equality4(decimal credit)

await Assert.That(credit).IsEqualTo(123_999.00000000000000001m);
}

private const int BatchSize = 42;

[Test]
[Arguments(BatchSize)]
public async Task TestMethod(decimal batchingSize)
{
await Assert.That(batchingSize).IsEqualTo(BatchSize);
}
}
Loading