Skip to content

Commit d2d9281

Browse files
Move CanTransformToExtensionMethodCall to CSharpResolver
1 parent e56f12c commit d2d9281

File tree

7 files changed

+64
-61
lines changed

7 files changed

+64
-61
lines changed

ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ sealed class ExpressionBuilder : ILVisitor<TranslationContext, TranslatedExpress
8383
internal readonly DecompilerSettings settings;
8484
readonly CancellationToken cancellationToken;
8585

86-
public ExpressionBuilder(StatementBuilder statementBuilder, IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, ILFunction currentFunction, DecompilerSettings settings, CancellationToken cancellationToken)
86+
public ExpressionBuilder(StatementBuilder statementBuilder, IDecompilerTypeSystem typeSystem,
87+
ITypeResolveContext decompilationContext, ILFunction currentFunction, DecompilerSettings settings,
88+
DecompileRun decompileRun, CancellationToken cancellationToken)
8789
{
8890
Debug.Assert(decompilationContext != null);
8991
this.statementBuilder = statementBuilder;
@@ -93,7 +95,12 @@ public ExpressionBuilder(StatementBuilder statementBuilder, IDecompilerTypeSyste
9395
this.settings = settings;
9496
this.cancellationToken = cancellationToken;
9597
this.compilation = decompilationContext.Compilation;
96-
this.resolver = new CSharpResolver(new CSharpTypeResolveContext(compilation.MainModule, null, decompilationContext.CurrentTypeDefinition, decompilationContext.CurrentMember));
98+
this.resolver = new CSharpResolver(new CSharpTypeResolveContext(
99+
compilation.MainModule,
100+
decompileRun.UsingScope.Resolve(compilation),
101+
decompilationContext.CurrentTypeDefinition,
102+
decompilationContext.CurrentMember
103+
));
97104
this.astBuilder = new TypeSystemAstBuilder(resolver);
98105
this.astBuilder.AlwaysUseShortTypeNames = true;
99106
this.astBuilder.AddResolveResultAnnotations = true;

ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2954,5 +2954,33 @@ public ResolveResult ResolveAssignment(AssignmentOperatorType op, ResolveResult
29542954
new[] { lhs, opResult.Operands[1] });
29552955
}
29562956
#endregion
2957+
2958+
#region CanTransformToExtensionMethodCall
2959+
public bool CanTransformToExtensionMethodCall(IMethod method,
2960+
IReadOnlyList<IType> typeArguments, ResolveResult target, ResolveResult[] arguments, string[] argumentNames)
2961+
{
2962+
if (target is LambdaResolveResult)
2963+
return false;
2964+
var rr = ResolveMemberAccess(target, method.Name, typeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult;
2965+
if (rr == null)
2966+
return false;
2967+
var or = rr.PerformOverloadResolution(CurrentTypeResolveContext.Compilation, arguments, argumentNames, allowExtensionMethods: true);
2968+
if (or == null || or.IsAmbiguous)
2969+
return false;
2970+
return method.Equals(or.GetBestCandidateWithSubstitutedTypeArguments())
2971+
&& CSharpResolver.IsEligibleExtensionMethod(target.Type, method, useTypeInference: false, out _);
2972+
}
2973+
2974+
public bool CanTransformToExtensionMethodCall(IMethod method, bool ignoreTypeArguments = false, bool ignoreArgumentNames = true)
2975+
{
2976+
if (method.Parameters.Count == 0)
2977+
return false;
2978+
var targetType = method.Parameters.Select(p => new ResolveResult(p.Type)).First();
2979+
var paramTypes = method.Parameters.Skip(1).Select(p => new ResolveResult(p.Type)).ToArray();
2980+
var paramNames = ignoreArgumentNames ? null : method.Parameters.SelectReadOnlyArray(p => p.Name);
2981+
var typeArgs = ignoreTypeArguments ? Empty<IType>.Array : method.TypeArguments.ToArray();
2982+
return CanTransformToExtensionMethodCall(method, typeArgs, targetType, paramTypes, argumentNames: paramNames);
2983+
}
2984+
#endregion
29572985
}
29582986
}

ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424

