Skip to content

Commit

Permalink
#3102: xUnit1019 inappropriately flags usage of IEnumerable of tuple …
Browse files Browse the repository at this point in the history
…as not legal in v3
  • Loading branch information
bradwilson committed Dec 23, 2024
1 parent 6789185 commit 4d90486
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -354,8 +354,6 @@ public void SkippedDataRow(int x, string y) {{ }}
[InlineData(/* lang=c#-test */ "System.Collections.Generic.IEnumerable<object>")]
[InlineData(/* lang=c#-test */ "object[]")]
[InlineData(/* lang=c#-test */ "object")]
[InlineData(/* lang=c#-test */ "System.Tuple<string, int>")]
[InlineData(/* lang=c#-test */ "System.Tuple<string, int>[]")]
public async Task InvalidMemberType_Triggers(string memberType)
{
var source = string.Format(/* lang=c#-test */ """
Expand All @@ -367,11 +365,39 @@ public void TestMethod() {{ }}
}}
""", memberType);
var expectedV2 = Verify.Diagnostic("xUnit1019").WithLocation(0).WithArguments("'System.Collections.Generic.IEnumerable<object[]>'", memberType);
var expectedV3 = Verify.Diagnostic("xUnit1019").WithLocation(0).WithArguments("'System.Collections.Generic.IEnumerable<object[]>', 'System.Collections.Generic.IAsyncEnumerable<object[]>', 'System.Collections.Generic.IEnumerable<Xunit.ITheoryDataRow>', or 'System.Collections.Generic.IAsyncEnumerable<Xunit.ITheoryDataRow>'", memberType);
var expectedV3 = Verify.Diagnostic("xUnit1019").WithLocation(0).WithArguments("'System.Collections.Generic.IEnumerable<object[]>', 'System.Collections.Generic.IAsyncEnumerable<object[]>', 'System.Collections.Generic.IEnumerable<Xunit.ITheoryDataRow>', 'System.Collections.Generic.IAsyncEnumerable<Xunit.ITheoryDataRow>', 'System.Collections.Generic.IEnumerable<System.Runtime.CompilerServices.ITuple>', or 'System.Collections.Generic.IAsyncEnumerable<System.Runtime.CompilerServices.ITuple>'", memberType);

await Verify.VerifyAnalyzerV2(source, expectedV2);
await Verify.VerifyAnalyzerV3(source, expectedV3);
}

[Fact]
public async Task Tuple_TriggersInV2_DoesNotTriggerInV3()
{
var source = /* lang=c#-test */ """
#pragma warning disable xUnit1042
using System;
using System.Collections.Generic;
using Xunit;
public class TestClass {
public static IEnumerable<(string, int)> UntypedTupleSource;
public static IEnumerable<Tuple<string, int>> TypedTupleSource;
[{|#0:MemberData(nameof(UntypedTupleSource))|}]
[{|#1:MemberData(nameof(TypedTupleSource))|}]
public void TestMethod(string _1, int _2) { }
}
""";
DiagnosticResult[] expectedV2 = [
Verify.Diagnostic("xUnit1019").WithLocation(0).WithArguments("'System.Collections.Generic.IEnumerable<object[]>'", "System.Collections.Generic.IEnumerable<(string, int)>"),
Verify.Diagnostic("xUnit1019").WithLocation(1).WithArguments("'System.Collections.Generic.IEnumerable<object[]>'", "System.Collections.Generic.IEnumerable<System.Tuple<string, int>>"),
];

await Verify.VerifyAnalyzerV2(LanguageVersion.CSharp7, source, expectedV2);
await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7, source);
}
}

