Browse Source

#907: CallBuilder: If type arguments cannot be inferred from the parameter list, add them before asking overload resolution, but remove them again, if it does not reduce the number of casts.

pull/1213/head
Siegfried Pammer 8 years ago
parent
commit
6096b7df29
  1. 136
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs
  2. 62
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  3. 2
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  4. 14
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs
  5. 4
      ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs

136
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs

@ -474,53 +474,53 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void ArrayIndexer() public static void ArrayIndexer()
{ {
Test((Func<int[], int>)((int[] array) => array[0]), (Expression<Func<int[], int>>)((int[] array) => array[0])); Test<Func<int[], int>>((int[] array) => array[0], (int[] array) => array[0]);
Test((Func<int[], int, int>)((int[] array, int index) => array[index]), (Expression<Func<int[], int, int>>)((int[] array, int index) => array[index])); Test<Func<int[], int, int>>((int[] array, int index) => array[index], (int[] array, int index) => array[index]);
Test((Func<int[,], int>)((int[,] array) => array[0, 5]), (Expression<Func<int[,], int>>)((int[,] array) => array[0, 5])); Test<Func<int[,], int>>((int[,] array) => array[0, 5], (int[,] array) => array[0, 5]);
Test((Func<int[,], int, int>)((int[,] array, int index) => array[index, 7]), (Expression<Func<int[,], int, int>>)((int[,] array, int index) => array[index, 7])); Test<Func<int[,], int, int>>((int[,] array, int index) => array[index, 7], (int[,] array, int index) => array[index, 7]);
Test((Func<int[][], int, int>)((int[][] array, int index) => array[index][7]), (Expression<Func<int[][], int, int>>)((int[][] array, int index) => array[index][7])); Test<Func<int[][], int, int>>((int[][] array, int index) => array[index][7], (int[][] array, int index) => array[index][7]);
} }
public static void ArrayLength() public static void ArrayLength()
{ {
Test((Func<int[], int>)((int[] array) => array.Length), (Expression<Func<int[], int>>)((int[] array) => array.Length)); Test<Func<int[], int>>((int[] array) => array.Length, (int[] array) => array.Length);
Test((Func<int>)(() => ((Array)null).Length), (Expression<Func<int>>)(() => ((Array)null).Length)); Test<Func<int>>(() => ((Array)null).Length, () => ((Array)null).Length);
} }
public static void NewObj() public static void NewObj()
{ {
Test((Func<object>)(() => new SimpleType()), (Expression<Func<object>>)(() => new SimpleType())); Test<Func<object>>(() => new SimpleType(), () => new SimpleType());
Test((Func<object>)(() => new SimpleTypeWithCtor(5)), (Expression<Func<object>>)(() => new SimpleTypeWithCtor(5))); Test<Func<object>>(() => new SimpleTypeWithCtor(5), () => new SimpleTypeWithCtor(5));
Test((Func<object>)(() => new SimpleTypeWithMultipleCtors()), (Expression<Func<object>>)(() => new SimpleTypeWithMultipleCtors())); Test<Func<object>>(() => new SimpleTypeWithMultipleCtors(), () => new SimpleTypeWithMultipleCtors());
Test((Func<object>)(() => new SimpleTypeWithMultipleCtors(5)), (Expression<Func<object>>)(() => new SimpleTypeWithMultipleCtors(5))); Test<Func<object>>(() => new SimpleTypeWithMultipleCtors(5), () => new SimpleTypeWithMultipleCtors(5));
Test((Func<object>)(() => new GenericClass<int>()), (Expression<Func<object>>)(() => new GenericClass<int>())); Test<Func<object>>(() => new GenericClass<int>(), () => new GenericClass<int>());
Test((Func<object>)(() => new GenericClassWithCtor<int>()), (Expression<Func<object>>)(() => new GenericClassWithCtor<int>())); Test<Func<object>>(() => new GenericClassWithCtor<int>(), () => new GenericClassWithCtor<int>());
Test((Func<object>)(() => new GenericClassWithMultipleCtors<int>(5)), (Expression<Func<object>>)(() => new GenericClassWithMultipleCtors<int>(5))); Test<Func<object>>(() => new GenericClassWithMultipleCtors<int>(5), () => new GenericClassWithMultipleCtors<int>(5));
} }
public unsafe static void TypeOfExpr() public unsafe static void TypeOfExpr()
{ {
Test((Func<Type>)(() => typeof(int)), (Expression<Func<Type>>)(() => typeof(int))); Test<Func<Type>>(() => typeof(int), () => typeof(int));
Test((Func<Type>)(() => typeof(object)), (Expression<Func<Type>>)(() => typeof(object))); Test<Func<Type>>(() => typeof(object), () => typeof(object));
Test((Func<Type>)(() => typeof(List<>)), (Expression<Func<Type>>)(() => typeof(List<>))); Test<Func<Type>>(() => typeof(List<>), () => typeof(List<>));
Test((Func<Type>)(() => typeof(List<int>)), (Expression<Func<Type>>)(() => typeof(List<int>))); Test<Func<Type>>(() => typeof(List<int>), () => typeof(List<int>));
Test((Func<Type>)(() => typeof(int*)), (Expression<Func<Type>>)(() => typeof(int*))); Test<Func<Type>>(() => typeof(int*), () => typeof(int*));
} }
public static void AsTypeExpr() public static void AsTypeExpr()
{ {
Test((Func<object, MyClass>)((object obj) => obj as MyClass), (Expression<Func<object, MyClass>>)((object obj) => obj as MyClass)); Test<Func<object, MyClass>>((object obj) => obj as MyClass, (object obj) => obj as MyClass);
Test((Func<object, GenericClass<object>>)((object obj) => obj as GenericClass<object>), (Expression<Func<object, GenericClass<object>>>)((object obj) => obj as GenericClass<object>)); Test<Func<object, GenericClass<object>>>((object obj) => obj as GenericClass<object>, (object obj) => obj as GenericClass<object>);
} }
public static void IsTypeExpr() public static void IsTypeExpr()
{ {
Test((Func<object, bool>)((object obj) => obj is MyClass), (Expression<Func<object, bool>>)((object obj) => obj is MyClass)); Test<Func<object, bool>>((object obj) => obj is MyClass, (object obj) => obj is MyClass);
} }
public static void UnaryLogicalOperators() public static void UnaryLogicalOperators()
{ {
Test((Func<bool, bool>)((bool a) => !a), (Expression<Func<bool, bool>>)((bool a) => !a)); Test<Func<bool, bool>>((bool a) => !a, (bool a) => !a);
} }
public static void ConditionalOperator() public static void ConditionalOperator()
@ -552,55 +552,55 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void UnaryArithmeticOperators() public static void UnaryArithmeticOperators()
{ {
Test((Func<int, int>)((int a) => a), (Expression<Func<int, int>>)((int a) => a)); Test<Func<int, int>>((int a) => a, (int a) => a);
Test((Func<int, int>)((int a) => -a), (Expression<Func<int, int>>)((int a) => -a)); Test<Func<int, int>>((int a) => -a, (int a) => -a);
} }
public static void BinaryArithmeticOperators() public static void BinaryArithmeticOperators()
{ {
Test((Func<int, int, int>)((int a, int b) => a + b), (Expression<Func<int, int, int>>)((int a, int b) => a + b)); Test<Func<int, int, int>>((int a, int b) => a + b, (int a, int b) => a + b);
Test((Func<int, int, int>)((int a, int b) => a - b), (Expression<Func<int, int, int>>)((int a, int b) => a - b)); Test<Func<int, int, int>>((int a, int b) => a - b, (int a, int b) => a - b);
Test((Func<int, int, int>)((int a, int b) => a * b), (Expression<Func<int, int, int>>)((int a, int b) => a * b)); Test<Func<int, int, int>>((int a, int b) => a * b, (int a, int b) => a * b);
Test((Func<int, int, int>)((int a, int b) => a / b), (Expression<Func<int, int, int>>)((int a, int b) => a / b)); Test<Func<int, int, int>>((int a, int b) => a / b, (int a, int b) => a / b);
Test((Func<int, int, int>)((int a, int b) => a % b), (Expression<Func<int, int, int>>)((int a, int b) => a % b)); Test<Func<int, int, int>>((int a, int b) => a % b, (int a, int b) => a % b);
Test((Func<long, int, long>)((long a, int b) => a + b), (Expression<Func<long, int, long>>)((long a, int b) => a + (long)b)); Test<Func<long, int, long>>((long a, int b) => a + b, (long a, int b) => a + (long)b);
Test((Func<long, int, long>)((long a, int b) => a - b), (Expression<Func<long, int, long>>)((long a, int b) => a - (long)b)); Test<Func<long, int, long>>((long a, int b) => a - b, (long a, int b) => a - (long)b);
Test((Func<long, int, long>)((long a, int b) => a * b), (Expression<Func<long, int, long>>)((long a, int b) => a * (long)b)); Test<Func<long, int, long>>((long a, int b) => a * b, (long a, int b) => a * (long)b);
Test((Func<long, int, long>)((long a, int b) => a / b), (Expression<Func<long, int, long>>)((long a, int b) => a / (long)b)); Test<Func<long, int, long>>((long a, int b) => a / b, (long a, int b) => a / (long)b);
Test((Func<long, int, long>)((long a, int b) => a % b), (Expression<Func<long, int, long>>)((long a, int b) => a % (long)b)); Test<Func<long, int, long>>((long a, int b) => a % b, (long a, int b) => a % (long)b);
Test((Func<short, int, int>)((short a, int b) => a + b), (Expression<Func<short, int, int>>)((short a, int b) => a + b)); Test<Func<short, int, int>>((short a, int b) => a + b, (short a, int b) => a + b);
Test((Func<int, short, int>)((int a, short b) => a - b), (Expression<Func<int, short, int>>)((int a, short b) => a - b)); Test<Func<int, short, int>>((int a, short b) => a - b, (int a, short b) => a - b);
Test((Func<short, int, int>)((short a, int b) => a * b), (Expression<Func<short, int, int>>)((short a, int b) => a * b)); Test<Func<short, int, int>>((short a, int b) => a * b, (short a, int b) => a * b);
Test((Func<int, short, int>)((int a, short b) => a / b), (Expression<Func<int, short, int>>)((int a, short b) => a / b)); Test<Func<int, short, int>>((int a, short b) => a / b, (int a, short b) => a / b);
Test((Func<short, int, int>)((short a, int b) => a % b), (Expression<Func<short, int, int>>)((short a, int b) => a % b)); Test<Func<short, int, int>>((short a, int b) => a % b, (short a, int b) => a % b);
} }
public static void BitOperators() public static void BitOperators()
{ {
Test((Func<int, int>)((int a) => ~a), (Expression<Func<int, int>>)((int a) => ~a)); Test<Func<int, int>>((int a) => ~a, (int a) => ~a);
Test((Func<int, int, int>)((int a, int b) => a & b), (Expression<Func<int, int, int>>)((int a, int b) => a & b)); Test<Func<int, int, int>>((int a, int b) => a & b, (int a, int b) => a & b);
Test((Func<int, int, int>)((int a, int b) => a | b), (Expression<Func<int, int, int>>)((int a, int b) => a | b)); Test<Func<int, int, int>>((int a, int b) => a | b, (int a, int b) => a | b);
Test((Func<int, int, int>)((int a, int b) => a ^ b), (Expression<Func<int, int, int>>)((int a, int b) => a ^ b)); Test<Func<int, int, int>>((int a, int b) => a ^ b, (int a, int b) => a ^ b);
} }
public static void ShiftOperators() public static void ShiftOperators()
{ {
Test((Func<int, int>)((int a) => a >> 2), (Expression<Func<int, int>>)((int a) => a >> 2)); Test<Func<int, int>>((int a) => a >> 2, (int a) => a >> 2);
Test((Func<int, int>)((int a) => a << 2), (Expression<Func<int, int>>)((int a) => a << 2)); Test<Func<int, int>>((int a) => a << 2, (int a) => a << 2);
Test((Func<long, long>)((long a) => a >> 2), (Expression<Func<long, long>>)((long a) => a >> 2)); Test<Func<long, long>>((long a) => a >> 2, (long a) => a >> 2);
Test((Func<long, long>)((long a) => a << 2), (Expression<Func<long, long>>)((long a) => a << 2)); Test<Func<long, long>>((long a) => a << 2, (long a) => a << 2);
} }
public static void SimpleExpressions() public static void SimpleExpressions()
{ {
Test((Func<int>)(() => 0), (Expression<Func<int>>)(() => 0)); Test<Func<int>>(() => 0, () => 0);
Test((Func<int, int>)((int a) => a), (Expression<Func<int, int>>)((int a) => a)); Test<Func<int, int>>((int a) => a, (int a) => a);
} }
public static void Capturing() public static void Capturing()
{ {
int captured = 5; int captured = 5;
Test((Func<int>)(() => captured), (Expression<Func<int>>)(() => captured)); Test<Func<int>>(() => captured, () => captured);
} }
public static void FieldAndPropertyAccess() public static void FieldAndPropertyAccess()
@ -619,55 +619,55 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void Call() public static void Call()
{ {
ToCode(null, (string a) => Console.WriteLine(a)); ToCode(null, (string a) => Console.WriteLine(a));
Test((Func<string, string>)((string a) => a.ToString()), (Expression<Func<string, string>>)((string a) => a.ToString())); Test<Func<string, string>>((string a) => a.ToString(), (string a) => a.ToString());
Test((Func<int, string>)((int a) => a.ToString()), (Expression<Func<int, string>>)((int a) => a.ToString())); Test<Func<int, string>>((int a) => a.ToString(), (int a) => a.ToString());
Test((Func<string, char[]>)((string a) => a.ToArray()), (Expression<Func<string, char[]>>)((string a) => a.ToArray())); Test<Func<string, char[]>>((string a) => a.ToArray(), (string a) => a.ToArray());
Test((Func<bool>)(() => 'a'.CompareTo('b') < 0), (Expression<Func<bool>>)(() => 'a'.CompareTo('b') < 0)); Test<Func<bool>>(() => 'a'.CompareTo('b') < 0, () => 'a'.CompareTo('b') < 0);
} }
public static void Quote() public static void Quote()
{ {
Test((Func<bool>)(() => (Expression<Func<int, string, string>>)((int n, string s) => s + n.ToString()) != null), (Expression<Func<bool>>)(() => (Expression<Func<int, string, string>>)((int n, string s) => s + n.ToString()) != null)); Test<Func<bool>>(() => (Expression<Func<int, string, string>>)((int n, string s) => s + n.ToString()) != null, () => (Expression<Func<int, string, string>>)((int n, string s) => s + n.ToString()) != null);
} }
public static void ArrayInitializer() public static void ArrayInitializer()
{ {
Test((Func<int[]>)(() => new int[3] { Test<Func<int[]>>(() => new int[3] {
1, 1,
2, 2,
3 3
}), (Expression<Func<int[]>>)(() => new int[3] { }, () => new int[3] {
1, 1,
2, 2,
3 3
})); });
Test((Func<int[]>)(() => new int[3]), (Expression<Func<int[]>>)(() => new int[3])); Test<Func<int[]>>(() => new int[3], () => new int[3]);
Test((Func<int[,]>)(() => new int[3, 5]), (Expression<Func<int[,]>>)(() => new int[3, 5])); Test<Func<int[,]>>(() => new int[3, 5], () => new int[3, 5]);
Test((Func<int[][]>)(() => new int[3][]), (Expression<Func<int[][]>>)(() => new int[3][])); Test<Func<int[][]>>(() => new int[3][], () => new int[3][]);
Test((Func<int[][]>)(() => new int[1][] { Test<Func<int[][]>>(() => new int[1][] {
new int[3] { new int[3] {
1, 1,
2, 2,
3 3
} }
}), (Expression<Func<int[][]>>)(() => new int[1][] { }, () => new int[1][] {
new int[3] { new int[3] {
1, 1,
2, 2,
3 3
} }
})); });
} }
public static void AnonymousTypes() public static void AnonymousTypes()
{ {
Test((Func<object>)(() => new { Test<Func<object>>(() => new {
A = 5, A = 5,
B = "Test" B = "Test"
}), (Expression<Func<object>>)(() => new { }, () => new {
A = 5, A = 5,
B = "Test" B = "Test"
})); });
} }
public static void ObjectInit() public static void ObjectInit()

