Skip to content

Commit 3d9ddb6

Browse files
authoredMar 20, 2024··
Fix MSTEST0014 problems with arrays (#2607)
1 parent 510787c commit 3d9ddb6

File tree

3 files changed

+90
-15
lines changed

3 files changed

+90
-15
lines changed
 

‎src/Analyzers/MSTest.Analyzers/DataRowShouldBeValidAnalyzer.cs

+33-15
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ private static void AnalyzeSymbol(
111111

112112
private static void AnalyzeAttribute(SymbolAnalysisContext context, AttributeData attribute, IMethodSymbol methodSymbol)
113113
{
114-
if (attribute.ApplicationSyntaxReference?.GetSyntax() is not { } syntax)
114+
if (attribute.ApplicationSyntaxReference?.GetSyntax() is not { } dataRowSyntax)
115115
{
116116
return;
117117
}
@@ -126,21 +126,19 @@ private static void AnalyzeAttribute(SymbolAnalysisContext context, AttributeDat
126126
// constructor argument.
127127
if (methodSymbol.Parameters.Length == 0)
128128
{
129-
context.ReportDiagnostic(syntax.CreateDiagnostic(
129+
context.ReportDiagnostic(dataRowSyntax.CreateDiagnostic(
130130
ArgumentCountMismatchRule,
131131
attribute.ConstructorArguments.Length,
132132
methodSymbol.Parameters.Length));
133133
return;
134134
}
135135

136136
// Possible count mismatch depending on whether last method parameter is an array or not.
137-
IParameterSymbol lastMethodParameter = methodSymbol.Parameters.Last();
138-
bool lastMethodParameterIsArray = lastMethodParameter.Type.Kind == SymbolKind.ArrayType;
139137
if (attribute.ConstructorArguments.Length == 0)
140138
{
141-
if (!lastMethodParameterIsArray)
139+
if (methodSymbol.Parameters[^1].Type.Kind != SymbolKind.ArrayType)
142140
{
143-
context.ReportDiagnostic(syntax.CreateDiagnostic(
141+
context.ReportDiagnostic(dataRowSyntax.CreateDiagnostic(
144142
ArgumentCountMismatchRule,
145143
attribute.ConstructorArguments.Length,
146144
methodSymbol.Parameters.Length));
@@ -160,7 +158,7 @@ private static void AnalyzeAttribute(SymbolAnalysisContext context, AttributeDat
160158

161159
if (IsArgumentCountMismatch(constructorArguments.Length, methodSymbol.Parameters))
162160
{
163-
context.ReportDiagnostic(syntax.CreateDiagnostic(
161+
context.ReportDiagnostic(dataRowSyntax.CreateDiagnostic(
164162
ArgumentCountMismatchRule,
165163
constructorArguments.Length,
166164
methodSymbol.Parameters.Length));
@@ -169,34 +167,54 @@ private static void AnalyzeAttribute(SymbolAnalysisContext context, AttributeDat
169167

170168
// Check constructor argument types match method parameter types.
171169
List<(int ConstructorArgumentIndex, int MethodParameterIndex)> typeMismatchIndices = new();
172-
for (int i = 0; i < constructorArguments.Length; ++i)
170+
for (int currentArgumentIndex = 0; currentArgumentIndex < constructorArguments.Length; currentArgumentIndex++)
173171
{
174172
// Null is considered as default for non-nullable types.
175-
if (constructorArguments[i].IsNull)
173+
if (constructorArguments[currentArgumentIndex].IsNull)
176174
{
177175
continue;
178176
}
179177

180-
ITypeSymbol? argumentType = constructorArguments[i].Type;
181-
ITypeSymbol paramType = (lastMethodParameterIsArray && i >= methodSymbol.Parameters.Length - 1)
182-
? ((IArrayTypeSymbol)lastMethodParameter.Type).ElementType
183-
: methodSymbol.Parameters[i].Type;
178+
ITypeSymbol? argumentType = constructorArguments[currentArgumentIndex].Type;
179+
ITypeSymbol paramType = GetParameterType(methodSymbol, currentArgumentIndex, constructorArguments.Length);
184180

185181
if (argumentType is not null && !argumentType.IsAssignableTo(paramType, context.Compilation))
186182
{
187-
typeMismatchIndices.Add((i, Math.Min(i, methodSymbol.Parameters.Length - 1)));
183+
typeMismatchIndices.Add((currentArgumentIndex, Math.Min(currentArgumentIndex, methodSymbol.Parameters.Length - 1)));
188184
}
189185
}
190186

191187
// Report diagnostics if there's any type mismatch.
192188
if (typeMismatchIndices.Count > 0)
193189
{
194-
context.ReportDiagnostic(syntax.CreateDiagnostic(
190+
context.ReportDiagnostic(dataRowSyntax.CreateDiagnostic(
195191
ArgumentTypeMismatchRule,
196192
string.Join(", ", typeMismatchIndices)));
197193
}
198194
}
199195

196+
private static ITypeSymbol GetParameterType(IMethodSymbol methodSymbol, int currentArgumentIndex, int argumentsCount)
197+
{
198+
if (currentArgumentIndex >= methodSymbol.Parameters.Length - 1)
199+
{
200+
IParameterSymbol lastParameter = methodSymbol.Parameters[^1];
201+
202+
// When last parameter is params, we want to check that the extra arguments match the type of the array
203+
if (lastParameter.IsParams)
204+
{
205+
return ((IArrayTypeSymbol)lastParameter.Type).ElementType;
206+
}
207+
208+
// When only parameter is array and we have more than one argument, we want to check the array type
209+
if (argumentsCount > 1 && methodSymbol.Parameters.Length == 1 && lastParameter.Type.Kind == SymbolKind.ArrayType)
210+
{
211+
return ((IArrayTypeSymbol)lastParameter.Type).ElementType;
212+
}
213+
}
214+
215+
return methodSymbol.Parameters[currentArgumentIndex].Type;
216+
}
217+
200218
private static bool IsArgumentCountMismatch(int constructorArgumentsLength, ImmutableArray<IParameterSymbol> methodParameters)
201219
{
202220
int optionalParametersCount = methodParameters.Count(x => x.HasExplicitDefaultValue);

‎test/UnitTests/MSTest.Analyzers.UnitTests/DataRowShouldBeValidAnalyzerTests.cs

+55
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,29 @@ public void TestMethod1(object[] o)
131131
await VerifyCS.VerifyAnalyzerAsync(code);
132132
}
133133

134+
public async Task WhenDataRowPassesOneItemAndParameterExpectsArray_Diagnostic()
135+
{
136+
var code = """
137+
using Microsoft.VisualStudio.TestTools.UnitTesting;
138+
139+
[TestClass]
140+
public class MyTestClass
141+
{
142+
[{|#0:DataRow(1)|}]
143+
[TestMethod]
144+
public void TestMethod1(object[] o)
145+
{
146+
}
147+
}
148+
""";
149+
150+
await VerifyCS.VerifyAnalyzerAsync(
151+
code,
152+
VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.ArgumentTypeMismatchRule)
153+
.WithLocation(0)
154+
.WithArguments((0, 0)));
155+
}
156+
134157
public async Task WhenDataRowHasThreeArgumentsAndMethodHasAnIntegerAndAnArrayArgument_Diagnostic()
135158
{
136159
var code = """
@@ -487,4 +510,36 @@ await VerifyCS.VerifyAnalyzerAsync(
487510
VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.ArgumentCountMismatchRule).WithLocation(1).WithArguments(1, 3),
488511
VerifyCS.Diagnostic(DataRowShouldBeValidAnalyzer.ArgumentCountMismatchRule).WithLocation(2).WithArguments(1, 5));
489512
}
513+
514+
public async Task Testfx_2606_NullArgumentForArray()
515+
{
516+
string code = """
517+
using Microsoft.VisualStudio.TestTools.UnitTesting;
518+
519+
[TestClass]
520+
public class MyTestClass
521+
{
522+
[DataTestMethod]
523+
[DataRow(
524+
"123",
525+
new string[] { "something" },
526+
null)]
527+
[DataRow(
528+
"123",
529+
null,
530+
new string[] { "something" })]
531+
public void TestSomething(
532+
string x,
533+
string[] y,
534+
string[] z)
535+
{
536+
Assert.AreEqual("123", x);
537+
Assert.IsNotNull(y);
538+
Assert.IsNull(z);
539+
}
540+
}
541+
""";
542+
543+
await VerifyCS.VerifyAnalyzerAsync(code);
544+
}
490545
}

‎test/UnitTests/MSTest.Analyzers.UnitTests/testsbaseline.txt

+2
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAna
6464
MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeReturnTypeIsNotValid_Diagnostic()
6565
MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.ClassInitializeShouldBeValidAnalyzerTests.WhenClassInitializeReturnTypeIsValid_NoDiagnostic()
6666
MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.DefaultArguments()
67+
MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.Testfx_2606_NullArgumentForArray()
6768
MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowHasArgumentMismatchWithTestMethod_Diagnostic()
6869
MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowHasArgumentMismatchWithTestMethod2_Diagnostic()
6970
MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowHasArgumentMismatchWithTestMethod3_Diagnostic()
@@ -85,6 +86,7 @@ MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTes
8586
MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithThreeArgumentsAndMethodHasArrayArgument_NoDiagnostic()
8687
MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithThreeArgumentsAndMethodHasParamsArgument_NoDiagnostic()
8788
MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsNotSetOnATestMethod_Diagnostic()
89+
MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowPassesOneItemAndParameterExpectsArray_Diagnostic()
8890
MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DoNotNegateBooleanAssertionAnalyzerTests.WhenAssertionIsNegated_Diagnostic()
8991
MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DoNotNegateBooleanAssertionAnalyzerTests.WhenAssertionIsNotNegated_NoDiagnostic()
9092
MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DoNotStoreStaticTestContextAnalyzerTests.WhenAssemblyInitializeOrClassInitialize_Diagnostic()

0 commit comments

Comments
 (0)
Please sign in to comment.