Browse Source

Move CanTransformToExtensionMethodCall to CSharpResolver

pull/3532/head
Siegfried Pammer 5 months ago
parent
commit
d2d9281072
  1. 11
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 28
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs
  3. 11
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  4. 36
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs
  5. 13
      ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs
  6. 6
      ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs
  7. 20
      ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs

11
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -83,7 +83,9 @@ namespace ICSharpCode.Decompiler.CSharp
internal readonly DecompilerSettings settings; internal readonly DecompilerSettings settings;
readonly CancellationToken cancellationToken; 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); Debug.Assert(decompilationContext != null);
this.statementBuilder = statementBuilder; this.statementBuilder = statementBuilder;
@ -93,7 +95,12 @@ namespace ICSharpCode.Decompiler.CSharp
this.settings = settings; this.settings = settings;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.compilation = decompilationContext.Compilation; 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 = new TypeSystemAstBuilder(resolver);
this.astBuilder.AlwaysUseShortTypeNames = true; this.astBuilder.AlwaysUseShortTypeNames = true;
this.astBuilder.AddResolveResultAnnotations = true; this.astBuilder.AddResolveResultAnnotations = true;

28
ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs

@ -2954,5 +2954,33 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
new[] { lhs, opResult.Operands[1] }); new[] { lhs, opResult.Operands[1] });
} }
#endregion #endregion
#region CanTransformToExtensionMethodCall
public bool CanTransformToExtensionMethodCall(IMethod method,
IReadOnlyList<IType> 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<IType>.Array : method.TypeArguments.ToArray();
return CanTransformToExtensionMethodCall(method, typeArgs, targetType, paramTypes, argumentNames: paramNames);
}
#endregion
} }
} }

11
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -24,8 +24,6 @@ using System.Threading;
using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
using ICSharpCode.Decompiler.CSharp.Transforms;
using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Semantics;
@ -60,6 +58,7 @@ namespace ICSharpCode.Decompiler.CSharp
decompilationContext, decompilationContext,
currentFunction, currentFunction,
settings, settings,
decompileRun,
cancellationToken cancellationToken
); );
this.currentFunction = currentFunction; this.currentFunction = currentFunction;
@ -617,12 +616,8 @@ namespace ICSharpCode.Decompiler.CSharp
if (!m.Success) if (!m.Success)
return null; return null;
// Validate that the invocation is an extension method invocation. // Validate that the invocation is an extension method invocation.
var context = new CSharpTypeResolveContext( if (!(resource.GetSymbol() is IMethod method
typeSystem.MainModule, && exprBuilder.resolver.CanTransformToExtensionMethodCall(method, true)))
decompileRun.UsingScope.Resolve(typeSystem)
);
if (!IntroduceExtensionMethods.CanTransformToExtensionMethodCall(context,
(InvocationExpression)resource))
{ {
return null; return null;
} }

36
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs

@ -205,41 +205,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} }
pos++; pos++;
} }
return CanTransformToExtensionMethodCall(resolver, method, typeArguments, target, args, argNames); return resolver.CanTransformToExtensionMethodCall(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<IType> 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<IType>.Array : method.TypeArguments.ToArray();
var resolver = new CSharpResolver(resolveContext);
return CanTransformToExtensionMethodCall(resolver, method, typeArgs, targetType, paramTypes, argumentNames: paramNames);
} }
} }
} }

13
ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs

