Tighten MSTEST0065 to fire on argument types, not only the generic T (#8766)#8770
Open
Evangelink wants to merge 2 commits into
Open
Tighten MSTEST0065 to fire on argument types, not only the generic T (#8766)#8770Evangelink wants to merge 2 commits into
Evangelink wants to merge 2 commits into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR tightens MSTEST0065 (AvoidAssertAreEqualOnCollectionsAnalyzer) so it can report when callers widen the generic type argument (e.g., T=object) but still pass collection-typed arguments at the call site, and adds unit tests for the newly-covered scenarios. It also updates the test-improver agentic workflow guidance with a repo-specific constraint around analyzer test coverage.
Changes:
- Update MSTEST0065 to fall back to inspecting un-converted argument static types (expected/notExpected + actual) when
Titself is not a collection. - Add new unit tests covering explicit
<object>generic arguments, object casts, mixed object/collection static types, and preserved exclusions (string + null-literal short-circuit). - Document a repository-specific constraint for the test-improver workflow (no VB.NET analyzer tests).
Show a summary per file
| File | Description |
|---|---|
src/Analyzers/MSTest.Analyzers/AvoidAssertAreEqualOnCollectionsAnalyzer.cs |
Adjusts MSTEST0065 reporting logic to consider argument static types (after stripping conversions) when T isn’t itself a collection. |
test/UnitTests/MSTest.Analyzers.UnitTests/AvoidAssertAreEqualOnCollectionsAnalyzerTests.cs |
Adds coverage for newly-detected call-site widening/casting patterns and key negative cases. |
.github/workflows/test-improver.md |
Adds repo-specific constraints for the test-improver agent workflow (notably: no VB analyzer tests). |
Copilot's findings
- Files reviewed: 3/3 changed files
- Comments generated: 2
The AvoidAssertAreEqualOnCollectionsAnalyzer previously only inspected`targetMethod.TypeArguments[0]` (the inferred `T`) and so silently missed real-world pit-of-failure patterns where the caller forces `T` to a non-collection type, e.g.`Assert.AreEqual<object>(arr1, arr2)` or `Assert.AreEqual((object)arr, (object)arr)`. The analyzer now also walks back through implicit conversions on the`expected`/`notExpected` and `actual` arguments and fires the diagnostic when the un-converted static type of either argument implements`IEnumerable<T>` (and is not `string`). The existing null-literal short-circuit and `string` exclusion are preserved. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ions aren't unwrapped Per review feedback, switch from WalkDownConversion to WalkDownBuiltInConversion in GetCollectionArgumentType. The intent is to reason about the call-site static type after only built-in conversions (boxing, reference widening, implicit numeric conversions); a user-defined conversion changes what the user actually wrote, so the type after that conversion is the comparison the user intended. Add a regression test that defines a user-defined implicit conversion from int[] to a non-collection Wrapper type and asserts MSTEST0065 stays quiet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
e84dc30 to
c6ac2db
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #8766.
Summary
Tighten MSTEST0065 (AvoidAssertAreEqualOnCollectionsAnalyzer) so it fires based on the argument / parameter type at the call site, not only on the generic type argument
T. The analyzer previously inspected onlytargetMethod.TypeArguments[0]and so silently missed real-world pit-of-failure patterns where the caller forcesTto a non-collection type.Now flagged (in addition to today's cases):
csharp int[] arr1 = [1, 2]; int[] arr2 = [1, 2]; Assert.AreEqual<object>(arr1, arr2); // T = object, but arguments are int[] Assert.AreEqual((object)arr1, (object)arr2); // explicit object castThe analyzer now walks back through implicit conversions on the
expected/notExpectedandactualarguments viaIArgumentOperation.Value.WalkDownConversion()and fires the diagnostic if either argument's un-converted static type implementsIEnumerable<T>(and is notstring).Behavior preserved
expected/notExpectedoractual.stringexclusion.Twhen it is itself a collection; otherwise it is the un-converted argument type that triggered the report.What is not covered
The acceptance criteria included
object x = ...; Assert.AreEqual(x, y)wherexis declared asobjectbut assigned a collection. Without dataflow analysis the analyzer cannot see beyond the local's compile-time typeobject, so that case is intentionally not flagged (an explicit negative test documents this). The mixed caseAssert.AreEqual(list, (object)list2)is flagged because at least one argument has a collection static type at the call site.Risk / migration
This is a tightening of a
Warning-severity analyzer and will produce new warnings on existing codebases that hit the newly-covered patterns. That is the desired effect (more pit-of-failure cases caught). Per the issue, this should ship separately from any severity escalation of MSTEST0065.Tests
Added 7 new tests covering both positive and negative cases:
Assert.AreEqual<object>(arr1, arr2)— flaggedAssert.AreNotEqual<object>(arr1, arr2)— flaggedAssert.AreEqual((object)expected, (object)actual)— flaggedAssert.AreEqual(list, objectThatIsList)— flagged (one side has collection static type)Assert.AreEqual<object>(s1, s2)withstringarguments — not flaggedAssert.AreEqual(objectX, objectY)with both sides declared asobject— not flagged (documents dataflow limitation)Assert.AreEqual(record1, record2)for value-equal record — not flaggedAssert.AreEqual<object?>(null, actual)— null-literal short-circuit preserved across the new pathAll 31
AvoidAssertAreEqualOnCollectionsAnalyzerTestspass, as do the 123 tests across the relatedUseProperAssertMethodsAnalyzer/AvoidAssertAreSameWithValueTypesAnalyzer/CollectionAssertToAssertMigrationAnalyzersuites.