2525
using ICSharpCode.Decompiler.CSharp.Syntax;
2626
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
27-
using ICSharpCode.Decompiler.CSharp.Transforms;
28-
using ICSharpCode.Decompiler.CSharp.TypeSystem;
2927
using ICSharpCode.Decompiler.IL;
3028
using ICSharpCode.Decompiler.IL.Transforms;
3129
using ICSharpCode.Decompiler.Semantics;
@@ -60,6 +58,7 @@ public StatementBuilder(IDecompilerTypeSystem typeSystem, ITypeResolveContext de
6058
decompilationContext,
6159
currentFunction,
6260
settings,
61+
decompileRun,
6362
cancellationToken
6463
);
6564
this.currentFunction = currentFunction;
@@ -617,12 +616,8 @@ Statement TransformToForeach(UsingInstruction inst, Expression resource)
617616
if (!m.Success)
618617
return null;
619618
// Validate that the invocation is an extension method invocation.
620-
var context = new CSharpTypeResolveContext(
621-
typeSystem.MainModule,
622-
decompileRun.UsingScope.Resolve(typeSystem)
623-
);
624-
if (!IntroduceExtensionMethods.CanTransformToExtensionMethodCall(context,
625-
(InvocationExpression)resource))
619+
if (!(resource.GetSymbol() is IMethod method
620+
&& exprBuilder.resolver.CanTransformToExtensionMethodCall(method, true)))
626621
{
627622
return null;
628623
}

ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -205,41 +205,7 @@ static bool CanTransformToExtensionMethodCall(CSharpResolver resolver,
205205
}
206206
pos++;
207207
}
208-
return CanTransformToExtensionMethodCall(resolver, method, typeArguments, target, args, argNames);
209-
}
210-
211-
public static bool CanTransformToExtensionMethodCall(CSharpTypeResolveContext resolveContext,
212-
InvocationExpression invocationExpression)
213-
{
214-
return CanTransformToExtensionMethodCall(new CSharpResolver(resolveContext),
215-
invocationExpression, out _, out _, out _);
216-
}
217-
218-
public static bool CanTransformToExtensionMethodCall(CSharpResolver resolver, IMethod method,
219-
IReadOnlyList<IType> typeArguments, ResolveResult target, ResolveResult[] arguments, string[] argumentNames)
220-
{
221-
if (target is LambdaResolveResult)
222-
return false;
223-
var rr = resolver.ResolveMemberAccess(target, method.Name, typeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult;
224-
if (rr == null)
225-
return false;
226-
var or = rr.PerformOverloadResolution(resolver.CurrentTypeResolveContext.Compilation, arguments, argumentNames, allowExtensionMethods: true);
227-
if (or == null || or.IsAmbiguous)
228-
return false;
229-
return method.Equals(or.GetBestCandidateWithSubstitutedTypeArguments())
230-
&& CSharpResolver.IsEligibleExtensionMethod(target.Type, method, useTypeInference: false, out _);
231-
}
232-
233-
public static bool CanTransformToExtensionMethodCall(IMethod method, CSharpTypeResolveContext resolveContext, bool ignoreTypeArguments = false, bool ignoreArgumentNames = true)
234-
{
235-
if (method.Parameters.Count == 0)
236-
return false;
237-
var targetType = method.Parameters.Select(p => new ResolveResult(p.Type)).First();
238-
var paramTypes = method.Parameters.Skip(1).Select(p => new ResolveResult(p.Type)).ToArray();
239-
var paramNames = ignoreArgumentNames ? null : method.Parameters.SelectReadOnlyArray(p => p.Name);
240-
var typeArgs = ignoreTypeArguments ? Empty<IType>.Array : method.TypeArguments.ToArray();
241-
var resolver = new CSharpResolver(resolveContext);
242-
return CanTransformToExtensionMethodCall(resolver, method, typeArgs, targetType, paramTypes, argumentNames: paramNames);
208+
return resolver.CanTransformToExtensionMethodCall(method, typeArguments, target, args, argNames);
243209
}
244210
}
245211
}

ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222
using System.Diagnostics;
2323
using System.Threading;
2424

