From d2d9281072fefe2a63dd2846d52c7000c34c54d3 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 2 Aug 2025 09:09:20 +0200 Subject: [PATCH] Move CanTransformToExtensionMethodCall to CSharpResolver --- .../CSharp/ExpressionBuilder.cs | 11 ++++-- .../CSharp/Resolver/CSharpResolver.cs | 28 +++++++++++++++ .../CSharp/StatementBuilder.cs | 11 ++---- .../Transforms/IntroduceExtensionMethods.cs | 36 +------------------ .../IL/Transforms/IILTransform.cs | 13 +++++++ .../IL/Transforms/NullPropagationTransform.cs | 6 +--- ...ransformCollectionAndObjectInitializers.cs | 20 +++++------ 7 files changed, 64 insertions(+), 61 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 4fc23581c..d55839037 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -83,7 +83,9 @@ namespace ICSharpCode.Decompiler.CSharp internal readonly DecompilerSettings settings; readonly CancellationToken cancellationToken; - public ExpressionBuilder(StatementBuilder statementBuilder, IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, ILFunction currentFunction, DecompilerSettings settings, CancellationToken cancellationToken) + public ExpressionBuilder(StatementBuilder statementBuilder, IDecompilerTypeSystem typeSystem, + ITypeResolveContext decompilationContext, ILFunction currentFunction, DecompilerSettings settings, + DecompileRun decompileRun, CancellationToken cancellationToken) { Debug.Assert(decompilationContext != null); this.statementBuilder = statementBuilder; @@ -93,7 +95,12 @@ namespace ICSharpCode.Decompiler.CSharp this.settings = settings; this.cancellationToken = cancellationToken; this.compilation = decompilationContext.Compilation; - this.resolver = new CSharpResolver(new CSharpTypeResolveContext(compilation.MainModule, null, decompilationContext.CurrentTypeDefinition, decompilationContext.CurrentMember)); + this.resolver = new CSharpResolver(new CSharpTypeResolveContext( + compilation.MainModule, + decompileRun.UsingScope.Resolve(compilation), + decompilationContext.CurrentTypeDefinition, + decompilationContext.CurrentMember + )); this.astBuilder = new TypeSystemAstBuilder(resolver); this.astBuilder.AlwaysUseShortTypeNames = true; this.astBuilder.AddResolveResultAnnotations = true; diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs index 3a5f1252b..9e691fb40 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs @@ -2954,5 +2954,33 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver new[] { lhs, opResult.Operands[1] }); } #endregion + + #region CanTransformToExtensionMethodCall + public bool CanTransformToExtensionMethodCall(IMethod method, + IReadOnlyList typeArguments, ResolveResult target, ResolveResult[] arguments, string[] argumentNames) + { + if (target is LambdaResolveResult) + return false; + var rr = ResolveMemberAccess(target, method.Name, typeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult; + if (rr == null) + return false; + var or = rr.PerformOverloadResolution(CurrentTypeResolveContext.Compilation, arguments, argumentNames, allowExtensionMethods: true); + if (or == null || or.IsAmbiguous) + return false; + return method.Equals(or.GetBestCandidateWithSubstitutedTypeArguments()) + && CSharpResolver.IsEligibleExtensionMethod(target.Type, method, useTypeInference: false, out _); + } + + public bool CanTransformToExtensionMethodCall(IMethod method, bool ignoreTypeArguments = false, bool ignoreArgumentNames = true) + { + if (method.Parameters.Count == 0) + return false; + var targetType = method.Parameters.Select(p => new ResolveResult(p.Type)).First(); + var paramTypes = method.Parameters.Skip(1).Select(p => new ResolveResult(p.Type)).ToArray(); + var paramNames = ignoreArgumentNames ? null : method.Parameters.SelectReadOnlyArray(p => p.Name); + var typeArgs = ignoreTypeArguments ? Empty.Array : method.TypeArguments.ToArray(); + return CanTransformToExtensionMethodCall(method, typeArgs, targetType, paramTypes, argumentNames: paramNames); + } + #endregion } } diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index d3f2f5069..3e8dba558 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -24,8 +24,6 @@ using System.Threading; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; -using ICSharpCode.Decompiler.CSharp.Transforms; -using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.Semantics; @@ -60,6 +58,7 @@ namespace ICSharpCode.Decompiler.CSharp decompilationContext, currentFunction, settings, + decompileRun, cancellationToken ); this.currentFunction = currentFunction; @@ -617,12 +616,8 @@ namespace ICSharpCode.Decompiler.CSharp if (!m.Success) return null; // Validate that the invocation is an extension method invocation. - var context = new CSharpTypeResolveContext( - typeSystem.MainModule, - decompileRun.UsingScope.Resolve(typeSystem) - ); - if (!IntroduceExtensionMethods.CanTransformToExtensionMethodCall(context, - (InvocationExpression)resource)) + if (!(resource.GetSymbol() is IMethod method + && exprBuilder.resolver.CanTransformToExtensionMethodCall(method, true))) { return null; } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs index 754044666..4aa48b9f9 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs @@ -205,41 +205,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } pos++; } - return CanTransformToExtensionMethodCall(resolver, method, typeArguments, target, args, argNames); - } - - public static bool CanTransformToExtensionMethodCall(CSharpTypeResolveContext resolveContext, - InvocationExpression invocationExpression) - { - return CanTransformToExtensionMethodCall(new CSharpResolver(resolveContext), - invocationExpression, out _, out _, out _); - } - - public static bool CanTransformToExtensionMethodCall(CSharpResolver resolver, IMethod method, - IReadOnlyList typeArguments, ResolveResult target, ResolveResult[] arguments, string[] argumentNames) - { - if (target is LambdaResolveResult) - return false; - var rr = resolver.ResolveMemberAccess(target, method.Name, typeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult; - if (rr == null) - return false; - var or = rr.PerformOverloadResolution(resolver.CurrentTypeResolveContext.Compilation, arguments, argumentNames, allowExtensionMethods: true); - if (or == null || or.IsAmbiguous) - return false; - return method.Equals(or.GetBestCandidateWithSubstitutedTypeArguments()) - && CSharpResolver.IsEligibleExtensionMethod(target.Type, method, useTypeInference: false, out _); - } - - public static bool CanTransformToExtensionMethodCall(IMethod method, CSharpTypeResolveContext resolveContext, bool ignoreTypeArguments = false, bool ignoreArgumentNames = true) - { - if (method.Parameters.Count == 0) - return false; - var targetType = method.Parameters.Select(p => new ResolveResult(p.Type)).First(); - var paramTypes = method.Parameters.Skip(1).Select(p => new ResolveResult(p.Type)).ToArray(); - var paramNames = ignoreArgumentNames ? null : method.Parameters.SelectReadOnlyArray(p => p.Name); - var typeArgs = ignoreTypeArguments ? Empty.Array : method.TypeArguments.ToArray(); - var resolver = new CSharpResolver(resolveContext); - return CanTransformToExtensionMethodCall(resolver, method, typeArgs, targetType, paramTypes, argumentNames: paramNames); + return resolver.CanTransformToExtensionMethodCall(method, typeArguments, target, args, argNames); } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs index 347e8e35c..39453f375 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs @@ -22,9 +22,11 @@ using System; using System.Diagnostics; using System.Threading; +using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { @@ -52,6 +54,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms internal DecompileRun? DecompileRun { get; set; } internal ResolvedUsingScope? UsingScope => DecompileRun?.UsingScope.Resolve(TypeSystem); + CSharpResolver? csharpResolver; + + internal CSharpResolver CSharpResolver { + get { + var resolver = LazyInit.VolatileRead(ref csharpResolver); + if (resolver != null) + return resolver; + return LazyInit.GetOrSet(ref csharpResolver, new CSharpResolver(new CSharpTypeResolveContext(TypeSystem.MainModule, UsingScope))); + } + } + public ILTransformContext(ILFunction function, IDecompilerTypeSystem typeSystem, IDebugInfoProvider? debugInfo, DecompilerSettings? settings = null) { this.Function = function ?? throw new ArgumentNullException(nameof(function)); diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs index 7a4b18deb..4e5e5b5a2 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs @@ -374,11 +374,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms bool CanTransformToExtensionMethodCall(CallInstruction call, ILTransformContext context) { - return CSharp.Transforms.IntroduceExtensionMethods.CanTransformToExtensionMethodCall( - call.Method, new CSharp.TypeSystem.CSharpTypeResolveContext( - context.TypeSystem.MainModule, context.UsingScope - ) - ); + return context.CSharpResolver.CanTransformToExtensionMethodCall(call.Method); } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs index 908138ca9..51d559f34 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs @@ -23,7 +23,6 @@ using System.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.CSharp.Resolver; -using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; @@ -211,8 +210,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms possibleIndexVariables.Add(stloc.Variable, (stloc.ChildIndex, stloc.Value)); return true; } - var resolveContext = new CSharpTypeResolveContext(context.TypeSystem.MainModule, context.UsingScope); - (var kind, var newPath, var values, var targetVariable) = AccessPathElement.GetAccessPath(instructions[pos], rootType, context.Settings, resolveContext, possibleIndexVariables); + (var kind, var newPath, var values, var targetVariable) = AccessPathElement.GetAccessPath(instructions[pos], rootType, context.Settings, context.CSharpResolver, possibleIndexVariables); if (kind == AccessPathKind.Invalid || target != targetVariable) return false; // Treat last element separately: @@ -302,7 +300,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms public static (AccessPathKind Kind, List Path, List? Values, ILVariable? Target) GetAccessPath( ILInstruction instruction, IType rootType, DecompilerSettings? settings = null, - CSharpTypeResolveContext? resolveContext = null, + CSharpResolver? resolver = null, Dictionary? possibleIndexVariables = null) { List path = new List(); @@ -319,7 +317,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!(call is CallVirt || call is Call)) goto default; method = call.Method; - if (resolveContext != null && !IsMethodApplicable(method, call.Arguments, rootType, resolveContext, settings)) + if (resolver != null && !IsMethodApplicable(method, call.Arguments, rootType, resolver, settings)) goto default; inst = call.Arguments[0]; if (inst is LdObjIfRef ldObjIfRef) @@ -329,7 +327,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (method.IsAccessor) { if (method.AccessorOwner is IProperty property && - !CanBeUsedInInitializer(property, resolveContext, kind)) + !CanBeUsedInInitializer(property, resolver, kind)) { goto default; } @@ -433,14 +431,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms return (kind, path, values, target); } - private static bool CanBeUsedInInitializer(IProperty property, CSharpTypeResolveContext? resolveContext, AccessPathKind kind) + private static bool CanBeUsedInInitializer(IProperty property, ITypeResolveContext? resolveContext, AccessPathKind kind) { if (property.CanSet && (property.Accessibility == property.Setter.Accessibility || IsAccessorAccessible(property.Setter, resolveContext))) return true; return kind != AccessPathKind.Setter; } - private static bool IsAccessorAccessible(IMethod setter, CSharpTypeResolveContext? resolveContext) + private static bool IsAccessorAccessible(IMethod setter, ITypeResolveContext? resolveContext) { if (resolveContext == null) return true; @@ -448,7 +446,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return lookup.IsAccessible(setter, allowProtectedAccess: setter.DeclaringTypeDefinition == resolveContext.CurrentTypeDefinition); } - static bool IsMethodApplicable(IMethod method, IReadOnlyList arguments, IType rootType, CSharpTypeResolveContext resolveContext, DecompilerSettings? settings) + static bool IsMethodApplicable(IMethod method, IReadOnlyList arguments, IType rootType, CSharpResolver resolver, DecompilerSettings? settings) { if (method.IsStatic && !method.IsExtensionMethod) return false; @@ -460,7 +458,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { if (settings?.ExtensionMethodsInCollectionInitializers == false) return false; - if (!CSharp.Transforms.IntroduceExtensionMethods.CanTransformToExtensionMethodCall(method, resolveContext, ignoreTypeArguments: true)) + if (!resolver.CanTransformToExtensionMethodCall(method, ignoreTypeArguments: true)) return false; } var targetType = GetReturnTypeFromInstruction(arguments[0]) ?? rootType; @@ -476,7 +474,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; // always use unspecialized member, otherwise type inference fails method = (IMethod)method.MemberDefinition; - new TypeInference(resolveContext.Compilation) + new TypeInference(resolver.Compilation) .InferTypeArguments( method.TypeParameters, // TODO : this is not entirely correct... we need argument type information to resolve Add methods properly