From e3ffe927f6bf919a355478fa559930fe3be3ae26 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 8 Oct 2025 01:18:05 +0100 Subject: [PATCH] Allow overriding test source location --- .../Generators/TestMetadataGenerator.cs | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs b/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs index dc6433da53..ae72be61cf 100644 --- a/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs +++ b/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs @@ -2300,6 +2300,18 @@ private static (string filePath, int lineNumber) GetTestMethodSourceLocation( MethodDeclarationSyntax methodSyntax, AttributeData testAttribute) { + // Prioritize TestAttribute's File/Line from [CallerFilePath]/[CallerLineNumber] first + var attrFilePath = testAttribute.ConstructorArguments.ElementAtOrDefault(0).Value?.ToString(); + if (!string.IsNullOrEmpty(attrFilePath)) + { + var attrLineNumber = (int?)testAttribute.ConstructorArguments.ElementAtOrDefault(1).Value ?? 0; + if (attrLineNumber > 0) + { + return (attrFilePath!, attrLineNumber); + } + } + + // Fall back to method syntax location var methodLocation = methodSyntax.GetLocation(); var filePath = methodLocation.SourceTree?.FilePath; if (!string.IsNullOrEmpty(filePath)) @@ -2308,14 +2320,7 @@ private static (string filePath, int lineNumber) GetTestMethodSourceLocation( return (filePath!, lineNumber); } - var attrFilePath = testAttribute.ConstructorArguments.ElementAtOrDefault(0).Value?.ToString(); - if (!string.IsNullOrEmpty(attrFilePath)) - { - var attrLineNumber = (int?)testAttribute.ConstructorArguments.ElementAtOrDefault(1).Value ?? - methodLocation.GetLineSpan().StartLinePosition.Line + 1; - return (attrFilePath!, attrLineNumber); - } - + // Final fallback filePath = methodSyntax.SyntaxTree.FilePath ?? ""; var fallbackLineNumber = methodLocation.GetLineSpan().StartLinePosition.Line + 1; return (filePath, fallbackLineNumber); @@ -2326,6 +2331,18 @@ private static (string filePath, int lineNumber) GetTestMethodSourceLocation( AttributeData testAttribute, InheritsTestsClassMetadata classInfo) { + // Prioritize TestAttribute's File/Line from [CallerFilePath]/[CallerLineNumber] first + var attrFilePath = testAttribute.ConstructorArguments.ElementAtOrDefault(0).Value?.ToString(); + if (!string.IsNullOrEmpty(attrFilePath)) + { + var attrLineNumber = (int?)testAttribute.ConstructorArguments.ElementAtOrDefault(1).Value ?? 0; + if (attrLineNumber > 0) + { + return (attrFilePath!, attrLineNumber); + } + } + + // Fall back to method symbol location var methodLocation = method.Locations.FirstOrDefault(); if (methodLocation != null && methodLocation.IsInSource) { @@ -2337,14 +2354,7 @@ private static (string filePath, int lineNumber) GetTestMethodSourceLocation( } } - var attrFilePath = testAttribute.ConstructorArguments.ElementAtOrDefault(0).Value?.ToString(); - if (!string.IsNullOrEmpty(attrFilePath)) - { - var attrLineNumber = (int?)testAttribute.ConstructorArguments.ElementAtOrDefault(1).Value ?? - classInfo.ClassSyntax.GetLocation().GetLineSpan().StartLinePosition.Line + 1; - return (attrFilePath!, attrLineNumber); - } - + // Final fallback to class location var classLocation = classInfo.ClassSyntax.GetLocation(); var derivedFilePath = classLocation.SourceTree?.FilePath ?? classInfo.ClassSyntax.SyntaxTree.FilePath ?? ""; var derivedLineNumber = classLocation.GetLineSpan().StartLinePosition.Line + 1;