Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Count > 0 normalized to Any should also work on ICollection #35381

Merged
merged 2 commits into from
Dec 24, 2024

Conversation

ChrisJollyAU
Copy link
Contributor

When normalizing .Count > 0 or .Count != 0 to .Any it wasn't picking up the ICollection interface if the current declaring type was already an ICollection

Fixes #35365

@ChrisJollyAU ChrisJollyAU requested a review from a team as a code owner December 23, 2024 14:57
@@ -70,7 +70,7 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
},
Right: ConstantExpression { Value: 0 }
}
when (member.DeclaringType.GetGenericTypeDefinition().GetInterfaces().Any(
when (member.DeclaringType.GetGenericTypeDefinition() == typeof(ICollection<>) || member.DeclaringType.GetGenericTypeDefinition().GetInterfaces().Any(
Copy link
Member

@roji roji Dec 24, 2024

Choose a reason for hiding this comment

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

Can invoke GetGenericTypeDefinition() only once:

            when member.DeclaringType.GetGenericTypeDefinition() is Type genericTypeDefinition
            && (genericTypeDefinition == typeof(ICollection<>)
                || genericTypeDefinition.GetInterfaces()
                    .Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>)))

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed we can. Just fixed that now

Copy link
Member

@roji roji left a comment

Choose a reason for hiding this comment

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

Looks great, thanks @ChrisJollyAU. See suggestion below, once that's fixed and the CI works again I'll merge.

@roji roji self-assigned this Dec 24, 2024
@ChrisJollyAU
Copy link
Contributor Author

@Roj Just thinking on this, I might have a look around at some stage to see if there is any other GetInterfaces that might encounter a similar style problem

@roji roji enabled auto-merge (squash) December 24, 2024 10:14
@roji
Copy link
Member

roji commented Dec 24, 2024

@ChrisJollyAU good idea, I wouldn't be surprised if we made this mistake in other places...

@ChrisJollyAU
Copy link
Contributor Author

Just seen some others do it like this memberExpression.Expression.Type.GetInterfaces().Append(memberExpression.Expression.Type)

Shall I fix this one to be like that?

@roji roji disabled auto-merge December 24, 2024 11:10
@roji
Copy link
Member

roji commented Dec 24, 2024

@ChrisJollyAU I've disabled auto-merge here, so we can maybe push some more related fixes to this PR.

memberExpression.Expression.Type.GetInterfaces().Append(memberExpression.Expression.Type)

I guess it depends on the specific case - not every usage of GetInterfaces() is necessarily wrong...

BTW if this is a common thing, we may want to simply extract out a GetInterfacesInclusive() extension method which does this. We already have GetBaseTypesAndInterfacesInclusive() on SharedTypeExtensions, we could add GetInterfacesInclusive() there...

@ChrisJollyAU
Copy link
Contributor Author

Just quick search for GetInterfaces only produced 24 results. Not that many
15 under src folder
9 under test folder

Of those under src, can eliminate 9 (4 from ProxyFactory and 5 from the Shared extensions (doesnt look to be needed and 1 already like the previous example I mentioned))

In the end it looks like theres only the ones from RelationalSqlTranslatingExpressionVisitor and its Cosmos equivalent that could maybe do with the change

&& unaryExpression.Type.GetInterfaces().Any(e => e == operand.Type)

@roji
Copy link
Member

roji commented Dec 24, 2024

Yeah, that one looks legit, though quite contrived - that seems to strip away up-casts from an interface to a base interface, so the change would also strip away no-op "up-casts" from the interface to itself. The compiler most probably never generates such a useless node, but we could include it for completeness.

@ChrisJollyAU
Copy link
Contributor Author

changing it to && unaryExpression.Type.GetInterfaces().Append(unaryExpression.Type).Any(e => e == operand.Type) doesnt make any difference so not really needed

On this quick look, it doesn't appear that there are any that would need to be changed

@roji roji merged commit 85c1c35 into dotnet:main Dec 24, 2024
7 checks passed
@roji
Copy link
Member

roji commented Dec 24, 2024

OK, thanks @ChrisJollyAU!

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.

Using .Count on a ICollection<T> does not produce EXISTS as it does when using List<T>
2 participants