Skip to content

Commit

Permalink
[xunit2189] Final PR for adding analyzers for MemberData (TheoryData) (
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesTerwilliger authored Nov 6, 2023
1 parent f7124ad commit ebfe422
Show file tree
Hide file tree
Showing 6 changed files with 376 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,8 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
if (declaredMemberTypeSymbol is null)
return;

var memberNameArgument = attributeList?.Arguments.FirstOrDefault();
if (memberNameArgument is null)
return;

var constantValue = semanticModel.GetConstantValue(memberNameArgument.Expression, context.CancellationToken);
if (constantValue.Value is not string memberName)
var memberName = diagnostic.Properties[Constants.Properties.MemberName];
if (memberName is null)
return;

IMethodSymbol? methodSymbol = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,8 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
if (declaredMemberTypeSymbol is null)
return;

var memberNameArgument = attributeList?.Arguments.FirstOrDefault();
if (memberNameArgument is null)
return;

var constantValue = semanticModel.GetConstantValue(memberNameArgument.Expression, context.CancellationToken);
if (constantValue.Value is not string memberName)
var memberName = diagnostic.Properties[Constants.Properties.MemberName];
if (memberName is null)
return;

var memberSymbol = MemberDataShouldReferenceValidMember.FindMethodSymbol(memberName, declaredMemberTypeSymbol, paramsCount);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using Microsoft;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Testing;
using Xunit;
using Xunit.Analyzers;
using Verify = CSharpVerifier<Xunit.Analyzers.MemberDataShouldReferenceValidMember>;

public class MemberDataShouldReferenceValidMemberTests
Expand Down Expand Up @@ -700,4 +698,159 @@ public void TestMethod(int n) { }

await Verify.VerifyAnalyzer(source);
}

[Fact]
public async void DoesNotFindWarning_IfHasValidTheoryDataMember()
{
var source = @"
public class TestClass {
public static Xunit.TheoryData<int> TestData(int n) => new();
[Xunit.MemberData(nameof(TestData), new object[] { 1 })]
public void TestMethod(int n) { }
}";

await Verify.VerifyAnalyzer(LanguageVersion.CSharp10, source);
}

[Fact]
public async void DoesNotFindWarning_IfHasValidTheoryDataMemberWithOptionalParameters()
{
var source = @"
public class TestClass {
public static Xunit.TheoryData<int> TestData(int n) => new();
[Xunit.MemberData(nameof(TestData), new object[] { 1 })]
public void TestMethod(int n, int a = 0) { }
}";

await Verify.VerifyAnalyzer(LanguageVersion.CSharp10, source);
}

[Fact]
public async void DoesNotFindWarning_IfHasValidTheoryDataMemberWithNeededParams()
{
var source = @"
public class TestClass {
public static Xunit.TheoryData<int, int> TestData(int n) => new();
[Xunit.MemberData(nameof(TestData), new object[] { 1 })]
public void TestMethod(int n, params int[] a) { }
}";

await Verify.VerifyAnalyzer(LanguageVersion.CSharp10, source);
}

[Fact]
public async void DoesNotFindWarning_IfHasValidTheoryDataMemberWithExtraParams()
{
var source = @"
public class TestClass {
public static Xunit.TheoryData<int> TestData(int n) => new();
[Xunit.MemberData(nameof(TestData), new object[] { 1 })]
public void TestMethod(int n, params int[] a) { }
}";

await Verify.VerifyAnalyzer(LanguageVersion.CSharp10, source);
}

[Fact]
public async void FindWarning_IfHasValidTheoryDataMemberWithTooManyTypeParameters()
{
var source = @"
public class TestClass {
public static Xunit.TheoryData<int, string> TestData(int n) => new();
[Xunit.MemberData(nameof(TestData), new object[] { 1 })]
public void TestMethod(int n) { }
}";

DiagnosticResult[] expected =
{
Verify
.Diagnostic("xUnit1038")
.WithSpan(5, 6, 5, 60)
.WithSeverity(DiagnosticSeverity.Error)
};

await Verify.VerifyAnalyzer(LanguageVersion.CSharp10, source, expected);
}

[Fact]
public async void FindWarning_IfHasValidTheoryDataMemberWithNotEnoughTypeParameters()
{
var source = @"
public class TestClass {
public static Xunit.TheoryData<int> TestData(int n) => new();
[Xunit.MemberData(nameof(TestData), new object[] { 1 })]
public void TestMethod(int n, string f) { }
}";

DiagnosticResult[] expected =
{
Verify
.Diagnostic("xUnit1037")
.WithSpan(5, 6, 5, 60)
.WithSeverity(DiagnosticSeverity.Error)
};

await Verify.VerifyAnalyzer(LanguageVersion.CSharp10, source, expected);
}

