From 40b30ddd70182b54c77ff09cadfdea72c704f3c4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Mar 2026 06:29:08 +0000 Subject: [PATCH 1/3] Initial plan From 04ee35265a838866fcabff4daf7e36d066f93154 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Mar 2026 06:44:27 +0000 Subject: [PATCH 2/3] Fix MSTEST0037 to not suggest HasCount for nullable int? expected arguments Agent-Logs-Url: https://github.com/microsoft/testfx/sessions/cfe39d4b-438c-4b84-b10a-cc44fed45592 Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com> --- .../UseProperAssertMethodsAnalyzer.cs | 20 ++++++++++++++ .../UseProperAssertMethodsAnalyzerTests.cs | 26 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs index be9462eac4..6ef3035cae 100644 --- a/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs @@ -1430,6 +1430,16 @@ expectedArgument.ConstantValue.Value is int expectedValue && { // We have Assert.AreEqual(expectedCount, collection.Count/Length) // We want Assert.HasCount(expectedCount, collection) + // Assert.HasCount takes int, not int?, so skip if expectedCount is a nullable value type. + if (expectedArgument.Type.IsNullableValueType()) + { + nodeToBeReplaced1 = null; + replacement1 = null; + nodeToBeReplaced2 = null; + replacement2 = null; + return CountCheckStatus.Unknown; + } + // So, only a single replacement is needed. We replace collection.Count with collection. nodeToBeReplaced1 = actualArgument.Syntax; // collection.Count replacement1 = expression; // collection @@ -1461,6 +1471,16 @@ expectedArgument.ConstantValue.Value is int expectedLinqValue && { // We have Assert.AreEqual(expectedCount, enumerable.Count()) // We want Assert.HasCount(expectedCount, enumerable) + // Assert.HasCount takes int, not int?, so skip if expectedCount is a nullable value type. + if (expectedArgument.Type.IsNullableValueType()) + { + nodeToBeReplaced1 = null; + replacement1 = null; + nodeToBeReplaced2 = null; + replacement2 = null; + return CountCheckStatus.Unknown; + } + nodeToBeReplaced1 = actualArgument.Syntax; // enumerable.Count() replacement1 = linqCollection; // enumerable nodeToBeReplaced2 = null; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs index e43eec561a..0ea574cbde 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs @@ -2086,6 +2086,32 @@ await VerifyCS.VerifyCodeFixAsync( fixedCode); } + [TestMethod] + public async Task WhenAssertAreEqualWithCollectionCountNonZeroNullableExpected() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + using System.Linq; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + int? count = 3; + var list = new List { 1, 2, 3 }; + Assert.AreEqual(count, list.Count); + Assert.AreEqual(count, list.AsEnumerable().Count()); + } + } + """; + + // Should not trigger MSTEST0037 because Assert.HasCount takes int, not int? + await VerifyCS.VerifyAnalyzerAsync(code); + } + [TestMethod] public async Task WhenAssertAreNotEqualWithCollectionCountZero() { From 781c5dbdbff0bc94571fea6461193088733e5412 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 30 Mar 2026 08:24:31 +0000 Subject: [PATCH 3/3] Fix MSTEST0037 to suppress HasCount suggestion for any non-int expected argument Agent-Logs-Url: https://github.com/microsoft/testfx/sessions/7a39bf37-3ac8-42ed-8a5a-07c0c2c5e22a Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com> --- .../UseProperAssertMethodsAnalyzer.cs | 8 ++++---- .../UseProperAssertMethodsAnalyzerTests.cs | 13 ++++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs index 6ef3035cae..d90faccc52 100644 --- a/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/UseProperAssertMethodsAnalyzer.cs @@ -1430,8 +1430,8 @@ expectedArgument.ConstantValue.Value is int expectedValue && { // We have Assert.AreEqual(expectedCount, collection.Count/Length) // We want Assert.HasCount(expectedCount, collection) - // Assert.HasCount takes int, not int?, so skip if expectedCount is a nullable value type. - if (expectedArgument.Type.IsNullableValueType()) + // Assert.HasCount takes int, so skip if expectedCount is not an int (e.g. int?, long, uint, decimal). + if (expectedArgument.Type?.SpecialType != SpecialType.System_Int32) { nodeToBeReplaced1 = null; replacement1 = null; @@ -1471,8 +1471,8 @@ expectedArgument.ConstantValue.Value is int expectedLinqValue && { // We have Assert.AreEqual(expectedCount, enumerable.Count()) // We want Assert.HasCount(expectedCount, enumerable) - // Assert.HasCount takes int, not int?, so skip if expectedCount is a nullable value type. - if (expectedArgument.Type.IsNullableValueType()) + // Assert.HasCount takes int, so skip if expectedCount is not an int (e.g. int?, long, uint, decimal). + if (expectedArgument.Type?.SpecialType != SpecialType.System_Int32) { nodeToBeReplaced1 = null; replacement1 = null; diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs index 0ea574cbde..bfafc26317 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseProperAssertMethodsAnalyzerTests.cs @@ -2087,7 +2087,7 @@ await VerifyCS.VerifyCodeFixAsync( } [TestMethod] - public async Task WhenAssertAreEqualWithCollectionCountNonZeroNullableExpected() + public async Task WhenAssertAreEqualWithCollectionCountNonZeroNonIntExpected() { string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -2100,15 +2100,18 @@ public class MyTestClass [TestMethod] public void MyTestMethod() { - int? count = 3; + int? nullableCount = 3; + long longCount = 3L; var list = new List { 1, 2, 3 }; - Assert.AreEqual(count, list.Count); - Assert.AreEqual(count, list.AsEnumerable().Count()); + Assert.AreEqual(nullableCount, list.Count); + Assert.AreEqual(nullableCount, list.AsEnumerable().Count()); + Assert.AreEqual(longCount, list.Count); + Assert.AreEqual(longCount, list.AsEnumerable().Count()); } } """; - // Should not trigger MSTEST0037 because Assert.HasCount takes int, not int? + // Should not trigger MSTEST0037 because Assert.HasCount takes int, not int? or long await VerifyCS.VerifyAnalyzerAsync(code); }