Skip to content

Add Span<T>/Memory<T> overloads to Assert.HasCount (and extend MSTEST0037) #9143

Description

Summary

Assert.HasCount<T> today only accepts IEnumerable<T>. With Span<T>/ReadOnlySpan<T>/Memory<T>/ReadOnlyMemory<T> being pervasive in modern .NET (System.Buffers, pipelines, parsers, low-allocation APIs), users repeatedly hit a gap right next to sites that MSTEST0037 happily rewrites.

Repro

In dotnet/sdk PR #54766, turning on MSTestAnalysisMode=Recommended flagged ~25 length/count assertions in ArrayBufferWriterTests.cs. All the IEnumerable/ArraySegment/array cases were auto-fixed to Assert.HasCount. But sites like:

csharp Assert.AreEqual(written.Length, transientSpan.Length);

cannot be fixed today because there's no HasCount overload that accepts a Span<T> or ReadOnlySpan<T>. The analyzer correctly skips them, but the result is an inconsistent UX: identical-looking call sites get different treatment based on whether the value is an IEnumerable or a Span.

Proposal

  1. Add overloads to Assert.Count.cs (and their interpolated-message variants):

    csharp public static void HasCount<T>(int expected, ReadOnlySpan<T> span); public static void HasCount<T>(int expected, Span<T> span); public static void HasCount<T>(int expected, ReadOnlyMemory<T> memory); public static void HasCount<T>(int expected, Memory<T> memory);

    Span/ReadOnlySpan are ref structs so they cannot satisfy a generic IEnumerable<T> constraint — they need explicit overloads. Memory<T> could technically go through .ToArray() but a direct overload avoids the allocation.

  2. Extend MSTEST0037 to flag Assert.AreEqual(x, span.Length) / Assert.AreEqual(x, memory.Length) and suggest Assert.HasCount(x, span) / Assert.HasCount(x, memory) once the overloads exist.

Naming consideration

HasCount reads slightly awkward for Span (.Length, not .Count). HasLength would fit better semantically, but I think API consistency wins — having one assertion name for "this sequence-like thing has N elements" is more discoverable than splitting by underlying property name. Happy to discuss.

Trade-offs

  • Surface area grows by 4 overloads × interpolated-string variants. Cost is small and the family precedent (Assert.AreSequenceEqual already accepts ReadOnlySpan<T>) supports it.
  • ref struct parameters mean these can't live behind a single generic, but they don't need to — explicit overloads are the standard pattern.

Related

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/analyzersMSTest.Analyzers Roslyn analyzers and code fixes.

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions