Skip to content

TUnit0001 false positive: [MethodDataSource] on a generic test method with type-parameter parameters #7104

Closed

Description

@thomhurst

Summary

The TUnit0001 analyzer ("Attribute argument types ... don't match method parameter types ...") reports an error when a [MethodDataSource] feeds a generic test method whose parameters are type parameters. The test compiles fine once the error is suppressed, and it runs in both source-gen and reflection mode. So the diagnostic is a false positive. Because TUnit0001 is an error, users have to add #pragma warning disable TUnit0001 to get the project to build.

PR #7094 had to add that suppression to its fixture tests/TUnit.TestProject/Bugs/_7092/GenerateGenericTestTupleInferenceTests.cs (GenerateGenericTestMethodDataSourceInferenceTests).

The method does not need [GenerateGenericTest] to hit this. A plain generic method with a single T parameter hits it too.

Repro (current main, b1cceb3)

public class FxAnalyzer
{
    public static IEnumerable<Func<(int, string)>> Rows()
    {
        yield return () => (1, "a");
    }

    public static IEnumerable<Func<int>> SingleRows()
    {
        yield return () => 1;
    }

    [Test]
    [GenerateGenericTest(typeof(long), typeof(string))]
    [MethodDataSource(nameof(Rows))]                 // TUnit0001
    public async Task A_GenerateGeneric_Tuple<T1, T2>(T1 first, T2 second)
        => await Assert.That(first is T1 && second is T2).IsTrue();

    [Test]
    [MethodDataSource(nameof(Rows))]                 // TUnit0001
    public async Task B_Tuple_Only<T1, T2>(T1 first, T2 second)
        => await Assert.That(first is T1 && second is T2).IsTrue();

    [Test]
    [MethodDataSource(nameof(SingleRows))]           // TUnit0001
    public async Task C_Single<T>(T value)
        => await Assert.That(value is T).IsTrue();
}

Build output (dotnet build tests/TUnit.TestProject -f net10.0 with this file added):

FxAnalyzer.cs(18,6): error TUnit0001: Attribute argument types 'int, string' don't match method parameter types 'T1, T2'
FxAnalyzer.cs(26,6): error TUnit0001: Attribute argument types 'int, string' don't match method parameter types 'T1, T2'
FxAnalyzer.cs(34,6): error TUnit0001: Attribute argument types 'int' don't match method parameter types 'T'

With #pragma warning disable TUnit0001 at the top of the file, the project builds and the tests run:

Test Source-gen Reflection (--reflection)
A_GenerateGeneric_Tuple passed <long, string>(1, a) and <int, string>(1, a) passed <Int64, String>(1, a) (the missing <int, string> is fixed by #7094)
B_Tuple_Only passed <int, string>(1, a) passed (1, a)
C_Single not discovered (see the note below) passed (1)

Expected

No TUnit0001 for these. A data value of type int can always bind to a method type parameter T, because T is inferred from the data or supplied by [GenerateGenericTest]. The analyzer should only report a mismatch when the types really cannot work, for example when the parameter is a concrete type with no conversion.

Actual

A TUnit0001 error on each [MethodDataSource].

Suspected cause

src/TUnit.Analyzers/TestDataAnalyzer.cs, in the [MethodDataSource] branch (around lines 568-705). After UnwrapTypes, it zips the unwrapped data types with testParameterTypes and calls HasImplicitConversionOrGenericParameter(argument, parameter) on each pair (around line 610). If any pair fails, it reports TUnit0001 and returns.

src/TUnit.Analyzers/Extensions/CompilationExtensions.cs, HasImplicitConversionOrGenericParameter: when parameterType is a bare ITypeParameterSymbol, IsGenericDefinition() is true, so the normal conversion check is skipped. The parameter is not an INamedTypeSymbol either, so the code falls through to the last line, compilation.HasImplicitConversion(int, T1). That is false for an unconstrained type parameter, so any concrete type checked against T fails.

CanConvert in the same analyzer (around line 1010), which handles [Arguments], returns true early when methodParameterType is ITypeParameterSymbol. The [MethodDataSource] path needs the same rule, or HasImplicitConversionOrGenericParameter should return true for a bare type parameter. The later tuple loop (around line 680) already skips IsGenericDefinition() parameters, but the earlier zip check returns before the code gets there.

A regression test belongs in tests/TUnit.Analyzers.Tests (the MethodDataSource analyzer tests). After the fix, the #pragma that #7094 added in tests/TUnit.TestProject/Bugs/_7092/GenerateGenericTestTupleInferenceTests.cs can be removed.

Note: a separate gap with the same fixture

C_Single<T>, whose [MethodDataSource] returns IEnumerable<Func<int>>, is not discovered at all in source-gen mode, but it runs in reflection mode. TestMetadataGenerator.InferTypesFromMethodDataSource (src/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs, around line 5788) seems to infer types only from tuple return types. This can be fixed here or split into its own issue.

Related: #7094, #7100, #7102

Activity

  1. added a commit that references this issue on Oct 18, 2026
    b4bcdbe
  2. added a commit that references this issue on Oct 18, 2026
    2a4bc3a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions