Skip to content

Commit c75b8e0

Browse files
IDisposablebuyaa-n
andauthored
Add message for AmbiguousMatchException (#84512)
Add information to what is ambiguously matched when an AmbiguousMatchException is thrown as we know what we found multiple matching items for. 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]>
1 parent a93feda commit c75b8e0

File tree

29 files changed

+191
-115
lines changed

29 files changed

+191
-115
lines changed

src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -515,10 +515,11 @@ public static bool IsDefined(MemberInfo element, Type attributeType, bool inheri
515515
if (attrib == null || attrib.Length == 0)
516516
return null;
517517

518+
Attribute match = attrib[0];
518519
if (attrib.Length == 1)
519-
return attrib[0];
520+
return match;
520521

521-
throw new AmbiguousMatchException(SR.RFLCT_AmbigCust);
522+
throw ThrowHelper.GetAmbiguousMatchException(match);
522523
}
523524

524525
#endregion
@@ -614,10 +615,11 @@ public static bool IsDefined(ParameterInfo element, Type attributeType, bool inh
614615
if (attrib == null || attrib.Length == 0)
615616
return null;
616617

618+
Attribute match = attrib[0];
617619
if (attrib.Length == 1)
618-
return attrib[0];
620+
return match;
619621

620-
throw new AmbiguousMatchException(SR.RFLCT_AmbigCust);
622+
throw ThrowHelper.GetAmbiguousMatchException(match);
621623
}
622624

623625
#endregion
@@ -683,10 +685,11 @@ public static bool IsDefined(Module element, Type attributeType, bool inherit)
683685
if (attrib == null || attrib.Length == 0)
684686
return null;
685687

688+
Attribute match = attrib[0];
686689
if (attrib.Length == 1)
687-
return attrib[0];
690+
return match;
688691

689-
throw new AmbiguousMatchException(SR.RFLCT_AmbigCust);
692+
throw ThrowHelper.GetAmbiguousMatchException(match);
690693
}
691694

692695
#endregion
@@ -752,10 +755,11 @@ public static bool IsDefined(Assembly element, Type attributeType, bool inherit)
752755
if (attrib == null || attrib.Length == 0)
753756
return null;
754757

758+
Attribute match = attrib[0];
755759
if (attrib.Length == 1)
756-
return attrib[0];
760+
return match;
757761

758-
throw new AmbiguousMatchException(SR.RFLCT_AmbigCust);
762+
throw ThrowHelper.GetAmbiguousMatchException(match);
759763
}
760764

761765
#endregion

src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2822,9 +2822,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn
28222822
{
28232823
MethodInfo methodInfo = candidates[j];
28242824
if (!System.DefaultBinder.CompareMethodSig(methodInfo, firstCandidate))
2825-
{
2826-
throw new AmbiguousMatchException();
2827-
}
2825+
throw ThrowHelper.GetAmbiguousMatchException(firstCandidate);
28282826
}
28292827

28302828
// All the methods have the exact same name and sig so return the most derived one.
@@ -2878,10 +2876,10 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn
28782876
if (types == null || types.Length == 0)
28792877
{
28802878
// no arguments
2879+
PropertyInfo firstCandidate = candidates[0];
2880+
28812881
if (candidates.Count == 1)
28822882
{
2883-
PropertyInfo firstCandidate = candidates[0];
2884-
28852883
if (returnType is not null && !returnType.IsEquivalentTo(firstCandidate.PropertyType))
28862884
return null;
28872885

@@ -2891,7 +2889,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn
28912889
{
28922890
if (returnType is null)
28932891
// if we are here we have no args or property type to select over and we have more than one property with that name
2894-
throw new AmbiguousMatchException();
2892+
throw ThrowHelper.GetAmbiguousMatchException(firstCandidate);
28952893
}
28962894
}
28972895

@@ -2920,7 +2918,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn
29202918
if ((bindingAttr & eventInfo.BindingFlags) == eventInfo.BindingFlags)
29212919
{
29222920
if (match != null)
2923-
throw new AmbiguousMatchException();
2921+
throw ThrowHelper.GetAmbiguousMatchException(match);
29242922

29252923
match = eventInfo;
29262924
}
@@ -2950,7 +2948,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn
29502948
if (match != null)
29512949
{
29522950
if (ReferenceEquals(fieldInfo.DeclaringType, match.DeclaringType))
2953-
throw new AmbiguousMatchException();
2951+
throw ThrowHelper.GetAmbiguousMatchException(match);
29542952

29552953
if ((match.DeclaringType!.IsInterface) && (fieldInfo.DeclaringType!.IsInterface))
29562954
multipleStaticFieldMatches = true;
@@ -2962,7 +2960,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn
29622960
}
29632961

29642962
if (multipleStaticFieldMatches && match!.DeclaringType!.IsInterface)
2965-
throw new AmbiguousMatchException();
2963+
throw ThrowHelper.GetAmbiguousMatchException(match);
29662964

29672965
return match;
29682966
}
@@ -2998,7 +2996,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn
29982996
if (FilterApplyType(iface, bindingAttr, name, false, ns))
29992997
{
30002998
if (match != null)
3001-
throw new AmbiguousMatchException();
2999+
throw ThrowHelper.GetAmbiguousMatchException(match);
30023000

30033001
match = iface;
30043002
}
@@ -3027,7 +3025,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn
30273025
if (FilterApplyType(nestedType, bindingAttr, name, false, ns))
30283026
{
30293027
if (match != null)
3030-
throw new AmbiguousMatchException();
3028+
throw ThrowHelper.GetAmbiguousMatchException(match);
30313029

30323030
match = nestedType;
30333031
}

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Attribute.NativeAot.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ private static Attribute OneOrNull(IEnumerable<CustomAttributeData> results)
121121
return null;
122122
CustomAttributeData result = enumerator.Current;
123123
if (enumerator.MoveNext())
124-
throw new AmbiguousMatchException();
124+
throw ThrowHelper.GetAmbiguousMatchException(result);
125125
return result.Instantiate();
126126
}
127127

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,10 @@ public void CopyTo(MemberInfo[] array, int startIndex)
112112
// Assuming the policy says it's ok to ignore the ambiguity, we're to resolve in favor of the member
113113
// declared by the most derived type. Since QueriedMemberLists are sorted in order of decreasing derivation,
114114
// that means we let the first match win - unless, of course, they're both the "most derived member".
115-
if (match.DeclaringType.Equals(challenger.DeclaringType))
116-
throw new AmbiguousMatchException();
117-
118-
if (!_policies.OkToIgnoreAmbiguity(match, challenger))
119-
throw new AmbiguousMatchException();
115+
// If they're not from same type, we throw if the policy doesn't allow ambiguity.
116+
if (match.DeclaringType.Equals(challenger.DeclaringType) ||
117+
!_policies.OkToIgnoreAmbiguity(match, challenger))
118+
throw ThrowHelper.GetAmbiguousMatchException(match);
120119
}
121120
else
122121
{

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/ThunkedApis.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ public sealed override Type[] GetGenericArguments()
199199
if (ns != null && !ns.Equals(ifc.Namespace))
200200
continue;
201201
if (match != null)
202-
throw new AmbiguousMatchException();
202+
throw ThrowHelper.GetAmbiguousMatchException(match);
203203
match = ifc;
204204
}
205205
return match;

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.BindingFlags.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,18 +155,21 @@ protected sealed override PropertyInfo GetPropertyImpl(string name, BindingFlags
155155
if (types == null || types.Length == 0)
156156
{
157157
// no arguments
158+
PropertyInfo firstCandidate = candidates[0];
159+
158160
if (candidates.Count == 1)
159161
{
160-
PropertyInfo firstCandidate = candidates[0];
161162
if (returnType is not null && !returnType.IsEquivalentTo(firstCandidate.PropertyType))
162163
return null;
163164
return firstCandidate;
164165
}
165166
else
166167
{
167168
if (returnType is null)
169+
{
168170
// if we are here we have no args or property type to select over and we have more than one property with that name
169-
throw new AmbiguousMatchException();
171+
throw ThrowHelper.GetAmbiguousMatchException(firstCandidate);
172+
}
170173
}
171174
}
172175

src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public sealed override bool Bind(RuntimeAssemblyName refName, bool cacheMissedLo
8484
{
8585
if (foundMatch)
8686
{
87-
exception = new AmbiguousMatchException();
87+
exception = new AmbiguousMatchException(SR.Format(SR.AmbiguousMatchException_Assembly, refName.FullName));
8888
return false;
8989
}
9090

src/coreclr/nativeaot/System.Private.TypeLoader/src/Resources/Strings.resx

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<root>
3-
<!--
4-
Microsoft ResX Schema
5-
3+
<!--
4+
Microsoft ResX Schema
5+
66
Version 2.0
7-
8-
The primary goals of this format is to allow a simple XML format
9-
that is mostly human readable. The generation and parsing of the
10-
various data types are done through the TypeConverter classes
7+
8+
The primary goals of this format is to allow a simple XML format
9+
that is mostly human readable. The generation and parsing of the
10+
various data types are done through the TypeConverter classes
1111
associated with the data types.
12-
12+
1313
Example:
14-
14+
1515
... ado.net/XML headers & schema ...
1616
<resheader name="resmimetype">text/microsoft-resx</resheader>
1717
<resheader name="version">2.0</resheader>
@@ -26,36 +26,36 @@
2626
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
2727
<comment>This is a comment</comment>
2828
</data>
29-
30-
There are any number of "resheader" rows that contain simple
29+
30+
There are any number of "resheader" rows that contain simple
3131
name/value pairs.
32-
33-
Each data row contains a name, and value. The row also contains a
34-
type or mimetype. Type corresponds to a .NET class that support
35-
text/value conversion through the TypeConverter architecture.
36-
Classes that don't support this are serialized and stored with the
32+
33+
Each data row contains a name, and value. The row also contains a
34+
type or mimetype. Type corresponds to a .NET class that support
35+
text/value conversion through the TypeConverter architecture.
36+
Classes that don't support this are serialized and stored with the
3737
mimetype set.
38-
39-
The mimetype is used for serialized objects, and tells the
40-
ResXResourceReader how to depersist the object. This is currently not
38+
39+
The mimetype is used for serialized objects, and tells the
40+
ResXResourceReader how to depersist the object. This is currently not
4141
extensible. For a given mimetype the value must be set accordingly:
42-
43-
Note - application/x-microsoft.net.object.binary.base64 is the format
44-
that the ResXResourceWriter will generate, however the reader can
42+
43+
Note - application/x-microsoft.net.object.binary.base64 is the format
44+
that the ResXResourceWriter will generate, however the reader can
4545
read any of the formats listed below.
46-
46+
4747
mimetype: application/x-microsoft.net.object.binary.base64
48-
value : The object must be serialized with
48+
value : The object must be serialized with
4949
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
5050
: and then encoded with base64 encoding.
51-
51+
5252
mimetype: application/x-microsoft.net.object.soap.base64
53-
value : The object must be serialized with
53+
value : The object must be serialized with
5454
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
5555
: and then encoded with base64 encoding.
5656
5757
mimetype: application/x-microsoft.net.object.bytearray.base64
58-
value : The object must be serialized into a byte array
58+
value : The object must be serialized into a byte array
5959
: using a System.ComponentModel.TypeConverter
6060
: and then encoded with base64 encoding.
6161
-->
@@ -129,4 +129,7 @@
129129
<data name="FileLoadException_RefDefMismatch" xml:space="preserve">
130130
<value>Cannot load assembly '{0}'. The assembly exists but its version {1} is lower than the requested version {2}.</value>
131131
</data>
132+
<data name="AmbiguousMatchException_Assembly" xml:space="preserve">
133+
<value>Ambiguous match found for assembly '{0}'.</value>
134+
</data>
132135
</root>

src/libraries/System.Private.CoreLib/src/Resources/Strings.resx

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -172,18 +172,27 @@
172172
<data name="AppDomain_Policy_PrincipalTwice" xml:space="preserve">
173173
<value>Default principal object cannot be set twice.</value>
174174
</data>
175-
<data name="AmbiguousImplementationException_NullMessage" xml:space="preserve">
176-
<value>Ambiguous implementation found.</value>
177-
</data>
178175
<data name="Arg_AccessException" xml:space="preserve">
179176
<value>Cannot access member.</value>
180177
</data>
181178
<data name="Arg_AccessViolationException" xml:space="preserve">
182179
<value>Attempted to read or write protected memory. This is often an indication that other memory is corrupt.</value>
183180
</data>
184-
<data name="Arg_AmbiguousMatchException" xml:space="preserve">
181+
<data name="Arg_AmbiguousImplementationException_NoMessage" xml:space="preserve">
182+
<value>Ambiguous implementation found.</value>
183+
</data>
184+
<data name="Arg_AmbiguousMatchException_Attribute" xml:space="preserve">
185+
<value>Multiple custom attributes of the same type '{0}' found.</value>
186+
</data>
187+
<data name="Arg_AmbiguousMatchException_NoMessage" xml:space="preserve">
185188
<value>Ambiguous match found.</value>
186189
</data>
190+
<data name="Arg_AmbiguousMatchException_CustomAttributeData" xml:space="preserve">
191+
<value>Ambiguous match found for '{0}'.</value>
192+
</data>
193+
<data name="Arg_AmbiguousMatchException_MemberInfo" xml:space="preserve">
194+
<value>Ambiguous match found for '{0} {1}'.</value>
195+
</data>
187196
<data name="Arg_ApplicationException" xml:space="preserve">
188197
<value>Error in the application.</value>
189198
</data>
@@ -3299,9 +3308,6 @@
32993308
<data name="Resources_StreamNotValid" xml:space="preserve">
33003309
<value>Stream is not a valid resource file.</value>
33013310
</data>
3302-
<data name="RFLCT_AmbigCust" xml:space="preserve">
3303-
<value>Multiple custom attributes of the same type found.</value>
3304-
</data>
33053311
<data name="InvalidFilterCriteriaException_CritInt" xml:space="preserve">
33063312
<value>An Int32 must be provided for the filter criteria.</value>
33073313
</data>

0 commit comments

Comments
 (0)