-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Add message details for AmbiguousMatchException #84512
Conversation
Tagging subscribers to this area: @dotnet/area-system-reflection Issue DetailsAdd information to what is ambiguously matched when an AmbiguousMatchException is thrown as we know what we found multiple matching items for. This is a draft PR to fix #18255 Basic idea is to use the known-first match as a source for the declaring type name and member name to format into the Exception message value. There's a bunch of similar/common/duplicated code all over, so not sure where (which subset) to apply so this PR attempts most of the uses of Need to know before this gets final which of the libraries/builds this needs to be done in:
I also noted that the same localizable string ends up in several assemblies the way things are now (e.g. there's a Strings.resx and matching ThrowHelper.cs for each of the targes above, with some overlapped strings)... not sure there is anything to be done about that, but worth calling out in this Draft
|
@@ -2891,7 +2889,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn | |||
{ | |||
if (returnType is null) | |||
// if we are here we have no args or property type to select over and we have more than one property with that name | |||
throw new AmbiguousMatchException(); | |||
ThrowHelper.ThrowAmbiguousMatchException(firstCandidate); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this is the first use of the helper in this PR, I thought I would mention the strategy. To resolve the original issue and add more information to figure out what was ambiguous, we need to know the name of the member that has ambiguity.
Since PropertyInfo
et al all descend from MemberInfo
, we can make a helper that formats messages from calling .ToString()
on the info and it's DeclaringType
. This yields, for example from String.Length
the values System.String
and Int32 Length
as the insertions. See ThrowHelper.cs
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
Outdated
Show resolved
Hide resolved
|
||
if (!_policies.OkToIgnoreAmbiguity(match, challenger)) | ||
throw new AmbiguousMatchException(); | ||
// If they're not the same method, we throw if the policy doesn't allow ambiguity. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merged the two if
statements as they are logically doing the full check, and in either case throwing with the same info.
...stem.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.BindingFlags.cs
Show resolved
Hide resolved
If this seems like a reasonable approach, I will build out tests for all the paths @steveharter, @marek-safar, @MichalStrehovsky I am not sure I actually need to handle the case of a null |
Check failures do not look to be related to anything I did. |
Fleshed out the rest of the AmbiguousMatchExeception uses (and removed talk about doing the same for AmbiguousImplementationExceptio as there's no details available) |
011f8d2
to
7fcafef
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cleanup and fleshed things out a bit more. Seems to build.
src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/DefaultBinder.cs
Show resolved
Hide resolved
src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/DefaultBinder.cs
Show resolved
Hide resolved
src/libraries/System.Reflection.MetadataLoadContext/src/System/ThrowHelper.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx
Outdated
Show resolved
Hide resolved
...aries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/ComAwareEventInfo.cs
Show resolved
Hide resolved
src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
Outdated
Show resolved
Hide resolved
...veaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Reflection.Context/src/System/Reflection/Context/ThrowHelper.cs
Outdated
Show resolved
Hide resolved
Thanks TONS for the review @buyaa-n ! I just had eye surgery so I'm face down with minimal screen time for at least a week, will integrate your changes ASAP! |
|
040cb97
to
2887a9c
Compare
ab3b597
to
be66552
Compare
Addressed final feedback, rebased and squashed. Clean build except:
Build Libraries Test Run release mono osx x64 Debug/Publish Logs has an unrelated error
|
...dataLoadContext/src/System/Reflection/TypeLoading/CustomAttributes/CustomAttributeHelpers.cs
Show resolved
Hide resolved
Not sure why the Detailed version isn't useful, but if we're killing off the uses, I can remove the literal. I added these originally because what I was getting back from the various If we're absolutely sure we should never emit the type's information, I'll make one more pass to neaten the .ResX files and rebase/squash again |
It is more about the extra conditional added for checking if the Also, another option could be just passing the declaring type without null check as string.Format accepts null. The message would just have and extra space when the declaring type is null If you want to keep it all as is I am also OK with that |
...dataLoadContext/src/System/Reflection/TypeLoading/CustomAttributes/CustomAttributeHelpers.cs
Outdated
Show resolved
Hide resolved
When we're figuring out why the exception was thrown (at post-mortem debug), knowing the exact class that had ONE of the ambiguities lets us narrow things down. We wouldn't be doing this on core classes usually, so having the type seems very helpful for opening the right source file :)
I like that idea... lemme churn that a bit and commit. |
d432b12
to
97df159
Compare
Incorporated these last few discussions. Now always passes the (possibly null) |
97df159
to
3bff35c
Compare
|
Add information to what is ambiguously matched when an AmbiguousMatchException is thrown as we know what we found multiple matching items for. Addressed review feedback Renames of SR/RESX strings. Removal of `.ToString()` calls. Remove unused strings from RESX RESX string rewording. Change the exception format string to "found for" style. Cleanup of DefaultBinder.cs FindMostDerived* to use the local variable. Remove copy-pasta ThrowHelper.cs instance. Don't emit the CustomAtrributeData's type All MemberInfo cases pass a (possibly null) `DeclaringType`. Co-authored-by: Buyaa Namnan <[email protected]>
3bff35c
to
8899574
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @IDisposable LGTM
The failures unrelated and all known issues, merging |
Add information to what is ambiguously matched when an AmbiguousMatchException or an AmbiguousInvovationException is thrown as we know what we found multiple matching items for.
Fixes #18255
Basic idea is to use the known-first match as a source for the declaring type name and member name to format into the Exception message value.
There's a bunch of similar/common/duplicated code all over, so not sure where (which subset) to apply so this PR attempts most of the uses of
AmbigousMatchException
andAmbiguousInvocationException
(which has similar issues).Need to know before this gets final which of the libraries/builds this needs to be done in:
I also noted that the same localizable string ends up in several assemblies the way things are now (e.g. there's a Strings.resx and matching ThrowHelper.cs for each of the target libraries above, with some overlapped strings). I am not sure there is anything to be done about that, but worth calling out in this PR