From 889957495aaaf4e12161af2eb012d64048777b59 Mon Sep 17 00:00:00 2001 From: Marc Brooks Date: Tue, 30 May 2023 20:47:59 -0500 Subject: [PATCH] Add message for AmbiguousMatchException 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 --- .../src/System/Attribute.CoreCLR.cs | 20 ++++--- .../src/System/RuntimeType.CoreCLR.cs | 20 +++---- .../System/Reflection/Attribute.NativeAot.cs | 2 +- .../Runtime/BindingFlagSupport/QueryResult.cs | 9 ++- .../Reflection/Runtime/General/ThunkedApis.cs | 2 +- .../TypeInfos/RuntimeTypeInfo.BindingFlags.cs | 7 ++- .../Execution/AssemblyBinderImplementation.cs | 2 +- .../src/Resources/Strings.resx | 57 ++++++++++--------- .../src/Resources/Strings.resx | 20 ++++--- .../src/System/DefaultBinder.cs | 30 +++++----- .../Reflection/AmbiguousMatchException.cs | 2 +- .../AmbiguousImplementationException.cs | 2 +- .../src/System/ThrowHelper.cs | 18 ++++++ .../src/Resources/Strings.resx | 5 +- .../Reflection/Context/Custom/CustomType.cs | 9 +-- .../src/Resources/Strings.resx | 8 ++- ...stem.Reflection.MetadataLoadContext.csproj | 1 + .../src/System/Reflection/DefaultBinder.cs | 14 +++-- .../Runtime/BindingFlagSupport/QueryResult.cs | 4 +- .../CustomAttributeHelpers.cs | 6 +- .../RuntimeTypeInfo.BindingFlags.cs | 5 +- .../Ecma/EcmaDefinitionType.BindingFlags.cs | 2 +- .../TypeLoading/Types/RoType.GetInterface.cs | 2 +- .../src/System/ThrowHelper.cs | 25 ++++++++ .../src/Resources/Strings.resx | 2 +- .../InteropServices/ComAwareEventInfo.cs | 7 ++- .../src/System/Attribute.Mono.cs | 5 +- .../Emit/RuntimeTypeBuilder.Mono.cs | 2 +- .../src/System/RuntimeType.Mono.cs | 18 +++--- 29 files changed, 191 insertions(+), 115 deletions(-) create mode 100644 src/libraries/System.Reflection.MetadataLoadContext/src/System/ThrowHelper.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs index 258d0470e71b0e..9365f8ba0b8695 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs @@ -515,10 +515,11 @@ public static bool IsDefined(MemberInfo element, Type attributeType, bool inheri if (attrib == null || attrib.Length == 0) return null; + Attribute match = attrib[0]; if (attrib.Length == 1) - return attrib[0]; + return match; - throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + throw ThrowHelper.GetAmbiguousMatchException(match); } #endregion @@ -614,10 +615,11 @@ public static bool IsDefined(ParameterInfo element, Type attributeType, bool inh if (attrib == null || attrib.Length == 0) return null; + Attribute match = attrib[0]; if (attrib.Length == 1) - return attrib[0]; + return match; - throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + throw ThrowHelper.GetAmbiguousMatchException(match); } #endregion @@ -683,10 +685,11 @@ public static bool IsDefined(Module element, Type attributeType, bool inherit) if (attrib == null || attrib.Length == 0) return null; + Attribute match = attrib[0]; if (attrib.Length == 1) - return attrib[0]; + return match; - throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + throw ThrowHelper.GetAmbiguousMatchException(match); } #endregion @@ -752,10 +755,11 @@ public static bool IsDefined(Assembly element, Type attributeType, bool inherit) if (attrib == null || attrib.Length == 0) return null; + Attribute match = attrib[0]; if (attrib.Length == 1) - return attrib[0]; + return match; - throw new AmbiguousMatchException(SR.RFLCT_AmbigCust); + throw ThrowHelper.GetAmbiguousMatchException(match); } #endregion diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 872f9d08b1910e..e3590ca68e1cef 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -2822,9 +2822,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn { MethodInfo methodInfo = candidates[j]; if (!System.DefaultBinder.CompareMethodSig(methodInfo, firstCandidate)) - { - throw new AmbiguousMatchException(); - } + throw ThrowHelper.GetAmbiguousMatchException(firstCandidate); } // 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 if (types == null || types.Length == 0) { // no arguments + PropertyInfo firstCandidate = candidates[0]; + if (candidates.Count == 1) { - PropertyInfo firstCandidate = candidates[0]; - if (returnType is not null && !returnType.IsEquivalentTo(firstCandidate.PropertyType)) return null; @@ -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(); + throw ThrowHelper.GetAmbiguousMatchException(firstCandidate); } } @@ -2920,7 +2918,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn if ((bindingAttr & eventInfo.BindingFlags) == eventInfo.BindingFlags) { if (match != null) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(match); match = eventInfo; } @@ -2950,7 +2948,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn if (match != null) { if (ReferenceEquals(fieldInfo.DeclaringType, match.DeclaringType)) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(match); if ((match.DeclaringType!.IsInterface) && (fieldInfo.DeclaringType!.IsInterface)) multipleStaticFieldMatches = true; @@ -2962,7 +2960,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn } if (multipleStaticFieldMatches && match!.DeclaringType!.IsInterface) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(match); return match; } @@ -2998,7 +2996,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn if (FilterApplyType(iface, bindingAttr, name, false, ns)) { if (match != null) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(match); match = iface; } @@ -3027,7 +3025,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn if (FilterApplyType(nestedType, bindingAttr, name, false, ns)) { if (match != null) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(match); match = nestedType; } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Attribute.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Attribute.NativeAot.cs index f7604612b5ad5e..fe6505f24c9c90 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Attribute.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Attribute.NativeAot.cs @@ -121,7 +121,7 @@ private static Attribute OneOrNull(IEnumerable results) return null; CustomAttributeData result = enumerator.Current; if (enumerator.MoveNext()) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(result); return result.Instantiate(); } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.cs index 301fe25f6fa389..6288b58c405217 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.cs @@ -112,11 +112,10 @@ public void CopyTo(MemberInfo[] array, int startIndex) // Assuming the policy says it's ok to ignore the ambiguity, we're to resolve in favor of the member // declared by the most derived type. Since QueriedMemberLists are sorted in order of decreasing derivation, // that means we let the first match win - unless, of course, they're both the "most derived member". - if (match.DeclaringType.Equals(challenger.DeclaringType)) - throw new AmbiguousMatchException(); - - if (!_policies.OkToIgnoreAmbiguity(match, challenger)) - throw new AmbiguousMatchException(); + // If they're not from same type, we throw if the policy doesn't allow ambiguity. + if (match.DeclaringType.Equals(challenger.DeclaringType) || + !_policies.OkToIgnoreAmbiguity(match, challenger)) + throw ThrowHelper.GetAmbiguousMatchException(match); } else { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/ThunkedApis.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/ThunkedApis.cs index 3e0765205c6373..99858e0bbaa784 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/ThunkedApis.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/ThunkedApis.cs @@ -199,7 +199,7 @@ public sealed override Type[] GetGenericArguments() if (ns != null && !ns.Equals(ifc.Namespace)) continue; if (match != null) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(match); match = ifc; } return match; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.BindingFlags.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.BindingFlags.cs index dcb0ed3e093320..35ae4a96ea7c28 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.BindingFlags.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.BindingFlags.cs @@ -155,9 +155,10 @@ protected sealed override PropertyInfo GetPropertyImpl(string name, BindingFlags if (types == null || types.Length == 0) { // no arguments + PropertyInfo firstCandidate = candidates[0]; + if (candidates.Count == 1) { - PropertyInfo firstCandidate = candidates[0]; if (returnType is not null && !returnType.IsEquivalentTo(firstCandidate.PropertyType)) return null; return firstCandidate; @@ -165,8 +166,10 @@ protected sealed override PropertyInfo GetPropertyImpl(string name, BindingFlags else { 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(); + throw ThrowHelper.GetAmbiguousMatchException(firstCandidate); + } } } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs index dd3f3dac45b191..bd6c07a99e2651 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Reflection/Execution/AssemblyBinderImplementation.cs @@ -84,7 +84,7 @@ public sealed override bool Bind(RuntimeAssemblyName refName, bool cacheMissedLo { if (foundMatch) { - exception = new AmbiguousMatchException(); + exception = new AmbiguousMatchException(SR.Format(SR.AmbiguousMatchException_Assembly, refName.FullName)); return false; } diff --git a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Resources/Strings.resx b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Resources/Strings.resx index 949e8adc2fbadc..cddf33a5bca989 100644 --- a/src/coreclr/nativeaot/System.Private.TypeLoader/src/Resources/Strings.resx +++ b/src/coreclr/nativeaot/System.Private.TypeLoader/src/Resources/Strings.resx @@ -1,17 +1,17 @@  - @@ -129,4 +129,7 @@ Cannot load assembly '{0}'. The assembly exists but its version {1} is lower than the requested version {2}. + + Ambiguous match found for assembly '{0}'. + diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 90536bdba39bb1..9b6bb54330f33c 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -172,18 +172,27 @@ Default principal object cannot be set twice. - - Ambiguous implementation found. - Cannot access member. Attempted to read or write protected memory. This is often an indication that other memory is corrupt. - + + Ambiguous implementation found. + + + Multiple custom attributes of the same type '{0}' found. + + Ambiguous match found. + + Ambiguous match found for '{0}'. + + + Ambiguous match found for '{0} {1}'. + Error in the application. @@ -3299,9 +3308,6 @@ Stream is not a valid resource file. - - Multiple custom attributes of the same type found. - An Int32 must be provided for the filter criteria. diff --git a/src/libraries/System.Private.CoreLib/src/System/DefaultBinder.cs b/src/libraries/System.Private.CoreLib/src/System/DefaultBinder.cs index 1a2e483ce371d0..44c14492878cae 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DefaultBinder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DefaultBinder.cs @@ -383,8 +383,10 @@ public sealed override MethodBase BindToMethod( #endregion } + MethodBase bestMatch = candidates[currentMin]!; + if (ambig) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(bestMatch); // Reorder (if needed) if (names != null) @@ -395,7 +397,7 @@ public sealed override MethodBase BindToMethod( // If the parameters and the args are not the same length or there is a paramArray // then we need to create a argument array. - ParameterInfo[] parameters = candidates[currentMin]!.GetParametersNoCopy(); + ParameterInfo[] parameters = bestMatch.GetParametersNoCopy(); if (parameters.Length == args.Length) { if (paramArrayTypes[currentMin] != null) @@ -431,7 +433,7 @@ public sealed override MethodBase BindToMethod( } else { - if ((candidates[currentMin]!.CallingConvention & CallingConventions.VarArgs) == 0) + if ((bestMatch.CallingConvention & CallingConventions.VarArgs) == 0) { object[] objs = new object[parameters.Length]; int paramArrayPos = parameters.Length - 1; @@ -442,7 +444,7 @@ public sealed override MethodBase BindToMethod( } } - return candidates[currentMin]!; + return bestMatch; } // Given a set of fields that match the base criteria, select a field. @@ -526,9 +528,10 @@ public sealed override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo } } } + FieldInfo bestMatch = candidates[currentMin]; if (ambig) - throw new AmbiguousMatchException(); - return candidates[currentMin]; + throw ThrowHelper.GetAmbiguousMatchException(bestMatch); + return bestMatch; } // Given a set of methods that match the base criteria, select a method based @@ -620,9 +623,10 @@ public sealed override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo } } } + MethodBase bestMatch = candidates[currentMin]; if (ambig) - throw new AmbiguousMatchException(); - return candidates[currentMin]; + throw ThrowHelper.GetAmbiguousMatchException(bestMatch); + return bestMatch; } // Given a set of properties that match the base criteria, select one. @@ -734,10 +738,10 @@ public sealed override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo currentMin = i; } } - + PropertyInfo bestMatch = candidates[currentMin]; if (ambig) - throw new AmbiguousMatchException(); - return candidates[currentMin]; + throw ThrowHelper.GetAmbiguousMatchException(bestMatch); + return bestMatch; } // ChangeType @@ -850,7 +854,7 @@ public sealed override void ReorderArgumentArray(ref object?[] args, object stat continue; if (bestMatch != null) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(bestMatch); bestMatch = match[i]; } @@ -1137,7 +1141,7 @@ private static int GetHierarchyDepth(Type t) // This can only happen if at least one is vararg or generic. if (currentHierarchyDepth == deepestHierarchy) { - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(methWithDeepestHierarchy!); } // Check to see if this method is on the most derived class. diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/AmbiguousMatchException.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/AmbiguousMatchException.cs index e86467d58a2330..b484a1dea2eda2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/AmbiguousMatchException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/AmbiguousMatchException.cs @@ -10,7 +10,7 @@ namespace System.Reflection public sealed class AmbiguousMatchException : SystemException { public AmbiguousMatchException() - : base(SR.Arg_AmbiguousMatchException) + : base(SR.Arg_AmbiguousMatchException_NoMessage) { HResult = HResults.COR_E_AMBIGUOUSMATCH; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/AmbiguousImplementationException.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/AmbiguousImplementationException.cs index 692a26884d4875..05665e089f33b6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/AmbiguousImplementationException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/AmbiguousImplementationException.cs @@ -10,7 +10,7 @@ namespace System.Runtime public sealed class AmbiguousImplementationException : Exception { public AmbiguousImplementationException() - : base(SR.AmbiguousImplementationException_NullMessage) + : base(SR.Arg_AmbiguousImplementationException_NoMessage) { HResult = HResults.COR_E_AMBIGUOUSIMPLEMENTATION; } diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index 3f4ae02f9befc6..21281e4a9b6610 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -40,6 +40,8 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Numerics; +using System.Reflection; +using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -605,6 +607,22 @@ internal static void ThrowFormatIndexOutOfRange() throw new FormatException(SR.Format_IndexOutOfRange); } + internal static AmbiguousMatchException GetAmbiguousMatchException(MemberInfo memberInfo) + { + Type? declaringType = memberInfo.DeclaringType; + return new AmbiguousMatchException(SR.Format(SR.Arg_AmbiguousMatchException_MemberInfo, declaringType, memberInfo)); + } + + internal static AmbiguousMatchException GetAmbiguousMatchException(Attribute attribute) + { + return new AmbiguousMatchException(SR.Format(SR.Arg_AmbiguousMatchException_Attribute, attribute)); + } + + internal static AmbiguousMatchException GetAmbiguousMatchException(CustomAttributeData customAttributeData) + { + return new AmbiguousMatchException(SR.Format(SR.Arg_AmbiguousMatchException_CustomAttributeData, customAttributeData)); + } + private static Exception GetArraySegmentCtorValidationFailedException(Array? array, int offset, int count) { if (array == null) diff --git a/src/libraries/System.Reflection.Context/src/Resources/Strings.resx b/src/libraries/System.Reflection.Context/src/Resources/Strings.resx index 3b0d6ec30a2a62..275355e7de70ee 100644 --- a/src/libraries/System.Reflection.Context/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.Context/src/Resources/Strings.resx @@ -108,4 +108,7 @@ Object of type '{0}' cannot be converted to type '{1}'. - \ No newline at end of file + + Ambiguous match found for '{0} {1}'. + + diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs index 543e113748995d..95ee34ab4504b6 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs @@ -257,7 +257,6 @@ public override MethodInfo[] GetMethods(BindingFlags bindingAttr) } } - if (matchingMethods.Count == 0) return null; @@ -266,10 +265,12 @@ public override MethodInfo[] GetMethods(BindingFlags bindingAttr) Debug.Assert(types == null || types.Length == 0); // matches any signature + MethodInfo match = matchingMethods[0]; if (matchingMethods.Count == 1) - return matchingMethods[0]; - else - throw new AmbiguousMatchException(); + return match; + + Type? declaringType = match.DeclaringType; + throw new AmbiguousMatchException(SR.Format(SR.Arg_AmbiguousMatchException_MemberInfo, declaringType, match)); } else { diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/Resources/Strings.resx b/src/libraries/System.Reflection.MetadataLoadContext/src/Resources/Strings.resx index 4412920b3cdf8d..2f48f0bd2ce47b 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/Resources/Strings.resx +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/Resources/Strings.resx @@ -270,4 +270,10 @@ The path '{0}' is not valid. - \ No newline at end of file + + Ambiguous match found for '{0}'. + + + Ambiguous match found for '{0} {1}'. + + diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System.Reflection.MetadataLoadContext.csproj b/src/libraries/System.Reflection.MetadataLoadContext/src/System.Reflection.MetadataLoadContext.csproj index fb84ae946b87e5..76d07ddacf18dc 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System.Reflection.MetadataLoadContext.csproj +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System.Reflection.MetadataLoadContext.csproj @@ -147,6 +147,7 @@ + diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/DefaultBinder.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/DefaultBinder.cs index e74384109ba342..10501e11c4a4ac 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/DefaultBinder.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/DefaultBinder.cs @@ -137,9 +137,10 @@ public sealed override MethodBase BindToMethod( ambig = false; } } + MethodBase bestMatch = candidates[currentMin]; if (ambig) - throw new AmbiguousMatchException(); - return candidates[currentMin]; + throw ThrowHelper.GetAmbiguousMatchException(bestMatch); + return bestMatch; } // Given a set of properties that match the base criteria, select one. @@ -256,9 +257,10 @@ public sealed override MethodBase BindToMethod( } } + PropertyInfo bestMatch = candidates[currentMin]; if (ambig) - throw new AmbiguousMatchException(); - return candidates[currentMin]; + throw ThrowHelper.GetAmbiguousMatchException(bestMatch); + return bestMatch; } // The default binder doesn't support any change type functionality. @@ -341,7 +343,7 @@ public sealed override MethodBase BindToMethod( continue; if (bestMatch != null) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(bestMatch); bestMatch = match[i]; } @@ -606,7 +608,7 @@ private static int GetHierarchyDepth(Type t) // This can only happen if at least one is vararg or generic. if (currentHierarchyDepth == deepestHierarchy) { - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(methWithDeepestHierarchy!); } // Check to see if this method is on the most derived class. diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.cs index f90ff00c6c4a1f..74ace1ca538ac3 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/Runtime/BindingFlagSupport/QueryResult.cs @@ -112,11 +112,11 @@ public void CopyTo(MemberInfo[] array, int startIndex) // declared by the most derived type. Since QueriedMemberLists are sorted in order of decreasing derivation, // that means we let the first match win - unless, of course, they're both the "most derived member". if (match.DeclaringType!.Equals(challenger.DeclaringType)) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(match); MemberPolicies policies = MemberPolicies.Default; if (!policies.OkToIgnoreAmbiguity(match, challenger)) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(match); } else { diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/CustomAttributes/CustomAttributeHelpers.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/CustomAttributes/CustomAttributeHelpers.cs index 56d7bfbf3a6cf2..745b5d13c7cdf7 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/CustomAttributes/CustomAttributeHelpers.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/CustomAttributes/CustomAttributeHelpers.cs @@ -17,10 +17,10 @@ public static CustomAttributeNamedArgument ToCustomAttributeNamedArgument(this T MemberInfo[] members = attributeType.GetMember(name, MemberTypes.Field | MemberTypes.Property, BindingFlags.Public | BindingFlags.Instance); if (members.Length == 0) throw new MissingMemberException(attributeType.FullName, name); + MemberInfo match = members[0]; if (members.Length > 1) - throw new AmbiguousMatchException(); - - return new CustomAttributeNamedArgument(members[0], new CustomAttributeTypedArgument(argumentType!, value)); + throw ThrowHelper.GetAmbiguousMatchException(match); + return new CustomAttributeNamedArgument(match, new CustomAttributeTypedArgument(argumentType!, value)); } /// diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/RuntimeTypeInfo.BindingFlags.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/RuntimeTypeInfo.BindingFlags.cs index 247379c22f4bf5..ed9d80059b22b0 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/RuntimeTypeInfo.BindingFlags.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/RuntimeTypeInfo.BindingFlags.cs @@ -136,10 +136,11 @@ internal partial class RoType // For perf and .NET Framework compat, fast-path these specific checks before calling on the binder to break ties. if (types == null || types.Length == 0) { + PropertyInfo firstCandidate = candidates[0]; + // no arguments if (candidates.Count == 1) { - PropertyInfo firstCandidate = candidates[0]; if (!(returnType is null) && !returnType.IsEquivalentTo(firstCandidate.PropertyType)) return null; return firstCandidate; @@ -148,7 +149,7 @@ internal partial class RoType { 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(); + throw ThrowHelper.GetAmbiguousMatchException(firstCandidate); } } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/Ecma/EcmaDefinitionType.BindingFlags.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/Ecma/EcmaDefinitionType.BindingFlags.cs index 099c52d3b33265..cf194198064c03 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/Ecma/EcmaDefinitionType.BindingFlags.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/Ecma/EcmaDefinitionType.BindingFlags.cs @@ -94,7 +94,7 @@ internal sealed override IEnumerable GetNestedTypesCore(NameFilter? filt if (nestedTypeDefinition.Name.Equals(utf8Name, reader)) { if (match != null) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(match); match = handle.ResolveTypeDef(GetEcmaModule()); } } diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/RoType.GetInterface.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/RoType.GetInterface.cs index 60530bb16fa71f..e9d9881cc52bc4 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/RoType.GetInterface.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/RoType.GetInterface.cs @@ -33,7 +33,7 @@ internal partial class RoType if (ns.Length != 0 && !ns.Equals(ifc.Namespace)) continue; if (match != null) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(match); match = ifc; } return match; diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/ThrowHelper.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/ThrowHelper.cs new file mode 100644 index 00000000000000..0b28c0cf90532a --- /dev/null +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/ThrowHelper.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This file defines an internal static class used to throw exceptions in the +// the System.Reflection.MetadataLoadContext code. + +using System.Reflection; +using System.Reflection.TypeLoading; + +namespace System +{ + internal static class ThrowHelper + { + internal static AmbiguousMatchException GetAmbiguousMatchException(RoDefinitionType roDefinitionType) + { + return new AmbiguousMatchException(SR.Format(SR.Arg_AmbiguousMatchException_RoDefinitionType, roDefinitionType.FullName)); + } + + internal static AmbiguousMatchException GetAmbiguousMatchException(MemberInfo memberInfo) + { + Type? declaringType = memberInfo.DeclaringType; + return new AmbiguousMatchException(SR.Format(SR.Arg_AmbiguousMatchException_MemberInfo, declaringType, memberInfo)); + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx b/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx index 08d7e045a580f5..828dc3e2b9fe01 100644 --- a/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx +++ b/src/libraries/System.Runtime.InteropServices/src/Resources/Strings.resx @@ -70,7 +70,7 @@ Event invocation for COM objects requires the declaring interface of the event to be attributed with ComEventInterfaceAttribute. - More than one ComEventInterfaceAttribute was found on the declaring interface of the event. + More than one ComEventInterfaceAttribute found for '{0}' on the declaring interface of the event. Event invocation for COM objects requires event to be attributed with DispIdAttribute. diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/ComAwareEventInfo.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/ComAwareEventInfo.cs index 50fbb3bb523e6f..2ecc3eb7cc9c03 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/ComAwareEventInfo.cs +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/ComAwareEventInfo.cs @@ -106,14 +106,15 @@ private static void GetDataForComInvocation(EventInfo eventInfo, out Guid source throw new InvalidOperationException(SR.InvalidOperation_NoComEventInterfaceAttribute); } + ComEventInterfaceAttribute interfaceAttribute = (ComEventInterfaceAttribute)comEventInterfaces[0]; + if (comEventInterfaces.Length > 1) { - throw new AmbiguousMatchException(SR.AmbiguousMatch_MultipleEventInterfaceAttributes); + throw new AmbiguousMatchException(SR.Format(SR.AmbiguousMatch_MultipleEventInterfaceAttributes, interfaceAttribute)); } - Type sourceInterface = ((ComEventInterfaceAttribute)comEventInterfaces[0]).SourceInterface; + Type sourceInterface = interfaceAttribute.SourceInterface; Guid guid = sourceInterface.GUID; - MethodInfo methodInfo = sourceInterface.GetMethod(eventInfo.Name)!; Attribute? dispIdAttribute = Attribute.GetCustomAttribute(methodInfo, typeof(DispIdAttribute)); if (dispIdAttribute == null) diff --git a/src/mono/System.Private.CoreLib/src/System/Attribute.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Attribute.Mono.cs index 7cdec768c13add..f885a3560911b2 100644 --- a/src/mono/System.Private.CoreLib/src/System/Attribute.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Attribute.Mono.cs @@ -17,9 +17,10 @@ public partial class Attribute object[] attrs = CustomAttribute.GetCustomAttributes(element, attributeType, inherit); if (attrs == null || attrs.Length == 0) return null; + Attribute match = (Attribute)attrs[0]; if (attrs.Length != 1) - throw new AmbiguousMatchException(); - return (Attribute)(attrs[0]); + throw ThrowHelper.GetAmbiguousMatchException(match); + return match; } public static Attribute? GetCustomAttribute(Assembly element, Type attributeType) => GetAttr(element, attributeType, true); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs index 8f4729a6e9150a..cb8beb606f1b85 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeTypeBuilder.Mono.cs @@ -431,7 +431,7 @@ protected override void AddInterfaceImplementationCore([DynamicallyAccessedMembe if (types == null) { if (count > 1) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(found!); return found; } MethodBase[] match = new MethodBase[count]; diff --git a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs index 014aa965939049..e34b3788e76a8e 100644 --- a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs @@ -794,7 +794,7 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) { MethodInfo methodInfo = candidates[j]; if (!System.DefaultBinder.CompareMethodSig(methodInfo, firstCandidate)) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(firstCandidate); } // All the methods have the exact same name and sig so return the most derived one. @@ -850,10 +850,10 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) if (types == null || types.Length == 0) { // no arguments + PropertyInfo firstCandidate = candidates[0]; + if (candidates.Count == 1) { - PropertyInfo firstCandidate = candidates[0]; - if (returnType is not null && !returnType.IsEquivalentTo(firstCandidate.PropertyType)) return null; @@ -863,7 +863,7 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) { 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(); + throw ThrowHelper.GetAmbiguousMatchException(firstCandidate); } } @@ -894,7 +894,7 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) if ((bindingAttr & eventInfo.BindingFlags) == eventInfo.BindingFlags) { if (match != null) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(match); match = eventInfo; } @@ -923,7 +923,7 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) if (match != null) { if (ReferenceEquals(fieldInfo.DeclaringType, match.DeclaringType)) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(match); if (match.DeclaringType!.IsInterface && fieldInfo.DeclaringType!.IsInterface) multipleStaticFieldMatches = true; @@ -935,7 +935,7 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) } if (multipleStaticFieldMatches && match!.DeclaringType!.IsInterface) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(match); return match; } @@ -987,7 +987,7 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) if (FilterApplyType(iface, bindingAttr, name, false, ns)) { if (match != null) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(match); match = iface; } @@ -1015,7 +1015,7 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) if (FilterApplyType(nestedType, bindingAttr, name, false, ns)) { if (match != null) - throw new AmbiguousMatchException(); + throw ThrowHelper.GetAmbiguousMatchException(match); match = nestedType; }