62
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -383,8 +383,10 @@ namespace ICSharpCode.Decompiler.CSharp
List<IParameter> expectedParameters, CallTransformation allowedTransforms, out IParameterizedMember foundMethod) List<IParameter> expectedParameters, CallTransformation allowedTransforms, out IParameterizedMember foundMethod)
{ {
CallTransformation transform = CallTransformation.None; CallTransformation transform = CallTransformation.None;
// initialize requireTarget flag
bool requireTarget; bool requireTarget;
bool requireTypeArguments = false; ResolveResult targetResolveResult;
if ((allowedTransforms & CallTransformation.RequireTarget) != 0) { if ((allowedTransforms & CallTransformation.RequireTarget) != 0) {
if (expressionBuilder.HidesVariableWithName(method.Name)) { if (expressionBuilder.HidesVariableWithName(method.Name)) {
requireTarget = true; requireTarget = true;
@ -398,13 +400,41 @@ namespace ICSharpCode.Decompiler.CSharp
else else
requireTarget = !(target.Expression is ThisReferenceExpression); requireTarget = !(target.Expression is ThisReferenceExpression);
} }
targetResolveResult = requireTarget ? target.ResolveResult : null;
} else { } else {
// HACK: this is a special case for collection initializer calls, they do not allow a target to be
// emitted, but we still need it for overload resolution.
requireTarget = true; requireTarget = true;
targetResolveResult = target.ResolveResult;
}
// initialize requireTypeArguments flag
bool requireTypeArguments;
IType[] typeArguments;
bool appliedRequireTypeArgumentsShortcut = false;
if (method.TypeParameters.Count > 0 && (allowedTransforms & CallTransformation.RequireTypeArguments) != 0
&& !IsPossibleExtensionMethodCallOnNull(method, arguments)) {
// The ambiguity resolution below only adds type arguments as last resort measure, however there are
// methods, such as Enumerable.OfType<TResult>(IEnumerable input) that always require type arguments,
// as those cannot be inferred from the parameters, which leads to bloated expressions full of extra casts
// that are no longer required once we add the type arguments.
// We lend overload resolution a hand by detecting such cases beforehand and requiring type arguments,
// if necessary.
if (!CanInferTypeArgumentsFromParameters(method, arguments.SelectArray(a => a.ResolveResult), expressionBuilder.typeInference)) {
requireTypeArguments = true;
typeArguments = method.TypeArguments.ToArray();
appliedRequireTypeArgumentsShortcut = true;
} else {
requireTypeArguments = false;
typeArguments = Empty<IType>.Array;
}
} else {
requireTypeArguments = false;
typeArguments = Empty<IType>.Array;
} }
bool targetCasted = false; bool targetCasted = false;
bool argumentsCasted = false; bool argumentsCasted = false;
IType[] typeArguments = Empty<IType>.Array;
var targetResolveResult = requireTarget ? target.ResolveResult : null;
OverloadResolutionErrors errors; OverloadResolutionErrors errors;
while ((errors = IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, typeArguments, arguments, argumentNames, out foundMethod)) != OverloadResolutionErrors.None) { while ((errors = IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, typeArguments, arguments, argumentNames, out foundMethod)) != OverloadResolutionErrors.None) {
switch (errors) { switch (errors) {
@ -423,6 +453,13 @@ namespace ICSharpCode.Decompiler.CSharp
// TODO : implement some more intelligent algorithm that decides which of these fixes (cast args, add target, cast target, add type args) // TODO : implement some more intelligent algorithm that decides which of these fixes (cast args, add target, cast target, add type args)
// is best in this case. Additionally we should not cast all arguments at once, but step-by-step try to add only a minimal number of casts. // is best in this case. Additionally we should not cast all arguments at once, but step-by-step try to add only a minimal number of casts.
if (!argumentsCasted) { if (!argumentsCasted) {
// If we added type arguments beforehand, but that didn't make the code any better,
// undo that decision and add casts first.
if (appliedRequireTypeArgumentsShortcut) {
requireTypeArguments = false;
typeArguments = Empty<IType>.Array;
appliedRequireTypeArgumentsShortcut = false;
}
argumentsCasted = true; argumentsCasted = true;
CastArguments(arguments, expectedParameters); CastArguments(arguments, expectedParameters);
} else if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && !requireTarget) { } else if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && !requireTarget) {
@ -451,6 +488,23 @@ namespace ICSharpCode.Decompiler.CSharp
return transform; return transform;
} }
private bool IsPossibleExtensionMethodCallOnNull(IMethod method, List<TranslatedExpression> arguments)
{
return method.IsExtensionMethod && arguments.Count > 0 && arguments[0].Expression is NullReferenceExpression;
}
public static bool CanInferTypeArgumentsFromParameters(IMethod method, IReadOnlyList<ResolveResult> arguments,
TypeInference typeInference)
{
if (method.TypeParameters.Count == 0)
return true;
// always use unspecialized member, otherwise type inference fails
method = (IMethod)method.MemberDefinition;
typeInference.InferTypeArguments(method.TypeParameters, arguments, method.Parameters.SelectArray(p => p.Type),
out bool success);
return success;
}
private void CastArguments(IList<TranslatedExpression> arguments, IReadOnlyList<IParameter> expectedParameters) private void CastArguments(IList<TranslatedExpression> arguments, IReadOnlyList<IParameter> expectedParameters)
{ {
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
@ -574,7 +628,7 @@ namespace ICSharpCode.Decompiler.CSharp
return OverloadResolutionErrors.AmbiguousMatch; return OverloadResolutionErrors.AmbiguousMatch;
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray()); or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
} else { } else {
var result = lookup.Lookup(target, method.Name, EmptyList<IType>.Instance, isInvocation: true) as MethodGroupResolveResult; var result = lookup.Lookup(target, method.Name, typeArguments, isInvocation: true) as MethodGroupResolveResult;
if (result == null) if (result == null)
return OverloadResolutionErrors.AmbiguousMatch; return OverloadResolutionErrors.AmbiguousMatch;
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray()); or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());

2
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.CSharp
internal readonly ICompilation compilation; internal readonly ICompilation compilation;
internal readonly CSharpResolver resolver; internal readonly CSharpResolver resolver;
readonly TypeSystemAstBuilder astBuilder; readonly TypeSystemAstBuilder astBuilder;
readonly TypeInference typeInference; internal readonly TypeInference typeInference;
internal readonly DecompilerSettings settings; internal readonly DecompilerSettings settings;
readonly CancellationToken cancellationToken; readonly CancellationToken cancellationToken;

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

@ -18,6 +18,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax;
@ -136,8 +137,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} }
if (!CanTransformToExtensionMethodCall(resolver, method, typeArguments, target, args, argNames)) if (!CanTransformToExtensionMethodCall(resolver, method, typeArguments, target, args, argNames))
return; return;
if (firstArgument is NullReferenceExpression) if (firstArgument is NullReferenceExpression) {
Debug.Assert(context.RequiredNamespacesSuperset.Contains(method.Parameters[0].Type.Namespace));
firstArgument = firstArgument.ReplaceWith(expr => new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.Parameters[0].Type), expr.Detach())); firstArgument = firstArgument.ReplaceWith(expr => new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.Parameters[0].Type), expr.Detach()));
}
if (invocationExpression.Target is IdentifierExpression identifierExpression) { if (invocationExpression.Target is IdentifierExpression identifierExpression) {
identifierExpression.Detach(); identifierExpression.Detach();
memberRefExpr = new MemberReferenceExpression(firstArgument.Detach(), method.Name, identifierExpression.TypeArguments.Detach()); memberRefExpr = new MemberReferenceExpression(firstArgument.Detach(), method.Name, identifierExpression.TypeArguments.Detach());
@ -169,14 +172,5 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
var resolver = new CSharpResolver(resolveContext); var resolver = new CSharpResolver(resolveContext);
return CanTransformToExtensionMethodCall(resolver, method, typeArgs, targetType, paramTypes, argumentNames: paramNames); return CanTransformToExtensionMethodCall(resolver, method, typeArgs, targetType, paramTypes, argumentNames: paramNames);
} }
public static bool CanInferTypeArgumentsFromParameters(IMethod method, IReadOnlyList<ResolveResult> arguments, ITypeResolveContext resolveContext)
{
if (method.TypeParameters.Count == 0)
return true;
var inference = new TypeInference(resolveContext.Compilation);
inference.InferTypeArguments(method.TypeParameters, arguments, method.Parameters.SelectArray(p => p.Type), out bool success);
return success;
}
} }
} }

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

@ -350,7 +350,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!targetType.GetAllBaseTypes().Any(i => i.IsKnownType(KnownTypeCode.IEnumerable) || i.IsKnownType(KnownTypeCode.IEnumerableOfT))) if (!targetType.GetAllBaseTypes().Any(i => i.IsKnownType(KnownTypeCode.IEnumerable) || i.IsKnownType(KnownTypeCode.IEnumerableOfT)))
return false; return false;
return CSharp.Transforms.IntroduceExtensionMethods.CanInferTypeArgumentsFromParameters(method, method.Parameters.SelectArray(p => new ResolveResult(p.Type)), resolveContext); return CSharp.CallBuilder.CanInferTypeArgumentsFromParameters(
method, method.Parameters.SelectArray(p => new ResolveResult(p.Type)),
new TypeInference(resolveContext.Compilation));
} }
static IType GetReturnTypeFromInstruction(ILInstruction instruction) static IType GetReturnTypeFromInstruction(ILInstruction instruction)

Loading…
Cancel
Save