@@ -36,6 +36,14 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
3636 . Where ( static m => m is not null )
3737 . Combine ( enabledProvider ) ;
3838
39+ // Custom test attributes that inherit from BaseTestAttribute
40+ var customTestMethodsProvider = context . SyntaxProvider
41+ . CreateSyntaxProvider (
42+ predicate : static ( node , _ ) => node is MethodDeclarationSyntax { AttributeLists . Count : > 0 } ,
43+ transform : static ( ctx , _ ) => GetCustomTestMethodMetadata ( ctx ) )
44+ . Where ( static m => m is not null )
45+ . Combine ( enabledProvider ) ;
46+
3947 var inheritsTestsClassesProvider = context . SyntaxProvider
4048 . ForAttributeWithMetadataName (
4149 "TUnit.Core.InheritsTestsAttribute" ,
@@ -55,6 +63,17 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
5563 GenerateTestMethodSource ( context , testMethod ) ;
5664 } ) ;
5765
66+ context . RegisterSourceOutput ( customTestMethodsProvider ,
67+ static ( context , data ) =>
68+ {
69+ var ( testMethod , isEnabled ) = data ;
70+ if ( ! isEnabled )
71+ {
72+ return ;
73+ }
74+ GenerateTestMethodSource ( context , testMethod ) ;
75+ } ) ;
76+
5877 context . RegisterSourceOutput ( inheritsTestsClassesProvider ,
5978 static ( context , data ) =>
6079 {
@@ -67,6 +86,86 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
6786 } ) ;
6887 }
6988
89+ private static TestMethodMetadata ? GetCustomTestMethodMetadata ( GeneratorSyntaxContext context )
90+ {
91+ var methodSyntax = ( MethodDeclarationSyntax ) context . Node ;
92+ var methodSymbol = context . SemanticModel . GetDeclaredSymbol ( methodSyntax ) as IMethodSymbol ;
93+
94+ if ( methodSymbol == null )
95+ {
96+ return null ;
97+ }
98+
99+ // Find the custom test attribute that inherits from BaseTestAttribute
100+ // Skip any attributes defined in TUnit.Core namespace (handled by built-in providers)
101+ AttributeData ? testAttribute = null ;
102+ foreach ( var attr in methodSymbol . GetAttributes ( ) )
103+ {
104+ var attrType = attr . AttributeClass ;
105+ if ( attrType == null )
106+ {
107+ continue ;
108+ }
109+
110+ // Skip built-in TUnit.Core attributes - they're handled by other providers
111+ if ( attrType . ContainingNamespace ? . ToDisplayString ( ) == "TUnit.Core" )
112+ {
113+ continue ;
114+ }
115+
116+ var baseType = attrType . BaseType ;
117+ while ( baseType != null )
118+ {
119+ if ( baseType . ToDisplayString ( ) == "TUnit.Core.BaseTestAttribute" )
120+ {
121+ testAttribute = attr ;
122+ break ;
123+ }
124+ baseType = baseType . BaseType ;
125+ }
126+ if ( testAttribute != null )
127+ {
128+ break ;
129+ }
130+ }
131+
132+ if ( testAttribute == null )
133+ {
134+ return null ;
135+ }
136+
137+ var containingType = methodSymbol . ContainingType ;
138+
139+ if ( containingType == null )
140+ {
141+ return null ;
142+ }
143+
144+ if ( containingType . IsAbstract )
145+ {
146+ return null ;
147+ }
148+
149+ var isGenericType = containingType is { IsGenericType : true , TypeParameters . Length : > 0 } ;
150+ var isGenericMethod = methodSymbol is { IsGenericMethod : true } ;
151+
152+ var ( filePath , lineNumber ) = GetTestMethodSourceLocation ( methodSyntax , testAttribute ) ;
153+
154+ return new TestMethodMetadata
155+ {
156+ MethodSymbol = methodSymbol ,
157+ TypeSymbol = containingType ,
158+ FilePath = filePath ,
159+ LineNumber = lineNumber ,
160+ TestAttribute = testAttribute ,
161+ SemanticModel = context . SemanticModel ,
162+ MethodSyntax = methodSyntax ,
163+ IsGenericType = isGenericType ,
164+ IsGenericMethod = isGenericMethod ,
165+ MethodAttributes = methodSymbol . GetAttributes ( )
166+ } ;
167+ }
168+
70169 private static InheritsTestsClassMetadata ? GetInheritsTestsClassMetadata ( GeneratorAttributeSyntaxContext context )
71170 {
72171 var classSyntax = ( ClassDeclarationSyntax ) context . TargetNode ;
@@ -85,7 +184,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
85184 {
86185 TypeSymbol = classSymbol ,
87186 ClassSyntax = classSyntax ,
88- Context = context
187+ SemanticModel = context . SemanticModel
89188 } ;
90189 }
91190
@@ -120,7 +219,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
120219 FilePath = filePath ,
121220 LineNumber = lineNumber ,
122221 TestAttribute = context . Attributes . First ( ) ,
123- Context = context ,
222+ SemanticModel = context . SemanticModel ,
124223 MethodSyntax = methodSyntax ,
125224 IsGenericType = isGenericType ,
126225 IsGenericMethod = isGenericMethod ,
@@ -185,7 +284,7 @@ private static void GenerateInheritedTestSources(SourceProductionContext context
185284 FilePath = filePath ,
186285 LineNumber = lineNumber ,
187286 TestAttribute = testAttribute ,
188- Context = classInfo . Context , // Use class context to access Compilation
287+ SemanticModel = classInfo . SemanticModel , // Use class context to access Compilation
189288 MethodSyntax = null , // No syntax for inherited methods
190289 IsGenericType = typeForMetadata . IsGenericType ,
191290 IsGenericMethod = ( concreteMethod ?? method ) . IsGenericMethod ,
@@ -228,14 +327,11 @@ private static void GenerateTestMethodSource(SourceProductionContext context, Te
228327 {
229328 try
230329 {
231- if ( testMethod ? . MethodSymbol == null || testMethod . Context == null )
330+ if ( testMethod ? . MethodSymbol == null || testMethod . SemanticModel ? . Compilation == null )
232331 {
233332 return ;
234333 }
235334
236- // Get compilation from semantic model instead of parameter
237- var compilation = testMethod . Context . Value . SemanticModel . Compilation ;
238-
239335 var writer = new CodeWriter ( ) ;
240336 GenerateFileHeader ( writer ) ;
241337 GenerateTestMetadata ( writer , testMethod ) ;
@@ -274,7 +370,7 @@ private static void GenerateFileHeader(CodeWriter writer)
274370
275371 private static void GenerateTestMetadata ( CodeWriter writer , TestMethodMetadata testMethod )
276372 {
277- var compilation = testMethod . Context ! . Value . SemanticModel . Compilation ;
373+ var compilation = testMethod . SemanticModel ? . Compilation ! ;
278374
279375 var className = testMethod . TypeSymbol . GloballyQualified ( ) ;
280376 var methodName = testMethod . MethodSymbol . Name ;
@@ -352,7 +448,7 @@ private static void GenerateSpecificGenericInstantiation(
352448 string combinationGuid ,
353449 ImmutableArray < ITypeSymbol > typeArguments )
354450 {
355- var compilation = testMethod . Context ! . Value . SemanticModel . Compilation ;
451+ var compilation = testMethod . SemanticModel ? . Compilation ! ;
356452 var methodName = testMethod . MethodSymbol . Name ;
357453 var typeArgsString = string . Join ( ", " , typeArguments . Select ( t => t . GloballyQualified ( ) ) ) ;
358454 var instantiatedMethodName = $ "{ methodName } <{ typeArgsString } >";
@@ -364,7 +460,7 @@ private static void GenerateSpecificGenericInstantiation(
364460 FilePath = testMethod . FilePath ,
365461 LineNumber = testMethod . LineNumber ,
366462 TestAttribute = testMethod . TestAttribute ,
367- Context = testMethod . Context ,
463+ SemanticModel = testMethod . SemanticModel ,
368464 MethodSyntax = testMethod . MethodSyntax ,
369465 IsGenericType = testMethod . IsGenericType ,
370466 IsGenericMethod = false , // We're creating a concrete instantiation
@@ -571,7 +667,7 @@ private static void GenerateTestMetadataInstance(CodeWriter writer, TestMethodMe
571667
572668 private static void GenerateMetadata ( CodeWriter writer , TestMethodMetadata testMethod )
573669 {
574- var compilation = testMethod . Context ! . Value . SemanticModel . Compilation ;
670+ var compilation = testMethod . SemanticModel ? . Compilation ! ;
575671 var methodSymbol = testMethod . MethodSymbol ;
576672
577673
@@ -617,7 +713,7 @@ private static void GenerateMetadata(CodeWriter writer, TestMethodMetadata testM
617713
618714 private static void GenerateMetadataForConcreteInstantiation ( CodeWriter writer , TestMethodMetadata testMethod )
619715 {
620- var compilation = testMethod . Context ! . Value . SemanticModel . Compilation ;
716+ var compilation = testMethod . SemanticModel ? . Compilation ! ;
621717 var methodSymbol = testMethod . MethodSymbol ;
622718
623719
@@ -669,7 +765,7 @@ private static void GenerateMetadataForConcreteInstantiation(CodeWriter writer,
669765
670766 private static void GenerateDataSources ( CodeWriter writer , TestMethodMetadata testMethod )
671767 {
672- var compilation = testMethod . Context ! . Value . SemanticModel . Compilation ;
768+ var compilation = testMethod . SemanticModel ? . Compilation ! ;
673769 var methodSymbol = testMethod . MethodSymbol ;
674770 var typeSymbol = testMethod . TypeSymbol ;
675771
@@ -1573,7 +1669,7 @@ private static void GeneratePropertyInjections(CodeWriter writer, INamedTypeSymb
15731669
15741670 private static void GeneratePropertyDataSources ( CodeWriter writer , TestMethodMetadata testMethod )
15751671 {
1576- var compilation = testMethod . Context ! . Value . SemanticModel . Compilation ;
1672+ var compilation = testMethod . SemanticModel ? . Compilation ! ;
15771673 var typeSymbol = testMethod . TypeSymbol ;
15781674 var currentType = typeSymbol ;
15791675 var processedProperties = new HashSet < string > ( ) ;
@@ -2791,7 +2887,7 @@ private static void GenerateGenericTestWithConcreteTypes(
27912887 string className ,
27922888 string combinationGuid )
27932889 {
2794- var compilation = testMethod . Context ! . Value . SemanticModel . Compilation ;
2890+ var compilation = testMethod . SemanticModel ? . Compilation ! ;
27952891 var methodName = testMethod . MethodSymbol . Name ;
27962892
27972893 writer . AppendLine ( "// Create generic metadata with concrete type registrations" ) ;
@@ -4270,7 +4366,7 @@ private static void GenerateConcreteTestMetadata(
42704366 ITypeSymbol [ ] typeArguments ,
42714367 AttributeData ? specificArgumentsAttribute = null )
42724368 {
4273- var compilation = testMethod . Context ! . Value . SemanticModel . Compilation ;
4369+ var compilation = testMethod . SemanticModel ? . Compilation ! ;
42744370 var methodName = testMethod . MethodSymbol . Name ;
42754371
42764372 // Separate class type arguments from method type arguments
@@ -4490,7 +4586,7 @@ private static void GenerateConcreteMetadataWithFilteredDataSources(
44904586 AttributeData ? specificArgumentsAttribute ,
44914587 ITypeSymbol [ ] typeArguments )
44924588 {
4493- var compilation = testMethod . Context ! . Value . SemanticModel . Compilation ;
4589+ var compilation = testMethod . SemanticModel ? . Compilation ! ;
44944590 var methodSymbol = testMethod . MethodSymbol ;
44954591 var typeSymbol = testMethod . TypeSymbol ;
44964592
@@ -4809,7 +4905,7 @@ private static void GenerateConcreteTestMetadataForNonGeneric(
48094905 AttributeData ? classDataSourceAttribute ,
48104906 AttributeData ? methodDataSourceAttribute )
48114907 {
4812- var compilation = testMethod . Context ! . Value . SemanticModel . Compilation ;
4908+ var compilation = testMethod . SemanticModel ? . Compilation ! ;
48134909 var methodName = testMethod . MethodSymbol . Name ;
48144910
48154911 writer . AppendLine ( $ "var metadata = new global::TUnit.Core.TestMetadata<{ className } >") ;
@@ -5002,6 +5098,6 @@ public class InheritsTestsClassMetadata
50025098{
50035099 public required INamedTypeSymbol TypeSymbol { get ; init ; }
50045100 public required ClassDeclarationSyntax ClassSyntax { get ; init ; }
5005- public GeneratorAttributeSyntaxContext Context { get ; init ; }
5101+ public SemanticModel SemanticModel { get ; init ; }
50065102}
50075103
0 commit comments