Skip to content

Conversation

Alex-Sob
Copy link

@Alex-Sob Alex-Sob commented Aug 20, 2025

This PR fixes #50398

Changes

  • Fixed PreferLengthCountIsEmptyOverAnyAnalyzer so that
    CA1860 is reported not only if the type itself has IsEmpty/Count/Length property, but also if that type is derived from a type with one of those properties, e.g. a custom collection type. I also tried to simplify code and remove some code duplication.
  • Added unit tests.

Performance improvements

I'm also suggesting some performance improvements that I included in this PR:

  • Currently the analyzer checks if an invocation is inside an expression tree before checking that it's actually Enumerable.Any call. I moved that check, so that it's not performed for each and every invocation expression, but only for Any calls.

  • Currently the analyzer checks if the type of an expression in Any call is IEnumerable<T> or IEnumerable. This check seems to be redundant because the analyzer makes a symbol check to make sure that it's exactly Enumerable.Any call, so the type of an expression should automatically be IEnumerable<T> and the check can be removed.

    Moreover, currently it checks for IEnumerable<T> incorrectly because it compares interface types with unbound generic type and the result is always false. So effectively it only checks if the type is non-generic IEnumerable.

  • Currently the analyzer makes ITypeSymbol.GetMembers and Enumerable.OfType calls for each of the three property checks while checking if the type has IsEmpty, Count or Length property. Each call apparently allocates an ImmutableArray with all type members, and also an enumerator returned by OfType. I'm changing this to call ITypeSymbol.GetMembers(name) method which will return a singleton ImmutableArray.Empty instance if there's no such property on the type, and will allocate a single element array if the property is found.

  • Currently when reporting an error the analyzer calls ImmutableDictionary.CreateBuilder and then calls ToImmutable on builder, this allocates both builder and dictionary. As it needs to add just one element to dictionary, it can be added to an empty instance thus avoiding builder allocation.

  • I moved the check for Count property before the check for Length property. I believe it's more likely that a type has Count property because all collection types have Count property while only arrays have Length. This should save a check in most cases.

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

Successfully merging this pull request may close these issues.

CA1860 should be reported for derived types as well
2 participants