Summary
We have MSTEST0032 ReviewAlwaysTrueAssertConditionAnalyzer which already catches always-true assertions, and MSTEST0025 PreferAssertFailOverAlwaysFalseConditionsAnalyzer which catches always-false ones, but only when both operands are compile-time constants (e.g. Assert.AreEqual(1, 1)). Neither catches the very common bug pattern where the same expression is passed for both expected and actual:
Assert.AreEqual(x, x); // always passes -- bug
Assert.AreSame(x, x); // always passes -- bug
Assert.AreNotEqual(x, x); // always fails -- bug
Assert.AreNotSame(x, x); // always fails -- bug
This is a real production-quality issue: a case was just spotted in dotnet/sdk, and other Roslyn analyzers (CA1508, RCS1156, S2701) already flag it.
Proposal
Extend the two existing analyzers so that the structural-equivalence check is also applied to operand pairs:
- MSTEST0032 (always true) -- report
AreEqual(a, b) / AreSame(a, b) when a and b reference the same local, parameter, field, or property (with the same receiver chain).
- MSTEST0025 (always false) -- report
AreNotEqual(a, b) / AreNotSame(a, b) under the same condition.
Implementation hint: a small helper on IOperation that walks down conversions/parentheses and then recursively compares the two operations by reference-symbol equality (matching ILocalReferenceOperation, IParameterReferenceOperation, IFieldReferenceOperation, IPropertyReferenceOperation with their instances, and IInstanceReferenceOperation).
Out of scope
- Side-effecting expressions (
GetX() == GetX()) -- skip, since results could differ.
- The existing diagnostic messages ("always true" / "always false") still fit; no new diagnostic IDs needed.
Summary
We have MSTEST0032
ReviewAlwaysTrueAssertConditionAnalyzerwhich already catches always-true assertions, and MSTEST0025PreferAssertFailOverAlwaysFalseConditionsAnalyzerwhich catches always-false ones, but only when both operands are compile-time constants (e.g.Assert.AreEqual(1, 1)). Neither catches the very common bug pattern where the same expression is passed for bothexpectedandactual:This is a real production-quality issue: a case was just spotted in
dotnet/sdk, and other Roslyn analyzers (CA1508, RCS1156, S2701) already flag it.Proposal
Extend the two existing analyzers so that the structural-equivalence check is also applied to operand pairs:
AreEqual(a, b)/AreSame(a, b)whenaandbreference the same local, parameter, field, or property (with the same receiver chain).AreNotEqual(a, b)/AreNotSame(a, b)under the same condition.Implementation hint: a small helper on
IOperationthat walks down conversions/parentheses and then recursively compares the two operations by reference-symbol equality (matchingILocalReferenceOperation,IParameterReferenceOperation,IFieldReferenceOperation,IPropertyReferenceOperationwith their instances, andIInstanceReferenceOperation).Out of scope
GetX() == GetX()) -- skip, since results could differ.