25+
using ICSharpCode.Decompiler.CSharp.Resolver;
2526
using ICSharpCode.Decompiler.CSharp.TypeSystem;
2627
using ICSharpCode.Decompiler.DebugInfo;
2728
using ICSharpCode.Decompiler.TypeSystem;
29+
using ICSharpCode.Decompiler.Util;
2830

2931
namespace ICSharpCode.Decompiler.IL.Transforms
3032
{
@@ -52,6 +54,17 @@ public class ILTransformContext
5254
internal DecompileRun? DecompileRun { get; set; }
5355
internal ResolvedUsingScope? UsingScope => DecompileRun?.UsingScope.Resolve(TypeSystem);
5456

57+
CSharpResolver? csharpResolver;
58+
59+
internal CSharpResolver CSharpResolver {
60+
get {
61+
var resolver = LazyInit.VolatileRead(ref csharpResolver);
62+
if (resolver != null)
63+
return resolver;
64+
return LazyInit.GetOrSet(ref csharpResolver, new CSharpResolver(new CSharpTypeResolveContext(TypeSystem.MainModule, UsingScope)));
65+
}
66+
}
67+
5568
public ILTransformContext(ILFunction function, IDecompilerTypeSystem typeSystem, IDebugInfoProvider? debugInfo, DecompilerSettings? settings = null)
5669
{
5770
this.Function = function ?? throw new ArgumentNullException(nameof(function));

ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -374,11 +374,7 @@ bool IsValidEndOfChain()
374374

375375
bool CanTransformToExtensionMethodCall(CallInstruction call, ILTransformContext context)
376376
{
377-
return CSharp.Transforms.IntroduceExtensionMethods.CanTransformToExtensionMethodCall(
378-
call.Method, new CSharp.TypeSystem.CSharpTypeResolveContext(
379-
context.TypeSystem.MainModule, context.UsingScope
380-
)
381-
);
377+
return context.CSharpResolver.CanTransformToExtensionMethodCall(call.Method);
382378
}
383379
}
384380

ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
using System.Linq;
2424

2525
using ICSharpCode.Decompiler.CSharp.Resolver;
26-
using ICSharpCode.Decompiler.CSharp.TypeSystem;
2726
using ICSharpCode.Decompiler.Semantics;
2827
using ICSharpCode.Decompiler.TypeSystem;
2928
using ICSharpCode.Decompiler.Util;
@@ -211,8 +210,7 @@ bool IsPartOfInitializer(InstructionCollection<ILInstruction> instructions, int
211210
possibleIndexVariables.Add(stloc.Variable, (stloc.ChildIndex, stloc.Value));
212211
return true;
213212
}
214-
var resolveContext = new CSharpTypeResolveContext(context.TypeSystem.MainModule, context.UsingScope);
215-
(var kind, var newPath, var values, var targetVariable) = AccessPathElement.GetAccessPath(instructions[pos], rootType, context.Settings, resolveContext, possibleIndexVariables);
213+
(var kind, var newPath, var values, var targetVariable) = AccessPathElement.GetAccessPath(instructions[pos], rootType, context.Settings, context.CSharpResolver, possibleIndexVariables);
216214
if (kind == AccessPathKind.Invalid || target != targetVariable)
217215
return false;
218216
// Treat last element separately:
@@ -302,7 +300,7 @@ public AccessPathElement(OpCode opCode, IMember member, ILInstruction[]? indices
302300

303301
public static (AccessPathKind Kind, List<AccessPathElement> Path, List<ILInstruction>? Values, ILVariable? Target) GetAccessPath(
304302
ILInstruction instruction, IType rootType, DecompilerSettings? settings = null,
305-
CSharpTypeResolveContext? resolveContext = null,
303+
CSharpResolver? resolver = null,
306304
Dictionary<ILVariable, (int Index, ILInstruction Value)>? possibleIndexVariables = null)
307305
{
308306
List<AccessPathElement> path = new List<AccessPathElement>();
@@ -319,7 +317,7 @@ public static (AccessPathKind Kind, List<AccessPathElement> Path, List<ILInstruc
319317
if (!(call is CallVirt || call is Call))
320318
goto default;
321319
method = call.Method;
322-
if (resolveContext != null && !IsMethodApplicable(method, call.Arguments, rootType, resolveContext, settings))
320+
if (resolver != null && !IsMethodApplicable(method, call.Arguments, rootType, resolver, settings))
323321
goto default;
324322
inst = call.Arguments[0];
325323
if (inst is LdObjIfRef ldObjIfRef)
@@ -329,7 +327,7 @@ public static (AccessPathKind Kind, List<AccessPathElement> Path, List<ILInstruc
329327
if (method.IsAccessor)
330328
{
331329
if (method.AccessorOwner is IProperty property &&
332-
!CanBeUsedInInitializer(property, resolveContext, kind))
330+
!CanBeUsedInInitializer(property, resolver, kind))
333331
{
334332
goto default;
335333
}
@@ -433,22 +431,22 @@ public static (AccessPathKind Kind, List<AccessPathElement> Path, List<ILInstruc
433431
return (kind, path, values, target);
434432
}
435433

436-
private static bool CanBeUsedInInitializer(IProperty property, CSharpTypeResolveContext? resolveContext, AccessPathKind kind)
434+
private static bool CanBeUsedInInitializer(IProperty property, ITypeResolveContext? resolveContext, AccessPathKind kind)
437435
{
438436
if (property.CanSet && (property.Accessibility == property.Setter.Accessibility || IsAccessorAccessible(property.Setter, resolveContext)))
439437
return true;
440438
return kind != AccessPathKind.Setter;
441439
}
442440

443-
private static bool IsAccessorAccessible(IMethod setter, CSharpTypeResolveContext? resolveContext)
441+
private static bool IsAccessorAccessible(IMethod setter, ITypeResolveContext? resolveContext)
444442
{
445443
if (resolveContext == null)
446444
return true;
447445
var lookup = new MemberLookup(resolveContext.CurrentTypeDefinition, resolveContext.CurrentModule);
448446
return lookup.IsAccessible(setter, allowProtectedAccess: setter.DeclaringTypeDefinition == resolveContext.CurrentTypeDefinition);
449447
}
450448

451-
static bool IsMethodApplicable(IMethod method, IReadOnlyList<ILInstruction> arguments, IType rootType, CSharpTypeResolveContext resolveContext, DecompilerSettings? settings)
449+
static bool IsMethodApplicable(IMethod method, IReadOnlyList<ILInstruction> arguments, IType rootType, CSharpResolver resolver, DecompilerSettings? settings)
452450
{
453451
if (method.IsStatic && !method.IsExtensionMethod)
454452
return false;
@@ -460,7 +458,7 @@ static bool IsMethodApplicable(IMethod method, IReadOnlyList<ILInstruction> argu
460458
{
461459
if (settings?.ExtensionMethodsInCollectionInitializers == false)
462460
return false;
463-
if (!CSharp.Transforms.IntroduceExtensionMethods.CanTransformToExtensionMethodCall(method, resolveContext, ignoreTypeArguments: true))
461+
if (!resolver.CanTransformToExtensionMethodCall(method, ignoreTypeArguments: true))
464462
return false;
465463
}
466464
var targetType = GetReturnTypeFromInstruction(arguments[0]) ?? rootType;
@@ -476,7 +474,7 @@ bool CanInferTypeArgumentsFromParameters(IMethod method)
476474
return true;
477475
// always use unspecialized member, otherwise type inference fails
478476
method = (IMethod)method.MemberDefinition;
479-
new TypeInference(resolveContext.Compilation)
477+
new TypeInference(resolver.Compilation)
480478
.InferTypeArguments(
481479
method.TypeParameters,
482480
// TODO : this is not entirely correct... we need argument type information to resolve Add methods properly

0 commit comments

Comments
 (0)