From 8da26dc3154f06391e02082de670b9360d137f92 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 10 Mar 2018 02:41:00 +0100 Subject: [PATCH] Fix #1080: Invalid null propagation decompilation with ambiguous extension method call --- .../Transforms/IntroduceExtensionMethods.cs | 19 +++++++--- .../IL/Transforms/NullPropagationTransform.cs | 35 ++++++++++++++++++- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs index 52c706169..45ccaa296 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs @@ -22,6 +22,7 @@ using System.Linq; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.TypeSystem; +using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; @@ -100,16 +101,24 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms var firstArgument = invocationExpression.Arguments.First(); var target = firstArgument.GetResolveResult(); var args = invocationExpression.Arguments.Skip(1).Select(a => a.GetResolveResult()).ToArray(); - var rr = resolver.ResolveMemberAccess(target, method.Name, typeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult; - if (rr == null) - return; - var or = rr.PerformOverloadResolution(resolver.CurrentTypeResolveContext.Compilation, args, allowExtensionMethods: true); - if (or == null || or.IsAmbiguous || !method.Equals(or.GetBestCandidateWithSubstitutedTypeArguments())) + if (!CanTransformToExtensionMethodCall(resolver, method, typeArguments, target, args)) return; if (firstArgument is NullReferenceExpression) firstArgument = firstArgument.ReplaceWith(expr => new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.Parameters[0].Type), expr.Detach())); else mre.Target = firstArgument.Detach(); } + + public static bool CanTransformToExtensionMethodCall(CSharpResolver resolver, IMethod method, IReadOnlyList typeArguments, ResolveResult target, ResolveResult[] arguments) + { + var rr = resolver.ResolveMemberAccess(target, method.Name, typeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult; + if (rr == null) + return false; + // TODO : add support for argument names as soon as named arguments are implemented in the decompiler. + var or = rr.PerformOverloadResolution(resolver.CurrentTypeResolveContext.Compilation, arguments, allowExtensionMethods: true); + if (or == null || or.IsAmbiguous || !method.Equals(or.GetBestCandidateWithSubstitutedTypeArguments())) + return false; + return true; + } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs index 0756dd1ed..5494664ae 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs @@ -18,8 +18,12 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; +using System.Linq; using System.Text; +using ICSharpCode.Decompiler.CSharp.Syntax; +using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; @@ -193,7 +197,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (call.Arguments.Count == 0) { return false; } - if (call.Method.IsStatic && !call.Method.IsExtensionMethod) { + if (call.Method.IsStatic && (!call.Method.IsExtensionMethod || !CanTransformToExtensionMethodCall(call))) { return false; // only instance or extension methods can be called with ?. syntax } if (call.Method.IsAccessor && !IsGetter(call.Method)) { @@ -235,6 +239,35 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } + bool CanTransformToExtensionMethodCall(CallInstruction call) + { + if (call.Method.Parameters.Count == 0) return false; + var targetType = call.Method.Parameters.Select(p => new ResolveResult(p.Type)).First(); + var paramTypes = call.Method.Parameters.Skip(1).Select(p => new ResolveResult(p.Type)).ToArray(); + var paramNames = call.Method.Parameters.SelectArray(p => p.Name); + var typeArgs = call.Method.TypeArguments.ToArray(); + var usingScope = CreateUsingScope(context.RequiredNamespacesSuperset).Resolve(context.TypeSystem.Compilation); + var resolveContext = new CSharp.TypeSystem.CSharpTypeResolveContext(context.TypeSystem.Compilation.MainAssembly, usingScope); + var resolver = new CSharp.Resolver.CSharpResolver(resolveContext); + return CSharp.Transforms.IntroduceExtensionMethods.CanTransformToExtensionMethodCall(resolver, call.Method, typeArgs, targetType, paramTypes); + } + + CSharp.TypeSystem.UsingScope CreateUsingScope(IImmutableSet requiredNamespacesSuperset) + { + var usingScope = new CSharp.TypeSystem.UsingScope(); + foreach (var ns in requiredNamespacesSuperset) { + string[] parts = ns.Split('.'); + AstType nsType = new SimpleType(parts[0]); + for (int i = 1; i < parts.Length; i++) { + nsType = new MemberType { Target = nsType, MemberName = parts[i] }; + } + var reference = nsType.ToTypeReference(CSharp.Resolver.NameLookupMode.TypeInUsingDeclaration) as CSharp.TypeSystem.TypeOrNamespaceReference; + if (reference != null) + usingScope.Usings.Add(reference); + } + return usingScope; + } + static bool IsGetter(IMethod method) { return method.AccessorOwner is IProperty p && p.Getter == method;