Skip to content

Extend MSTEST0037 to cover array.Length, enumerable.Count(), Any(), and Contains with comparer#7540

Draft
Copilot wants to merge 5 commits intomainfrom
copilot/extend-mstest0037-analyzer
Draft

Extend MSTEST0037 to cover array.Length, enumerable.Count(), Any(), and Contains with comparer#7540
Copilot wants to merge 5 commits intomainfrom
copilot/extend-mstest0037-analyzer

Conversation

Copy link
Contributor

Copilot AI commented Mar 12, 2026

MSTEST0037 was missing several common patterns equivalent to the already-handled collection.Count and collection.Contains() cases. This extends the analyzer and code fixer to cover those gaps.

New patterns detected and fixed

enumerable.Any() (no predicate)

  • Assert.IsTrue(e.Any())Assert.IsNotEmpty(e)
  • Assert.IsFalse(e.Any())Assert.IsEmpty(e)

enumerable.Count() in boolean expressions

  • Assert.IsTrue/IsFalse(e.Count() > 0 / != 0 / == 0)IsNotEmpty / IsEmpty

enumerable.Count() and array.Length in AreEqual/AreNotEqual

  • Assert.AreEqual(0, e.Count())Assert.IsEmpty(e)
  • Assert.AreEqual(n, e.Count())Assert.HasCount(n, e)
  • Assert.AreNotEqual(0, e.Count()/array.Length/collection.Count)Assert.IsNotEmpty(e) (was previously not flagged)

Enumerable.Contains with comparer

  • Assert.IsTrue(e.Contains(item, comparer))Assert.Contains(item, e, comparer)
  • Assert.IsFalse(e.Contains(item, comparer))Assert.DoesNotContain(item, e, comparer)

Implementation notes

  • Added TryGetLinqCountNoPredicate / TryGetLinqAnyNoPredicate helpers that match zero-predicate LINQ extension calls via enumerableTypeSymbol (consistent with existing LINQ detection).
  • Extended both RecognizeCountCheck overloads to accept enumerableTypeSymbol and handle IInvocationOperation in addition to IPropertyReferenceOperation.
  • Added CollectionCheckStatus.ContainsWithComparer and a new CodeFixModeAddTwoArguments code-fix mode that rewrites the condition argument into three new arguments (item, collection, comparer).
  • AreNotEqual(0, count) now triggers IsNotEmpty; the previously no-op test WhenAssertAreNotEqualWithCollectionCountZero is updated accordingly.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • x3yvsblobprodcus370.vsblob.vsassets.io
    • Triggering command: /home/REDACTED/work/testfx/testfx/artifacts/bin/MSTest.Analyzers.UnitTests/Release/net8.0/MSTest.Analyzers.UnitTests /home/REDACTED/work/testfx/testfx/artifacts/bin/MSTest.Analyzers.UnitTests/Release/net8.0/MSTest.Analyzers.UnitTests --diagnostic --diagnostic-output-directory /home/REDACTED/work/testfx/testfx/artifacts/log/Release --diagnostic-file-prefix MSTest.Analyzers.UnitTests_net8.0_Release_x64 --diagnostic-verbosity trace --crashdump --hangdump --hangdump-timeout 15m --report-azdo --coverage --coverage-settings /home/REDACTED/work/testfx/testfx/test/coverage.config --coverage-output MSTest.Analyzers.UnitTests_net8.0_Release_x64.coverage --filter UseProperAssertMethodsAnalyzerTests (dns block)

If you need me to access, download, or install something from one of these locations, you can either:


💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.

Copilot AI and others added 2 commits March 12, 2026 21:28
Copilot AI changed the title [WIP] Add support for array.Length and IEnumerable methods in MSTEST0037 analyzer Extend MSTEST0037 to cover array.Length, enumerable.Count(), Any(), and Contains with comparer Mar 12, 2026
Copilot AI requested a review from Evangelink March 12, 2026 21:32
Copy link
Member

@Youssef1313 Youssef1313 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This analyzer is really getting unnecessarily too complicated and handling too much stuff 😕

@Evangelink
Copy link
Member

This analyzer is really getting unnecessarily too complicated and handling too much stuff 😕

We can split to have 1 rule per target refactoring but I am not so sure this is helping much (user pov or even for our maintenance). Do you have some other ideas?

@Youssef1313
Copy link
Member

Do you have some other ideas?

