From 6a27959cbcf8ea2e1886bf4928492847c22661ec Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 16 Apr 2022 09:07:33 +0200 Subject: [PATCH] Improve decompilation of delegate references of extension methods. --- .../TestCases/Pretty/DelegateConstruction.cs | 2 +- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 92 ++++++++++++------- .../CSharp/ExpressionBuilder.cs | 2 +- 3 files changed, 62 insertions(+), 34 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs index 154502bdb..ac50c619a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs @@ -496,7 +496,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void ExtensionDelegateReference(IEnumerable ints) { - Use2((Func, IEnumerable>)ints.Select); + Use2(ints.Select); } #if CS70 diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 9701e9411..00f7fc91f 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -1612,29 +1612,40 @@ namespace ICSharpCode.Decompiler.CSharp thisArg = thisArgBox.Argument; } TranslatedExpression target = expressionBuilder.Translate(thisArg, targetType); + var currentTarget = target; bool targetCasted = false; bool addTypeArguments = false; // Initial inputs for IsUnambiguousMethodReference: ResolveResult targetResolveResult = target.ResolveResult; IReadOnlyList typeArguments = EmptyList.Instance; + if (thisArg.MatchLdNull()) + { + targetCasted = true; + currentTarget = currentTarget.ConvertTo(targetType, expressionBuilder); + targetResolveResult = currentTarget.ResolveResult; + } // Find somewhat minimal solution: ResolveResult result; - targetCasted = true; - target = target.ConvertTo(targetType, expressionBuilder); - targetResolveResult = target.ResolveResult; - addTypeArguments = true; - typeArguments = method.TypeArguments; - result = new MethodGroupResolveResult( - targetResolveResult, method.Name, - new[] { - new MethodListWithDeclaringType( - null, - new[] { method } - ) - }, - typeArguments - ); - return (target, addTypeArguments, method.Name, result); + while (!IsUnambiguousMethodReference(expectedTargetDetails, method, targetResolveResult, typeArguments, true, out result)) + { + if (!targetCasted) + { + // try casting target + targetCasted = true; + currentTarget = currentTarget.ConvertTo(targetType, expressionBuilder); + targetResolveResult = currentTarget.ResolveResult; + continue; + } + if (!addTypeArguments) + { + // try adding type arguments + addTypeArguments = true; + typeArguments = method.TypeArguments; + continue; + } + break; + } + return (currentTarget, addTypeArguments, method.Name, result); } else { @@ -1672,7 +1683,7 @@ namespace ICSharpCode.Decompiler.CSharp IReadOnlyList typeArguments = EmptyList.Instance; // Find somewhat minimal solution: ResolveResult result; - while (!IsUnambiguousMethodReference(expectedTargetDetails, method, targetResolveResult, typeArguments, out result)) + while (!IsUnambiguousMethodReference(expectedTargetDetails, method, targetResolveResult, typeArguments, false, out result)) { if (!addTypeArguments) { @@ -1717,30 +1728,47 @@ namespace ICSharpCode.Decompiler.CSharp return oce; } - bool IsUnambiguousMethodReference(ExpectedTargetDetails expectedTargetDetails, IMethod method, ResolveResult target, IReadOnlyList typeArguments, out ResolveResult result) + bool IsUnambiguousMethodReference(ExpectedTargetDetails expectedTargetDetails, IMethod method, ResolveResult target, IReadOnlyList typeArguments, bool isExtensionMethodReference, out ResolveResult result) { Log.WriteLine("IsUnambiguousMethodReference: Performing overload resolution for " + method); var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); - var or = new OverloadResolution(resolver.Compilation, - arguments: method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type)), // there are no arguments, use parameter types - argumentNames: null, // argument names are not possible - typeArguments.ToArray(), - conversions: expressionBuilder.resolver.conversions - ); - if (target == null) + OverloadResolution or; + + if (isExtensionMethodReference) { - result = resolver.ResolveSimpleName(method.Name, typeArguments, isInvocationTarget: false); - if (!(result is MethodGroupResolveResult mgrr)) + var resolver = this.resolver.WithCurrentUsingScope(this.expressionBuilder.statementBuilder.decompileRun.UsingScope.Resolve(this.resolver.Compilation)); + result = resolver.ResolveMemberAccess(target, method.Name, typeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult; + if (result == null) + return false; + or = ((MethodGroupResolveResult)result).PerformOverloadResolution(resolver.CurrentTypeResolveContext.Compilation, + method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type)), + argumentNames: null, allowExtensionMethods: true); + if (or == null || or.IsAmbiguous) return false; - or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); } else { - result = lookup.Lookup(target, method.Name, typeArguments, isInvocation: false); - if (!(result is MethodGroupResolveResult mgrr)) - return false; - or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); + or = new OverloadResolution(resolver.Compilation, + arguments: method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type)), // there are no arguments, use parameter types + argumentNames: null, // argument names are not possible + typeArguments.ToArray(), + conversions: expressionBuilder.resolver.conversions + ); + if (target == null) + { + result = resolver.ResolveSimpleName(method.Name, typeArguments, isInvocationTarget: false); + if (!(result is MethodGroupResolveResult mgrr)) + return false; + or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); + } + else + { + result = lookup.Lookup(target, method.Name, typeArguments, isInvocation: false); + if (!(result is MethodGroupResolveResult mgrr)) + return false; + or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); + } } var foundMethod = or.GetBestCandidateWithSubstitutedTypeArguments(); diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 23454d8cd..236a1c023 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -71,7 +71,7 @@ namespace ICSharpCode.Decompiler.CSharp /// sealed class ExpressionBuilder : ILVisitor { - readonly StatementBuilder statementBuilder; + internal readonly StatementBuilder statementBuilder; readonly IDecompilerTypeSystem typeSystem; internal readonly ITypeResolveContext decompilationContext; internal readonly ILFunction currentFunction;