diff --git a/TUnit.Analyzers.Tests/DataSourceGeneratorAnalyzerTests.cs b/TUnit.Analyzers.Tests/DataSourceGeneratorAnalyzerTests.cs index f9e1089d32..2715ab44b8 100644 --- a/TUnit.Analyzers.Tests/DataSourceGeneratorAnalyzerTests.cs +++ b/TUnit.Analyzers.Tests/DataSourceGeneratorAnalyzerTests.cs @@ -118,4 +118,45 @@ public void SomeTest() .WithArguments("string", "int") ); } + + [Test] + public async Task Custom_Generic_DataSource_With_IDataSourceAttribute_No_Error() + { + // This test reproduces the issue from GitHub issue #4812 + // Custom generic attribute implementing IDataSourceAttribute where the generic parameter + // doesn't directly match test parameter types, but the implementation returns compatible types + await Verifier + .VerifyAnalyzerAsync( + """ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + using TUnit.Core; + + namespace TUnit.Core; + + // Custom data source where T doesn't match test parameter types directly + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public class CustomDataAttribute : Attribute, IDataSourceAttribute + { + public bool SkipIfEmpty { get; set; } + + public async IAsyncEnumerable>> GetDataRowsAsync(DataGeneratorMetadata dataGeneratorMetadata) + { + // Returns Foo instances as the test expects, not T + yield return () => Task.FromResult([new Foo()]); + } + } + + public class Foo { } + + public class Tests + { + [Test] + [CustomData] // T is string, but method expects Foo - should not error + public void TestMethod(Foo data) { } + } + """ + ); + } } diff --git a/TUnit.Analyzers/TestDataAnalyzer.cs b/TUnit.Analyzers/TestDataAnalyzer.cs index 7451f1d3d4..4ae457bc43 100644 --- a/TUnit.Analyzers/TestDataAnalyzer.cs +++ b/TUnit.Analyzers/TestDataAnalyzer.cs @@ -910,15 +910,13 @@ private void CheckDataGenerator(SymbolAnalysisContext context, } } - // Final fallback: if no specific data source generator base type found, use the attribute's own type arguments - if (typeArguments.IsEmpty && attribute.AttributeClass?.TypeArguments.IsEmpty == false) - { - typeArguments = attribute.AttributeClass.TypeArguments; - } + // Don't fall back to the attribute's own type arguments - for custom generic attributes + // implementing IDataSourceAttribute, the generic parameters may not correspond to the + // test parameter types (they could be used for other purposes in the implementation) } - // If still no type arguments (like ArgumentsAttribute which returns object?[]?), - // skip compile-time type checking as it will be validated at runtime + // If no type arguments found (like ArgumentsAttribute which returns object?[]?, or custom + // generic data source attributes), skip compile-time type checking as it will be validated at runtime if (typeArguments.IsEmpty) { return;