Nothing immediately in mind. I'll try to look deeper in the current implementation later. Fine for me for now to make the fix here.

@Evangelink
Copy link
Member

@copilot there are some issues:

azure-pipelines
/ microsoft.testfx (Build Linux Debug)
test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs#L78

test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs(78,1): error : [WhenAssertIsTrueWithLinqContainsAndComparer] [net8.0] Test method MSTest.Analyzers.Test.UseProperAssertMethodsAnalyzerTests.WhenAssertIsTrueWithLinqContainsAndComparer threw exception:
System.InvalidOperationException: Context: Diagnostics of test state
Expected diagnostic to start on line "13" was actually on line "12"

Expected diagnostic:
// /0/Test0.cs(13,9,13,53): info MSTEST0037: Use 'Assert.Contains' instead of 'Assert.IsTrue'
VerifyCS.Diagnostic().WithSpan(13, 9, 13, 53).WithArguments("Contains", "IsTrue"),

Actual diagnostic:
// /0/Test0.cs(12,24): error CS0103: The name 'StringComparer' does not exist in the current context
DiagnosticResult.CompilerError("CS0103").WithSpan(12, 24, 12, 38).WithArguments("StringComparer"),

Copy link
Contributor Author

Copilot AI commented Mar 13, 2026

@copilot there are some issues:

azure-pipelines
...

Fixed in 0d0776f — the test code snippets for WhenAssertIsTrueWithLinqContainsAndComparer and WhenAssertIsFalseWithLinqContainsAndComparer were missing using System;, which is needed for StringComparer. Added the missing import to both test code and fixed code strings.

@Evangelink
Copy link
Member

@copilot please ensure everything builds and all tests are passing. THis test is failing:

test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs#L78

test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs(78,1): error : [WhenAssertIsFalseWithLinqContainsAndComparer] [net8.0] Test method MSTest.Analyzers.Test.UseProperAssertMethodsAnalyzerTests.WhenAssertIsFalseWithLinqContainsAndComparer threw exception:
System.InvalidOperationException: Context: Iterative code fix application
content of '/0/Test0.cs' did not match. Diff shown with expected as baseline:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Linq;

[TestClass]
public class MyTestClass
{
[TestMethod]
public void MyTestMethod()
{
IEnumerable items = new List { "a", "b", "c" };
var comparer = StringComparer.OrdinalIgnoreCase;

  •    Assert.DoesNotContain("X", items, comparer);
    
  •    Assert.IsFalse(items.Contains("X", comparer));
    
    }
    }

Copy link
Contributor Author

Copilot AI commented Mar 14, 2026

@copilot please ensure everything builds and all tests are passing. THis test is failing:

test/UnitTests/MSTest.Analyzers.UnitTests/Verifiers/CSharpCodeFixVerifier`2.cs#L78
...

Fixed in 49076b6. The root cause was that root.FindNode() returns ArgumentSyntax (outermost for tied spans) when locating the sub-expressions ("X" and comparer) within the inner items.Contains("X", comparer) call. The fixer was only checking for ExpressionSyntax, so it bailed out without applying the fix. Added a TryGetExpressionFromNode helper that extracts the expression from either ArgumentSyntax or ExpressionSyntax, consistent with how the existing FixAssertMethodForAddArgumentModeAsync handles this pattern.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • x3yvsblobprodcus370.vsblob.vsassets.io
    • Triggering command: /home/REDACTED/work/testfx/testfx/artifacts/bin/MSTest.Analyzers.UnitTests/Release/net8.0/MSTest.Analyzers.UnitTests /home/REDACTED/work/testfx/testfx/artifacts/bin/MSTest.Analyzers.UnitTests/Release/net8.0/MSTest.Analyzers.UnitTests --diagnostic --diagnostic-output-directory /home/REDACTED/work/testfx/testfx/artifacts/log/Release --diagnostic-file-prefix MSTest.Analyzers.UnitTests_net8.0_Release_x64 --diagnostic-verbosity trace --crashdump --hangdump --hangdump-timeout 15m --report-azdo --coverage --coverage-settings /home/REDACTED/work/testfx/testfx/test/coverage.config --coverage-output MSTest.Analyzers.UnitTests_net8.0_Release_x64.coverage --filter FullyQualifiedName~WhenAssertIsFalseWithLinqContainsAndComparer (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MSTEST0037 analyzer should also find array.Length and IEnumerable extension method usages

3 participants