@ -22,9 +22,11 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.TypeSystem; using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL.Transforms namespace ICSharpCode.Decompiler.IL.Transforms
{ {
@ -52,6 +54,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
internal DecompileRun? DecompileRun { get; set; } internal DecompileRun? DecompileRun { get; set; }
internal ResolvedUsingScope? UsingScope => DecompileRun?.UsingScope.Resolve(TypeSystem); 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) public ILTransformContext(ILFunction function, IDecompilerTypeSystem typeSystem, IDebugInfoProvider? debugInfo, DecompilerSettings? settings = null)
{ {
this.Function = function ?? throw new ArgumentNullException(nameof(function)); this.Function = function ?? throw new ArgumentNullException(nameof(function));

6
ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs

@ -374,11 +374,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
bool CanTransformToExtensionMethodCall(CallInstruction call, ILTransformContext context) bool CanTransformToExtensionMethodCall(CallInstruction call, ILTransformContext context)
{ {
return CSharp.Transforms.IntroduceExtensionMethods.CanTransformToExtensionMethodCall( return context.CSharpResolver.CanTransformToExtensionMethodCall(call.Method);
call.Method, new CSharp.TypeSystem.CSharpTypeResolveContext(
context.TypeSystem.MainModule, context.UsingScope
)
);
} }
} }

20
ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs

@ -23,7 +23,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
@ -211,8 +210,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
possibleIndexVariables.Add(stloc.Variable, (stloc.ChildIndex, stloc.Value)); possibleIndexVariables.Add(stloc.Variable, (stloc.ChildIndex, stloc.Value));
return true; 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, context.CSharpResolver, possibleIndexVariables);
(var kind, var newPath, var values, var targetVariable) = AccessPathElement.GetAccessPath(instructions[pos], rootType, context.Settings, resolveContext, possibleIndexVariables);
if (kind == AccessPathKind.Invalid || target != targetVariable) if (kind == AccessPathKind.Invalid || target != targetVariable)
return false; return false;
// Treat last element separately: // Treat last element separately:
@ -302,7 +300,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public static (AccessPathKind Kind, List<AccessPathElement> Path, List<ILInstruction>? Values, ILVariable? Target) GetAccessPath( public static (AccessPathKind Kind, List<AccessPathElement> Path, List<ILInstruction>? Values, ILVariable? Target) GetAccessPath(
ILInstruction instruction, IType rootType, DecompilerSettings? settings = null, ILInstruction instruction, IType rootType, DecompilerSettings? settings = null,
CSharpTypeResolveContext? resolveContext = null, CSharpResolver? resolver = null,
Dictionary<ILVariable, (int Index, ILInstruction Value)>? possibleIndexVariables = null) Dictionary<ILVariable, (int Index, ILInstruction Value)>? possibleIndexVariables = null)
{ {
List<AccessPathElement> path = new List<AccessPathElement>(); List<AccessPathElement> path = new List<AccessPathElement>();
@ -319,7 +317,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!(call is CallVirt || call is Call)) if (!(call is CallVirt || call is Call))
goto default; goto default;
method = call.Method; 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; goto default;
inst = call.Arguments[0]; inst = call.Arguments[0];
if (inst is LdObjIfRef ldObjIfRef) if (inst is LdObjIfRef ldObjIfRef)
@ -329,7 +327,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (method.IsAccessor) if (method.IsAccessor)
{ {
if (method.AccessorOwner is IProperty property && if (method.AccessorOwner is IProperty property &&
!CanBeUsedInInitializer(property, resolveContext, kind)) !CanBeUsedInInitializer(property, resolver, kind))
{ {
goto default; goto default;
} }
@ -433,14 +431,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (kind, path, values, target); 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))) if (property.CanSet && (property.Accessibility == property.Setter.Accessibility || IsAccessorAccessible(property.Setter, resolveContext)))
return true; return true;
return kind != AccessPathKind.Setter; return kind != AccessPathKind.Setter;
} }
private static bool IsAccessorAccessible(IMethod setter, CSharpTypeResolveContext? resolveContext) private static bool IsAccessorAccessible(IMethod setter, ITypeResolveContext? resolveContext)
{ {
if (resolveContext == null) if (resolveContext == null)
return true; return true;
@ -448,7 +446,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return lookup.IsAccessible(setter, allowProtectedAccess: setter.DeclaringTypeDefinition == resolveContext.CurrentTypeDefinition); return lookup.IsAccessible(setter, allowProtectedAccess: setter.DeclaringTypeDefinition == resolveContext.CurrentTypeDefinition);
} }
static bool IsMethodApplicable(IMethod method, IReadOnlyList<ILInstruction> arguments, IType rootType, CSharpTypeResolveContext resolveContext, DecompilerSettings? settings) static bool IsMethodApplicable(IMethod method, IReadOnlyList<ILInstruction> arguments, IType rootType, CSharpResolver resolver, DecompilerSettings? settings)
{ {
if (method.IsStatic && !method.IsExtensionMethod) if (method.IsStatic && !method.IsExtensionMethod)
return false; return false;
@ -460,7 +458,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
if (settings?.ExtensionMethodsInCollectionInitializers == false) if (settings?.ExtensionMethodsInCollectionInitializers == false)
return false; return false;
if (!CSharp.Transforms.IntroduceExtensionMethods.CanTransformToExtensionMethodCall(method, resolveContext, ignoreTypeArguments: true)) if (!resolver.CanTransformToExtensionMethodCall(method, ignoreTypeArguments: true))
return false; return false;
} }
var targetType = GetReturnTypeFromInstruction(arguments[0]) ?? rootType; var targetType = GetReturnTypeFromInstruction(arguments[0]) ?? rootType;
@ -476,7 +474,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
// always use unspecialized member, otherwise type inference fails // always use unspecialized member, otherwise type inference fails
method = (IMethod)method.MemberDefinition; method = (IMethod)method.MemberDefinition;
new TypeInference(resolveContext.Compilation) new TypeInference(resolver.Compilation)
.InferTypeArguments( .InferTypeArguments(
method.TypeParameters, method.TypeParameters,
// TODO : this is not entirely correct... we need argument type information to resolve Add methods properly // TODO : this is not entirely correct... we need argument type information to resolve Add methods properly

Loading…
Cancel
Save