public class X1020_MemberDataPropertyMustHaveGetter
Expand Down
21 changes: 21 additions & 0 deletions src/xunit.analyzers/Utility/TypeSymbolFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ public static class TypeSymbolFactory
public static INamedTypeSymbol? IAsyncEnumerableOfT(Compilation compilation) =>
Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Collections.Generic.IAsyncEnumerable`1");

public static INamedTypeSymbol? IAsyncEnumerableOfTuple(Compilation compilation)
{
var iTuple = ITuple(compilation);
if (iTuple is null)
return null;

return IAsyncEnumerableOfT(compilation)?.Construct(iTuple);
}

public static INamedTypeSymbol? IAsyncLifetime(Compilation compilation) =>
Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IAsyncLifetime);

Expand Down Expand Up @@ -142,6 +151,15 @@ public static INamedTypeSymbol IEnumerableOfObjectArray(Compilation compilation)
public static INamedTypeSymbol IEnumerableOfT(Compilation compilation) =>
Guard.ArgumentNotNull(compilation).GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T);

public static INamedTypeSymbol? IEnumerableOfTuple(Compilation compilation)
{
var iTuple = ITuple(compilation);
if (iTuple is null)
return null;

return IEnumerableOfT(compilation).Construct(iTuple);
}

public static INamedTypeSymbol? IMessageSink_V2(Compilation compilation) =>
Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.IMessageSink_V2);

Expand Down Expand Up @@ -253,6 +271,9 @@ public static INamedTypeSymbol IReadOnlyCollectionOfT(Compilation compilation) =
public static INamedTypeSymbol? ITheoryDataRow_V3(Compilation compilation) =>
Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITheoryDataRow_V3);

public static INamedTypeSymbol? ITuple(Compilation compilation) =>
Guard.ArgumentNotNull(compilation).GetTypeByMetadataName("System.Runtime.CompilerServices.ITuple");

public static INamedTypeSymbol? ITypeInfo_V2(Compilation compilation) =>
Guard.ArgumentNotNull(compilation).GetTypeByMetadataName(Constants.Types.Xunit.ITypeInfo_V2);

Expand Down
28 changes: 23 additions & 5 deletions src/xunit.analyzers/X1000/MemberDataShouldReferenceValidMember.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,10 @@ memberReturnType is INamedTypeSymbol namedMemberReturnType &&
// Make sure the member returns a compatible type
var iEnumerableOfTheoryDataRowType = TypeSymbolFactory.IEnumerableOfITheoryDataRow(compilation);
var iAsyncEnumerableOfTheoryDataRowType = TypeSymbolFactory.IAsyncEnumerableOfITheoryDataRow(compilation);
var iEnumerableOfTupleType = TypeSymbolFactory.IEnumerableOfTuple(compilation);
var iAsyncEnumerableOfTupleType = TypeSymbolFactory.IAsyncEnumerableOfTuple(compilation);
var IsValidMemberReturnType =
VerifyDataSourceReturnType(context, compilation, xunitContext, memberReturnType, memberProperties, attributeSyntax, iEnumerableOfTheoryDataRowType, iAsyncEnumerableOfTheoryDataRowType);
VerifyDataSourceReturnType(context, compilation, xunitContext, memberReturnType, memberProperties, attributeSyntax, iEnumerableOfTheoryDataRowType, iAsyncEnumerableOfTheoryDataRowType, iEnumerableOfTupleType, iAsyncEnumerableOfTupleType);

// Make sure public properties have a public getter
if (memberSymbol.Kind == SymbolKind.Property && memberSymbol.DeclaredAccessibility == Accessibility.Public)
Expand Down Expand Up @@ -381,6 +383,8 @@ static void ReportIncorrectReturnType(
INamedTypeSymbol? iAsyncEnumerableOfObjectArrayType,
INamedTypeSymbol? iEnumerableOfTheoryDataRowType,
INamedTypeSymbol? iAsyncEnumerableOfTheoryDataRowType,
INamedTypeSymbol? iEnumerableOfTupleType,
INamedTypeSymbol? iAsyncEnumerableOfTupleType,
AttributeSyntax attribute,
ImmutableDictionary<string, string?> memberProperties,
ITypeSymbol memberType)
Expand All @@ -390,14 +394,18 @@ static void ReportIncorrectReturnType(
// Only want the extra types when we know ITheoryDataRow is valid
if (iAsyncEnumerableOfObjectArrayType is not null &&
iEnumerableOfTheoryDataRowType is not null &&
iAsyncEnumerableOfTheoryDataRowType is not null)
iAsyncEnumerableOfTheoryDataRowType is not null &&
iEnumerableOfTupleType is not null &&
iAsyncEnumerableOfTupleType is not null)
#pragma warning disable RS1035 // The suggested fix is not available in this context
validSymbols += string.Format(
CultureInfo.CurrentCulture,
", '{0}', '{1}', or '{2}'",
", '{0}', '{1}', '{2}', '{3}', or '{4}'",
SymbolDisplay.ToDisplayString(iAsyncEnumerableOfObjectArrayType),
SymbolDisplay.ToDisplayString(iEnumerableOfTheoryDataRowType),
SymbolDisplay.ToDisplayString(iAsyncEnumerableOfTheoryDataRowType)
SymbolDisplay.ToDisplayString(iAsyncEnumerableOfTheoryDataRowType),
SymbolDisplay.ToDisplayString(iEnumerableOfTupleType),
SymbolDisplay.ToDisplayString(iAsyncEnumerableOfTupleType)
);
#pragma warning restore RS1035

Expand Down Expand Up @@ -721,7 +729,9 @@ static bool VerifyDataSourceReturnType(
ImmutableDictionary<string, string?> memberProperties,
AttributeSyntax attributeSyntax,
INamedTypeSymbol? iEnumerableOfTheoryDataRowType,
INamedTypeSymbol? iAsyncEnumerableOfTheoryDataRowType)
INamedTypeSymbol? iAsyncEnumerableOfTheoryDataRowType,
INamedTypeSymbol? iEnumerableOfTupleType,
INamedTypeSymbol? iAsyncEnumerableOfTupleType)
{
var v3 = xunitContext.HasV3References;
var iEnumerableOfObjectArrayType = TypeSymbolFactory.IEnumerableOfObjectArray(compilation);
Expand All @@ -738,13 +748,21 @@ static bool VerifyDataSourceReturnType(
if (!valid && v3 && iAsyncEnumerableOfTheoryDataRowType is not null)
valid = iAsyncEnumerableOfTheoryDataRowType.IsAssignableFrom(memberType);

if (!valid && v3 && iEnumerableOfTupleType is not null)
valid = iEnumerableOfTupleType.IsAssignableFrom(memberType);

if (!valid && v3 && iAsyncEnumerableOfTupleType is not null)
valid = iAsyncEnumerableOfTupleType.IsAssignableFrom(memberType);

if (!valid)
ReportIncorrectReturnType(
context,
iEnumerableOfObjectArrayType,
iAsyncEnumerableOfObjectArrayType,
iEnumerableOfTheoryDataRowType,
iAsyncEnumerableOfTheoryDataRowType,
iEnumerableOfTupleType,
iAsyncEnumerableOfTupleType,
attributeSyntax,
memberProperties,
memberType
Expand Down

0 comments on commit 4d90486

Please sign in to comment.