[Theory]
[InlineData("int")]
[InlineData("System.Exception")]
public async void FindWarning_IfHasValidTheoryDataMemberWithIncompatibleTypeParameters(string type)
{
var source = @$"
public class TestClass {{
public static Xunit.TheoryData<{type}> TestData(int n) => new();
[Xunit.MemberData(nameof(TestData), new object[] {{ 1 }})]
public void TestMethod(string f) {{ }}
}}";

DiagnosticResult[] expected =
{
Verify
.Diagnostic("xUnit1039")
.WithSpan(6, 28, 6, 34)
.WithSeverity(DiagnosticSeverity.Error)
.WithArguments(type, "f")
};

await Verify.VerifyAnalyzer(LanguageVersion.CSharp10, source, expected);
}

[Fact]
public async void FindWarning_IfHasValidTheoryDataMemberWithMismatchedNullability()
{
var source = @"
#nullable enable
public class TestClass {
public static Xunit.TheoryData<int?, string?> TestData(int n) => new();
[Xunit.MemberData(nameof(TestData), new object[] { 1 })]
public void TestMethod(int n, string f) { }
}
#nullable restore";

DiagnosticResult[] expected =
{
Verify
.Diagnostic("xUnit1039")
.WithSpan(7, 28, 7, 31)
.WithSeverity(DiagnosticSeverity.Error)
.WithArguments("int?", "n"),
Verify
.Diagnostic("xUnit1040")
.WithSpan(7, 35, 7, 41)
.WithSeverity(DiagnosticSeverity.Warning)
.WithArguments("string?", "f")
};

await Verify.VerifyAnalyzer(LanguageVersion.CSharp10, source, expected);
}
}
54 changes: 51 additions & 3 deletions src/xunit.analyzers/Utility/Descriptors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -368,11 +368,59 @@ static DiagnosticDescriptor Rule(
"There is no matching method parameter for value: {0}. Remove unused value(s), or add more parameter(s)."
);

// Placeholder for rule X1037
public static DiagnosticDescriptor X1037_MemberDataTheoryDataTypeArgumentsMustMatchTestMethodParameters_TooFewTypeParameters { get; } =
Rule(
"xUnit1037",
"There are fewer TheoryData type arguments than required by the parameters of the test method",
Usage,
Error,
"There are fewer TheoryData type arguments than required by the parameters of the test method. Add more type parameters to match the method signature, or remove parameters from the test method."
);

public static DiagnosticDescriptor X1038_MemberDataTheoryDataTypeArgumentsMustMatchTestMethodParameters_ExtraTypeParameters { get; } =
Rule(
"xUnit1038",
"There are more TheoryData type arguments than allowed by the parameters of the test method",
Usage,
Error,
"There are more TheoryData type arguments than allowed by the parameters of the test method. Remove unused type arguments, or add more parameters."
);

public static DiagnosticDescriptor X1039_MemberDataTheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleTypes { get; } =
Rule(
"xUnit1039",
"The type argument to TheoryData is not compatible with the type of the corresponding test method parameter",
Usage,
Error,
"The type argument {0} to TheoryData is not compatible with the type of the corresponding test method parameter {1}."
);

public static DiagnosticDescriptor X1040_MemberDataTheoryDataTypeArgumentsMustMatchTestMethodParameters_IncompatibleNullability { get; } =
Rule(
"xUnit1040",
"The type argument to TheoryData is nullable, while the type of the corresponding test method parameter is not",
Usage,
Warning,
"The type argument {0} to TheoryData is nullable, while the type of the corresponding test method parameter {1} is not. Make the TheoryData type non-nullable, or make the test method parameter nullable."
);

// Placeholder for rule X1041

// Placeholder for rule X1042

// Placeholder for rule X1043

// Placeholder for rule X1044

// Placeholder for rule X1045

// Placeholder for rule X1046

// Placeholder for rule X1047

// Placeholder for rule X1038
// Placeholder for rule X1048

// Placeholder for rule X1039
// Placeholder for rule X1049

public static DiagnosticDescriptor X2000_AssertEqualLiteralValueShouldBeFirst { get; } =
Rule(
Expand Down
4 changes: 4 additions & 0 deletions src/xunit.analyzers/Utility/TypeSymbolFactory.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Globalization;
using Microsoft.CodeAnalysis;

namespace Xunit.Analyzers;
Expand Down Expand Up @@ -128,6 +129,9 @@ public static INamedTypeSymbol IReadOnlyCollectionOfT(Compilation compilation) =
public static INamedTypeSymbol? ITheoryDataRow(Compilation compilation) =>
compilation.GetTypeByMetadataName("Xunit.ITheoryDataRow");

public static INamedTypeSymbol? TheoryDataN(Compilation compilation, int n) =>
compilation.GetTypeByMetadataName("Xunit.TheoryData`" + n.ToString(CultureInfo.InvariantCulture));

public static INamedTypeSymbol? ITypeInfo_V2(Compilation compilation) =>
compilation.GetTypeByMetadataName("Xunit.Abstractions.ITypeInfo");

Expand Down
Loading

0 comments on commit ebfe422

Please sign in to comment.