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 7 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 @@ -474,53 +474,53 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
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>)((int[] array, int index) => array[index]), (Expression<Func<int[], int, int>>)((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>)((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]), (Expression<Func<int[][], int, int>>)((int[][] array, int index) => array[index][7]));
Test<Func<int[], int>>((int[] array) => array[0], (int[] array) => array[0]);
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], (int[,] array) => array[0, 5]);
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], (int[][] array, int index) => array[index][7]);
}
public static void ArrayLength()
{
Test((Func<int[], int>)((int[] array) => array.Length), (Expression<Func<int[], int>>)((int[] array) => array.Length));
Test((Func<int>)(() => ((Array)null).Length), (Expression<Func<int>>)(() => ((Array)null).Length));
Test<Func<int[], int>>((int[] array) => array.Length, (int[] array) => array.Length);
Test<Func<int>>(() => ((Array)null).Length, () => ((Array)null).Length);
}
public static void NewObj()
{
Test((Func<object>)(() => new SimpleType()), (Expression<Func<object>>)(() => new SimpleType()));
Test((Func<object>)(() => new SimpleTypeWithCtor(5)), (Expression<Func<object>>)(() => new SimpleTypeWithCtor(5)));
Test((Func<object>)(() => new SimpleTypeWithMultipleCtors()), (Expression<Func<object>>)(() => new SimpleTypeWithMultipleCtors()));
Test((Func<object>)(() => new SimpleTypeWithMultipleCtors(5)), (Expression<Func<object>>)(() => new SimpleTypeWithMultipleCtors(5)));
Test((Func<object>)(() => new GenericClass<int>()), (Expression<Func<object>>)(() => new GenericClass<int>()));
Test((Func<object>)(() => new GenericClassWithCtor<int>()), (Expression<Func<object>>)(() => new GenericClassWithCtor<int>()));
Test((Func<object>)(() => new GenericClassWithMultipleCtors<int>(5)), (Expression<Func<object>>)(() => new GenericClassWithMultipleCtors<int>(5)));
Test<Func<object>>(() => new SimpleType(), () => new SimpleType());
Test<Func<object>>(() => new SimpleTypeWithCtor(5), () => new SimpleTypeWithCtor(5));
Test<Func<object>>(() => new SimpleTypeWithMultipleCtors(), () => new SimpleTypeWithMultipleCtors());
Test<Func<object>>(() => new SimpleTypeWithMultipleCtors(5), () => new SimpleTypeWithMultipleCtors(5));
Test<Func<object>>(() => new GenericClass<int>(), () => new GenericClass<int>());
Test<Func<object>>(() => new GenericClassWithCtor<int>(), () => new GenericClassWithCtor<int>());
Test<Func<object>>(() => new GenericClassWithMultipleCtors<int>(5), () => new GenericClassWithMultipleCtors<int>(5));
}
public unsafe static void TypeOfExpr()
{
Test((Func<Type>)(() => typeof(int)), (Expression<Func<Type>>)(() => typeof(int)));
Test((Func<Type>)(() => typeof(object)), (Expression<Func<Type>>)(() => typeof(object)));
Test((Func<Type>)(() => typeof(List<>)), (Expression<Func<Type>>)(() => typeof(List<>)));
Test((Func<Type>)(() => typeof(List<int>)), (Expression<Func<Type>>)(() => typeof(List<int>)));
Test((Func<Type>)(() => typeof(int*)), (Expression<Func<Type>>)(() => typeof(int*)));
Test<Func<Type>>(() => typeof(int), () => typeof(int));
Test<Func<Type>>(() => typeof(object), () => typeof(object));
Test<Func<Type>>(() => typeof(List<>), () => typeof(List<>));
Test<Func<Type>>(() => typeof(List<int>), () => typeof(List<int>));
Test<Func<Type>>(() => typeof(int*), () => typeof(int*));
}
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, GenericClass<object>>)((object obj) => obj as GenericClass<object>), (Expression<Func<object, GenericClass<object>>>)((object obj) => obj as GenericClass<object>));
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>, (object obj) => obj as GenericClass<object>);
}
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()
{
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()
@ -552,55 +552,55 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -552,55 +552,55 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
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), (Expression<Func<int, int>>)((int a) => -a));
Test<Func<int, int>>((int a) => a, (int a) => a);
Test<Func<int, int>>((int a) => -a, (int a) => -a);
}
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), (Expression<Func<int, int, int>>)((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), (Expression<Func<int, int, int>>)((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<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), (Expression<Func<long, int, long>>)((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), (Expression<Func<long, int, long>>)((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<short, int, int>)((short a, int b) => a + b), (Expression<Func<short, int, int>>)((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<short, int, int>)((short a, int b) => a * b), (Expression<Func<short, int, int>>)((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<short, int, int>)((short a, int b) => a % b), (Expression<Func<short, int, int>>)((short 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, (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, (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, (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, (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, (long a, int b) => a % (long)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, (int a, short 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, (int a, short b) => a / b);
Test<Func<short, int, int>>((short a, int b) => a % b, (short a, int b) => a % b);
}
public static void BitOperators()
{
Test((Func<int, int>)((int a) => ~a), (Expression<Func<int, int>>)((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), (Expression<Func<int, int, int>>)((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 a) => ~a, (int a) => ~a);
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, (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()
{
Test((Func<int, int>)((int a) => a >> 2), (Expression<Func<int, int>>)((int a) => a >> 2));
Test((Func<int, int>)((int a) => a << 2), (Expression<Func<int, int>>)((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), (Expression<Func<long, long>>)((long a) => a << 2));
Test<Func<int, int>>((int a) => a >> 2, (int a) => a >> 2);
Test<Func<int, int>>((int a) => a << 2, (int a) => a << 2);
Test<Func<long, long>>((long a) => a >> 2, (long a) => a >> 2);
Test<Func<long, long>>((long a) => a << 2, (long a) => a << 2);
}
public static void SimpleExpressions()
{
Test((Func<int>)(() => 0), (Expression<Func<int>>)(() => 0));
Test((Func<int, int>)((int a) => a), (Expression<Func<int, int>>)((int a) => a));
Test<Func<int>>(() => 0, () => 0);
Test<Func<int, int>>((int a) => a, (int a) => a);
}
public static void Capturing()
{
int captured = 5;
Test((Func<int>)(() => captured), (Expression<Func<int>>)(() => captured));
Test<Func<int>>(() => captured, () => captured);
}
public static void FieldAndPropertyAccess()
@ -619,55 +619,55 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -619,55 +619,55 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void Call()
{
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<int, string>)((int a) => a.ToString()), (Expression<Func<int, string>>)((int a) => a.ToString()));
Test((Func<string, char[]>)((string a) => a.ToArray()), (Expression<Func<string, char[]>>)((string a) => a.ToArray()));
Test((Func<bool>)(() => 'a'.CompareTo('b') < 0), (Expression<Func<bool>>)(() => 'a'.CompareTo('b') < 0));
Test<Func<string, string>>((string a) => a.ToString(), (string a) => a.ToString());
Test<Func<int, string>>((int a) => a.ToString(), (int a) => a.ToString());
Test<Func<string, char[]>>((string a) => a.ToArray(), (string a) => a.ToArray());
Test<Func<bool>>(() => 'a'.CompareTo('b') < 0, () => 'a'.CompareTo('b') < 0);
}
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()
{
Test((Func<int[]>)(() => new int[3] {
Test<Func<int[]>>(() => new int[3] {
1,
2,
3
}), (Expression<Func<int[]>>)(() => new int[3] {
}, () => new int[3] {
1,
2,
3
}));
Test((Func<int[]>)(() => new int[3]), (Expression<Func<int[]>>)(() => new int[3]));
Test((Func<int[,]>)(() => new int[3, 5]), (Expression<Func<int[,]>>)(() => new int[3, 5]));
Test((Func<int[][]>)(() => new int[3][]), (Expression<Func<int[][]>>)(() => new int[3][]));
Test((Func<int[][]>)(() => new int[1][] {
});
Test<Func<int[]>>(() => new int[3], () => new int[3]);
Test<Func<int[,]>>(() => new int[3, 5], () => new int[3, 5]);
Test<Func<int[][]>>(() => new int[3][], () => new int[3][]);
Test<Func<int[][]>>(() => new int[1][] {
new int[3] {
1,
2,
3
}
}), (Expression<Func<int[][]>>)(() => new int[1][] {
}, () => new int[1][] {
new int[3] {
1,
2,
3
}
}));
});
}
public static void AnonymousTypes()
{
Test((Func<object>)(() => new {
Test<Func<object>>(() => new {
A = 5,
B = "Test"
}), (Expression<Func<object>>)(() => new {
}, () => new {
A = 5,
B = "Test"
}));
});
}
public static void ObjectInit()

62
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -383,8 +383,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -383,8 +383,10 @@ namespace ICSharpCode.Decompiler.CSharp
List<IParameter> expectedParameters, CallTransformation allowedTransforms, out IParameterizedMember foundMethod)
{
CallTransformation transform = CallTransformation.None;
// initialize requireTarget flag
bool requireTarget;
bool requireTypeArguments = false;
ResolveResult targetResolveResult;
if ((allowedTransforms & CallTransformation.RequireTarget) != 0) {
if (expressionBuilder.HidesVariableWithName(method.Name)) {
requireTarget = true;
@ -398,13 +400,41 @@ namespace ICSharpCode.Decompiler.CSharp @@ -398,13 +400,41 @@ namespace ICSharpCode.Decompiler.CSharp
else
requireTarget = !(target.Expression is ThisReferenceExpression);
}
targetResolveResult = requireTarget ? target.ResolveResult : null;
} 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;
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 argumentsCasted = false;
IType[] typeArguments = Empty<IType>.Array;
var targetResolveResult = requireTarget ? target.ResolveResult : null;
OverloadResolutionErrors errors;
while ((errors = IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, typeArguments, arguments, argumentNames, out foundMethod)) != OverloadResolutionErrors.None) {
switch (errors) {
@ -423,6 +453,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -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)
// 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 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;
CastArguments(arguments, expectedParameters);
} else if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && !requireTarget) {
@ -451,6 +488,23 @@ namespace ICSharpCode.Decompiler.CSharp @@ -451,6 +488,23 @@ namespace ICSharpCode.Decompiler.CSharp
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)
{
for (int i = 0; i < arguments.Count; i++) {
@ -574,7 +628,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -574,7 +628,7 @@ namespace ICSharpCode.Decompiler.CSharp
return OverloadResolutionErrors.AmbiguousMatch;
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
} 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)
return OverloadResolutionErrors.AmbiguousMatch;
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());

2
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

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

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

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.Syntax;
@ -136,8 +137,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -136,8 +137,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
if (!CanTransformToExtensionMethodCall(resolver, method, typeArguments, target, args, argNames))
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()));
}
if (invocationExpression.Target is IdentifierExpression identifierExpression) {
identifierExpression.Detach();
memberRefExpr = new MemberReferenceExpression(firstArgument.Detach(), method.Name, identifierExpression.TypeArguments.Detach());
@ -169,14 +172,5 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -169,14 +172,5 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
var resolver = new CSharpResolver(resolveContext);
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 @@ -350,7 +350,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (!targetType.GetAllBaseTypes().Any(i => i.IsKnownType(KnownTypeCode.IEnumerable) || i.IsKnownType(KnownTypeCode.IEnumerableOfT)))
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)

Loading…
Cancel
Save