From e51e3a95cb0dc87653c4158e1ee134edf0b48a1f Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 8 Aug 2011 12:31:45 +0200 Subject: [PATCH] Add InvocationResolveResult. --- .../CSharp/Resolver/AttributeTests.cs | 14 ++-- .../CSharp/Resolver/ExtensionMethodTests.cs | 32 +++---- .../CSharp/Resolver/InvocationTests.cs | 24 +++--- .../CSharp/Resolver/NameLookupTests.cs | 8 +- .../CSharp/Resolver/ObjectCreationTests.cs | 37 ++++----- .../CSharp/Resolver/ResolverTestBase.cs | 3 +- .../CSharp/Resolver/TypeInferenceTests.cs | 12 ++- .../CSharp/Resolver/AmbiguousResolveResult.cs | 2 +- .../CSharp/Resolver/CSharpResolver.cs | 21 +++-- .../Resolver/InvocationResolveResult.cs | 83 +++++++++++++++++++ .../CSharp/Resolver/MemberLookup.cs | 9 +- .../CSharp/Resolver/MemberResolveResult.cs | 10 ++- .../Resolver/MethodGroupResolveResult.cs | 22 +++-- .../CSharp/Resolver/OverloadResolution.cs | 43 +++++++++- .../CSharp/Resolver/ResolveVisitor.cs | 12 +-- .../ICSharpCode.NRefactory.csproj | 1 + 16 files changed, 236 insertions(+), 97 deletions(-) create mode 100644 ICSharpCode.NRefactory/CSharp/Resolver/InvocationResolveResult.cs diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/AttributeTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/AttributeTests.cs index 0451e45c17..cefdb6482f 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/AttributeTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/AttributeTests.cs @@ -40,18 +40,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public void AttributeConstructor1() { string program = "using System; [$LoaderOptimization(3)$] class Test { }"; - var mrr = Resolve(program); + var mrr = Resolve(program); Assert.AreEqual("System.LoaderOptimizationAttribute..ctor", mrr.Member.FullName); - Assert.AreEqual("System.Byte", (mrr.Member as IMethod).Parameters[0].Type.Resolve(context).FullName); + Assert.AreEqual("System.Byte", mrr.Member.Parameters[0].Type.Resolve(context).FullName); } [Test] public void AttributeConstructor2() { string program = "using System; [$LoaderOptimization(LoaderOptimization.NotSpecified)$] class Test { }"; - var mrr = Resolve(program); + var mrr = Resolve(program); Assert.AreEqual("System.LoaderOptimizationAttribute..ctor", mrr.Member.FullName); - Assert.AreEqual("System.LoaderOptimization", (mrr.Member as IMethod).Parameters[0].Type.Resolve(context).FullName); + Assert.AreEqual("System.LoaderOptimization", mrr.Member.Parameters[0].Type.Resolve(context).FullName); } [Test] @@ -59,7 +59,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { string program = "using System; [$Obsolete$] class Test {}"; - MemberResolveResult result = Resolve(program); + var result = Resolve(program); Assert.AreEqual("System.ObsoleteAttribute..ctor", result.Member.FullName); } @@ -71,7 +71,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public const AttributeTargets XXX = AttributeTargets.All; } "; - MemberResolveResult result = Resolve(program); + var result = Resolve(program); Assert.AreEqual("MyAttribute.XXX", result.Member.FullName); } @@ -83,7 +83,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver enum E { A, B } } "; - MemberResolveResult result = Resolve(program); + var result = Resolve(program); Assert.AreEqual("MyNamespace.E.A", result.Member.FullName); } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs index 3a9f0a7396..91d2e6bd5e 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ExtensionMethodTests.cs @@ -33,31 +33,31 @@ namespace XN { } } "; - MemberResolveResult mrr; + InvocationResolveResult mrr; - mrr = Resolve(program.Replace("$", "$a.F(1)$")); + mrr = Resolve(program.Replace("$", "$a.F(1)$")); Assert.AreEqual("XN.XC.F", mrr.Member.FullName); - Assert.AreEqual("System.Int32", ((IMethod)mrr.Member).Parameters[1].Type.Resolve(context).FullName); + Assert.AreEqual("System.Int32", mrr.Member.Parameters[1].Type.Resolve(context).FullName); - mrr = Resolve(program.Replace("$", "$a.F(\"text\")$")); + mrr = Resolve(program.Replace("$", "$a.F(\"text\")$")); Assert.AreEqual("XN.XC.F", mrr.Member.FullName); - Assert.AreEqual("System.String", ((IMethod)mrr.Member).Parameters[1].Type.Resolve(context).FullName); + Assert.AreEqual("System.String", mrr.Member.Parameters[1].Type.Resolve(context).FullName); - mrr = Resolve(program.Replace("$", "$b.F(1)$")); + mrr = Resolve(program.Replace("$", "$b.F(1)$")); Assert.AreEqual("B.F", mrr.Member.FullName); - mrr = Resolve(program.Replace("$", "$b.F(\"text\")$")); + mrr = Resolve(program.Replace("$", "$b.F(\"text\")$")); Assert.AreEqual("XN.XC.F", mrr.Member.FullName); - Assert.AreEqual("System.String", ((IMethod)mrr.Member).Parameters[1].Type.Resolve(context).FullName); + Assert.AreEqual("System.String", mrr.Member.Parameters[1].Type.Resolve(context).FullName); - mrr = Resolve(program.Replace("$", "$c.F(1)$")); + mrr = Resolve(program.Replace("$", "$c.F(1)$")); Assert.AreEqual("C.F", mrr.Member.FullName); - mrr = Resolve(program.Replace("$", "$c.F(\"text\")$")); + mrr = Resolve(program.Replace("$", "$c.F(\"text\")$")); Assert.AreEqual("C.F", mrr.Member.FullName); } - [Test, Ignore("Anonymous methods not yet implemented")] + [Test] public void ExtensionMethodsTest2() { string program = @"using System; using System.Collections.Generic; @@ -72,18 +72,18 @@ public static class XC { public static IEnumerable Filter(this IEnumerable source, Predicate predicate) { throw new NotImplementedException(); } } "; - MemberResolveResult mrr; + InvocationResolveResult mrr; - mrr = Resolve(program.Replace("$", "$\"text\".ToInt32()$")); + mrr = Resolve(program.Replace("$", "$\"text\".ToInt32()$")); Assert.AreEqual("XC.ToInt32", mrr.Member.FullName); - mrr = Resolve(program.Replace("$", "$args.Slice(1, 2)$")); + mrr = Resolve(program.Replace("$", "$args.Slice(1, 2)$")); Assert.AreEqual("XC.Slice", mrr.Member.FullName); Assert.AreEqual("System.String[]", mrr.Type.ReflectionName); - mrr = Resolve(program.Replace("$", "$args.Filter(delegate { return true; })$")); + mrr = Resolve(program.Replace("$", "$args.Filter(delegate { return true; })$")); Assert.AreEqual("XC.Filter", mrr.Member.FullName); - Assert.AreEqual("System.Collections.Generic.IEnumerable{System.String}", mrr.Type.ReflectionName); + Assert.AreEqual("System.Collections.Generic.IEnumerable`1[[System.String]]", mrr.Type.ReflectionName); } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/InvocationTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/InvocationTests.cs index b59b2941e9..731f6af222 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/InvocationTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/InvocationTests.cs @@ -25,7 +25,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } "; - MemberResolveResult result = Resolve(program); + InvocationResolveResult result = Resolve(program); Assert.AreEqual("A.TargetMethod", result.Member.FullName); Assert.AreEqual("System.Int32", result.Type.ReflectionName); } @@ -66,7 +66,7 @@ class B : A { } } "; - MemberResolveResult result = Resolve(program); + InvocationResolveResult result = Resolve(program); Assert.AreEqual("B.GetRandomNumber", result.Member.FullName); } @@ -87,7 +87,7 @@ class B : A { } } "; - MemberResolveResult result = Resolve(program); + InvocationResolveResult result = Resolve(program); Assert.AreEqual("B.GetRandomNumber", result.Member.FullName); } @@ -104,7 +104,7 @@ class B : A { } } "; - MemberResolveResult result = Resolve(program); + InvocationResolveResult result = Resolve(program); Assert.AreEqual("A.TargetMethod", result.Member.FullName); Assert.AreEqual("System.Int32", result.Type.ReflectionName); } @@ -202,11 +202,11 @@ class Program { static void T(ref int y) {} }"; - MemberResolveResult mrr = Resolve(program, "T(a)"); - Assert.IsFalse(((IMethod)mrr.Member).Parameters[0].IsRef); + InvocationResolveResult mrr = Resolve(program, "T(a)"); + Assert.IsFalse(mrr.Member.Parameters[0].IsRef); - mrr = Resolve(program, "T(ref a)"); - Assert.IsTrue(((IMethod)mrr.Member).Parameters[0].IsRef); + mrr = Resolve(program, "T(ref a)"); + Assert.IsTrue(mrr.Member.Parameters[0].IsRef); } [Test, Ignore("Grouping by declaring type not yet implemented")] @@ -221,7 +221,7 @@ class Program { class DerivedClass : BaseClass { public void Test(object a) { } }"; - MemberResolveResult mrr = Resolve(program); + InvocationResolveResult mrr = Resolve(program); Assert.AreEqual("DerivedClass.Test", mrr.Member.FullName); } @@ -237,10 +237,10 @@ class DerivedClass : BaseClass { class DerivedClass : BaseClass { public void Test(string a) { } }"; - MemberResolveResult mrr = Resolve(program); + InvocationResolveResult mrr = Resolve(program); Assert.AreEqual("BaseClass.Test", mrr.Member.FullName); - mrr = Resolve(program.Replace("(3)", "(\"3\")")); + mrr = Resolve(program.Replace("(3)", "(\"3\")")); Assert.AreEqual("DerivedClass.Test", mrr.Member.FullName); } @@ -261,7 +261,7 @@ class DerivedClass : MiddleClass { public override void Test(int a) { } }"; - MemberResolveResult mrr = Resolve(program); + InvocationResolveResult mrr = Resolve(program); Assert.AreEqual("MiddleClass.Test", mrr.Member.FullName); } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/NameLookupTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/NameLookupTests.cs index 2b6cff43c1..4ef62c07d4 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/NameLookupTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/NameLookupTests.cs @@ -408,7 +408,7 @@ class TestClass { "; TypeResolveResult trr = Resolve(program.Replace("COL a", "$COL$ a")); Assert.AreEqual("System.Collections.ArrayList", trr.Type.FullName, "COL"); - ResolveResult rr = Resolve(program.Replace("new COL()", "$new COL()$")); + ResolveResult rr = Resolve(program.Replace("new COL()", "$new COL()$")); Assert.AreEqual("System.Collections.ArrayList", rr.Type.FullName, "a"); } @@ -539,7 +539,7 @@ class TestClass { trr = Resolve(program.Replace("$", "$global::XX.XX$")); Assert.AreEqual("XX.XX", trr.Type.FullName); - MemberResolveResult mrr = Resolve(program.Replace("$", "$XX.Test()$")); + InvocationResolveResult mrr = Resolve(program.Replace("$", "$XX.Test()$")); Assert.AreEqual("XX.XX.Test", mrr.Member.FullName); } @@ -651,7 +651,7 @@ namespace A { class BaseClass { public static string Test() {} }"; - MemberResolveResult mrr = Resolve(program.Replace("$", "$BaseClass.Test()$")); + MemberResolveResult mrr = Resolve(program.Replace("$", "$BaseClass.Test()$")); Assert.AreEqual("BaseClass.Test", mrr.Member.FullName); mrr = Resolve(program.Replace("$", "$Test$")); @@ -661,7 +661,7 @@ namespace A { Assert.AreEqual("DerivedClass.Test", mrr.Member.FullName); // returns BaseClass.Test because DerivedClass.Test is not invocable - mrr = Resolve(program.Replace("$", "$DerivedClass.Test()$")); + mrr = Resolve(program.Replace("$", "$DerivedClass.Test()$")); Assert.AreEqual("BaseClass.Test", mrr.Member.FullName); } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ObjectCreationTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ObjectCreationTests.cs index 6a8bcbadda..e3be24818c 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ObjectCreationTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ObjectCreationTests.cs @@ -20,7 +20,7 @@ class A { } } "; - MemberResolveResult result = Resolve(program); + InvocationResolveResult result = Resolve(program); Assert.AreEqual("System.Collections.Generic.List..ctor", result.Member.FullName); Assert.AreEqual("System.Collections.Generic.List`1[[System.String]]", result.Type.ReflectionName); @@ -35,7 +35,7 @@ class A { } } "; - ResolveResult result = Resolve(program); + ResolveResult result = Resolve(program); Assert.AreSame(SharedTypes.UnknownType, result.Type); } @@ -67,21 +67,18 @@ class A { A(double dblVal) {} } "; - MemberResolveResult result = Resolve(program.Replace("$", "$new A()$")); - IMethod m = (IMethod)result.Member; - Assert.IsFalse(m.IsStatic, "new A() is static"); - Assert.AreEqual(0, m.Parameters.Count, "new A() parameter count"); + InvocationResolveResult result = Resolve(program.Replace("$", "$new A()$")); + Assert.IsFalse(result.Member.IsStatic, "new A() is static"); + Assert.AreEqual(0, result.Member.Parameters.Count, "new A() parameter count"); Assert.AreEqual("A", result.Type.FullName); - result = Resolve(program.Replace("$", "$new A(10)$")); - m = (IMethod)result.Member; - Assert.AreEqual(1, m.Parameters.Count, "new A(10) parameter count"); - Assert.AreEqual("intVal", m.Parameters[0].Name, "new A(10) parameter"); + result = Resolve(program.Replace("$", "$new A(10)$")); + Assert.AreEqual(1, result.Member.Parameters.Count, "new A(10) parameter count"); + Assert.AreEqual("intVal", result.Member.Parameters[0].Name, "new A(10) parameter"); - result = Resolve(program.Replace("$", "$new A(11.1)$")); - m = (IMethod)result.Member; - Assert.AreEqual(1, m.Parameters.Count, "new A(11.1) parameter count"); - Assert.AreEqual("dblVal", m.Parameters[0].Name, "new A(11.1) parameter"); + result = Resolve(program.Replace("$", "$new A(11.1)$")); + Assert.AreEqual(1, result.Member.Parameters.Count, "new A(11.1) parameter count"); + Assert.AreEqual("dblVal", result.Member.Parameters[0].Name, "new A(11.1) parameter"); } [Test] @@ -93,11 +90,9 @@ class A { } } "; - MemberResolveResult result = Resolve(program); - IMethod m = (IMethod)result.Member; - Assert.IsNotNull(m); + InvocationResolveResult result = Resolve(program); Assert.AreEqual("A", result.Type.ReflectionName); - Assert.AreEqual(0, m.Parameters.Count); + Assert.AreEqual(0, result.Member.Parameters.Count); } [Test, Ignore("Parser returns incorrect positions")] @@ -122,13 +117,13 @@ class C : B { {} } "; - MemberResolveResult mrr = Resolve(program, "base(b)"); + InvocationResolveResult mrr = Resolve(program, "base(b)"); Assert.AreEqual("A..ctor", mrr.Member.FullName); - mrr = Resolve(program, "base(c)"); + mrr = Resolve(program, "base(c)"); Assert.AreEqual("B..ctor", mrr.Member.FullName); - mrr = Resolve(program, "this(0)"); + mrr = Resolve(program, "this(0)"); Assert.AreEqual("C..ctor", mrr.Member.FullName); } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs index f38fc1d3d1..931cdad98d 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ResolverTestBase.cs @@ -171,7 +171,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver protected T Resolve(string code) where T : ResolveResult { ResolveResult rr = Resolve(code); - Assert.IsTrue(rr is T, "Resolve should be " + typeof(T).Name + ", but was " + (rr != null ? rr.GetType().Name : "null")); + Assert.IsNotNull(rr); + Assert.IsTrue(rr.GetType() == typeof(T), "Resolve should be " + typeof(T).Name + ", but was " + rr.GetType().Name); return (T)rr; } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs index e14224c160..65a9d9fec9 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/TypeInferenceTests.cs @@ -86,7 +86,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ITypeDefinition declType = ctx.GetTypeDefinition(typeof(int)); var methods = declType.Methods.Where(m => m.Name == "Parse").ToList(); - var argument = new MethodGroupResolveResult(declType, "Parse", methods, new IType[0]); + var argument = new MethodGroupResolveResult(new TypeResolveResult(declType), "Parse", methods, new IType[0]); bool success; ti.InferTypeArguments(new [] { A, B }, new [] { argument }, @@ -105,7 +105,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ITypeDefinition declType = ctx.GetTypeDefinition(typeof(Console)); var methods = declType.Methods.Where(m => m.Name == "ReadKey").ToList(); - var argument = new MethodGroupResolveResult(declType, "ReadKey", methods, new IType[0]); + var argument = new MethodGroupResolveResult(new TypeResolveResult(declType), "ReadKey", methods, new IType[0]); bool success; Assert.AreEqual( @@ -123,15 +123,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { IType[] expectedParameterTypes; IType inferredReturnType; + IParameter[] parameters; public MockImplicitLambda(IType[] expectedParameterTypes, IType inferredReturnType) { this.expectedParameterTypes = expectedParameterTypes; this.inferredReturnType = inferredReturnType; + this.parameters = new IParameter[expectedParameterTypes.Length]; + for (int i = 0; i < parameters.Length; i++) { + // UnknownType because this lambda is implicitly typed + parameters[i] = new DefaultParameter(SharedTypes.UnknownType, "X" + i); + } } public override IList Parameters { - get { throw new NotImplementedException(); } + get { return parameters; } } public override Conversion IsValid(IType[] parameterTypes, IType returnType, Conversions conversions) diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/AmbiguousResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/AmbiguousResolveResult.cs index 3c17dd65f3..ec86ce4357 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/AmbiguousResolveResult.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/AmbiguousResolveResult.cs @@ -22,7 +22,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public class AmbiguousMemberResultResult : MemberResolveResult { - public AmbiguousMemberResultResult(IMember member, IType returnType) : base(member, returnType) + public AmbiguousMemberResultResult(ResolveResult targetResult, IMember member, IType returnType) : base(targetResult, member, returnType) { } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs index 0aa33dc4b3..5220b98312 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs @@ -1555,6 +1555,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } } + // TODO: return implicit/explicit conversion being applied return new ResolveResult(targetType); } @@ -1651,7 +1652,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver MemberLookup lookup = new MemberLookup(context, t, t.ProjectContent); ResolveResult r; if (lookupMode == SimpleNameLookupMode.Expression || lookupMode == SimpleNameLookupMode.InvocationTarget) { - r = lookup.Lookup(t, identifier, typeArguments, lookupMode == SimpleNameLookupMode.InvocationTarget); + r = lookup.Lookup(new TypeResolveResult(t), identifier, typeArguments, lookupMode == SimpleNameLookupMode.InvocationTarget); } else { r = lookup.LookupType(t, identifier, typeArguments, parameterizeResultType); } @@ -1785,11 +1786,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return DynamicResult; MemberLookup lookup = CreateMemberLookup(); - ResolveResult result = lookup.Lookup(target.Type, identifier, typeArguments, isInvocationTarget); + ResolveResult result = lookup.Lookup(target, identifier, typeArguments, isInvocationTarget); if (result is UnknownMemberResolveResult) { var extensionMethods = GetExtensionMethods(target.Type, identifier, typeArguments.Count); if (extensionMethods.Count > 0) { - return new MethodGroupResolveResult(target.Type, identifier, EmptyList.Instance, typeArguments) { + return new MethodGroupResolveResult(target, identifier, EmptyList.Instance, typeArguments) { extensionMethods = extensionMethods }; } @@ -1842,6 +1843,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return extensionMethodGroups; } + /// + /// Gets all extension methods available in the current using scope. + /// This list includes unaccessible + /// List> GetAllExtensionMethods() { // TODO: maybe cache the result? @@ -1873,7 +1878,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { return from c in context.GetTypes(namespaceName, StringComparer.Ordinal) - where c.IsStatic && c.HasExtensionMethods + where c.IsStatic && c.HasExtensionMethods && c.TypeParameters.Count == 0 from m in c.Methods where m.IsExtensionMethod select m; @@ -1894,9 +1899,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (mgrr != null) { OverloadResolution or = mgrr.PerformOverloadResolution(context, arguments, argumentNames); if (or.BestCandidate != null) { - IType returnType = or.BestCandidate.ReturnType.Resolve(context); - returnType = returnType.AcceptVisitor(new MethodTypeParameterSubstitution(or.InferredTypeArguments)); - return new MemberResolveResult(or.BestCandidate, returnType); + return new InvocationResolveResult(mgrr.TargetResult, or, context); } else { // No candidate found at all (not even an inapplicable one). // This can happen with empty method groups (as sometimes used with extension methods) @@ -2019,7 +2022,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver or.AddCandidate(p); } if (or.BestCandidate != null) { - return new MemberResolveResult(or.BestCandidate, or.BestCandidate.ReturnType.Resolve(context)); + return new InvocationResolveResult(target, or, context); } else { return ErrorResult; } @@ -2039,7 +2042,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver or.AddCandidate(ctor); } if (or.BestCandidate != null) { - return new MemberResolveResult(or.BestCandidate, type); + return new InvocationResolveResult(new TypeResolveResult(type), or, context); } else { return new ErrorResolveResult(type); } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/InvocationResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/InvocationResolveResult.cs new file mode 100644 index 0000000000..46778dea54 --- /dev/null +++ b/ICSharpCode.NRefactory/CSharp/Resolver/InvocationResolveResult.cs @@ -0,0 +1,83 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the MIT license (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// Represents the result of a method invocation. + /// + public class InvocationResolveResult : MemberResolveResult + { + public readonly OverloadResolutionErrors OverloadResolutionErrors; + public readonly IList TypeArguments; + + public readonly IList Arguments; + public readonly IList ArgumentConversions; + + /// + /// Gets whether this invocation is calling an extension method using extension method syntax. + /// + public readonly bool IsExtensionMethodInvocation; + + /// + /// Gets whether a params-Array is being used in its expanded form. + /// + public readonly bool IsExpandedForm; + + readonly IList argumentToParameterMap; + + public InvocationResolveResult(ResolveResult targetResult, OverloadResolution or, ITypeResolveContext context) + : base( + or.IsExtensionMethodInvocation ? new TypeResolveResult(or.BestCandidate.DeclaringType) : targetResult, + or.BestCandidate, + GetReturnType(or, context)) + { + this.OverloadResolutionErrors = or.BestCandidateErrors; + this.TypeArguments = or.InferredTypeArguments; + this.Arguments = or.Arguments; + this.ArgumentConversions = or.ArgumentConversions; + this.IsExtensionMethodInvocation = or.IsExtensionMethodInvocation; + this.IsExpandedForm = or.BestCandidateIsExpandedForm; + this.argumentToParameterMap = or.GetArgumentToParameterMap(); + } + + static IType GetReturnType(OverloadResolution or, ITypeResolveContext context) + { + IType returnType; + if (or.BestCandidate.EntityType == EntityType.Constructor) + returnType = or.BestCandidate.DeclaringType; + else + returnType = or.BestCandidate.ReturnType.Resolve(context); + + var typeArguments = or.InferredTypeArguments; + if (typeArguments.Count > 0) + return returnType.AcceptVisitor(new MethodTypeParameterSubstitution(typeArguments)); + else + return returnType; + } + + public override bool IsError { + get { return this.OverloadResolutionErrors != OverloadResolutionErrors.None; } + } + + /// + /// Gets an array that maps argument indices to parameter indices. + /// For arguments that could not be mapped to any parameter, the value will be -1. + /// + /// parameterIndex = ArgumentToParameterMap[argumentIndex] + /// + public IList GetArgumentToParameterMap() + { + return argumentToParameterMap; + } + + public new IParameterizedMember Member { + get { return (IParameterizedMember)base.Member; } + } + } +} diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs b/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs index 70bf265857..03912961e2 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/MemberLookup.cs @@ -162,8 +162,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// /// Performs a member lookup. /// - public ResolveResult Lookup(IType type, string name, IList typeArguments, bool isInvocation) + public ResolveResult Lookup(ResolveResult targetResolveResult, string name, IList typeArguments, bool isInvocation) { + IType type = targetResolveResult.Type; int typeArgumentCount = typeArguments.Count; List types = new List(); @@ -259,10 +260,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return new UnknownMemberResolveResult(type, name, typeArguments); IMember firstNonMethod = members.FirstOrDefault(m => !(m is IMethod)); if (members.Count == 1 && firstNonMethod != null) - return new MemberResolveResult(firstNonMethod, context); + return new MemberResolveResult(targetResolveResult, firstNonMethod, context); if (firstNonMethod == null) - return new MethodGroupResolveResult(type, name, members.ConvertAll(m => (IMethod)m), typeArguments); - return new AmbiguousMemberResultResult(firstNonMethod, firstNonMethod.ReturnType.Resolve(context)); + return new MethodGroupResolveResult(targetResolveResult, name, members.ConvertAll(m => (IMethod)m), typeArguments); + return new AmbiguousMemberResultResult(targetResolveResult, firstNonMethod, firstNonMethod.ReturnType.Resolve(context)); } static bool IsNonInterfaceType(ITypeDefinition def) diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/MemberResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/MemberResolveResult.cs index f6994ef3a8..4e11010072 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/MemberResolveResult.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/MemberResolveResult.cs @@ -14,25 +14,29 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver readonly IMember member; readonly bool isConstant; readonly object constantValue; + readonly ResolveResult targetResult; - public MemberResolveResult(IMember member, IType returnType) : base(returnType) + public MemberResolveResult(ResolveResult targetResult, IMember member, IType returnType) : base(returnType) { if (member == null) throw new ArgumentNullException("member"); + this.targetResult = targetResult; this.member = member; } - public MemberResolveResult(IMember member, IType returnType, object constantValue) : base(returnType) + public MemberResolveResult(ResolveResult targetResult, IMember member, IType returnType, object constantValue) : base(returnType) { if (member == null) throw new ArgumentNullException("member"); + this.targetResult = targetResult; this.member = member; this.isConstant = true; this.constantValue = constantValue; } - public MemberResolveResult(IMember member, ITypeResolveContext context) : base(member.ReturnType.Resolve(context)) + public MemberResolveResult(ResolveResult targetResult, IMember member, ITypeResolveContext context) : base(member.ReturnType.Resolve(context)) { + this.targetResult = targetResult; this.member = member; IField field = member as IField; if (field != null) { diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs index e4ad764051..46650a1fd9 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/MethodGroupResolveResult.cs @@ -17,26 +17,33 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { readonly ReadOnlyCollection methods; readonly ReadOnlyCollection typeArguments; - readonly IType targetType; + readonly ResolveResult targetResult; readonly string methodName; - public MethodGroupResolveResult(IType targetType, string methodName, IList methods, IList typeArguments) : base(SharedTypes.UnknownType) + public MethodGroupResolveResult(ResolveResult targetResult, string methodName, IList methods, IList typeArguments) : base(SharedTypes.UnknownType) { - if (targetType == null) - throw new ArgumentNullException("targetType"); + if (targetResult == null) + throw new ArgumentNullException("targetResult"); if (methods == null) throw new ArgumentNullException("methods"); - this.targetType = targetType; + this.targetResult = targetResult; this.methodName = methodName; this.methods = new ReadOnlyCollection(methods); this.typeArguments = typeArguments != null ? new ReadOnlyCollection(typeArguments) : EmptyList.Instance; } + /// + /// Gets the resolve result for the target object. + /// + public ResolveResult TargetResult { + get { return targetResult; } + } + /// /// Gets the type of the reference to the target object. /// public IType TargetType { - get { return targetType; } + get { return targetResult.Type; } } /// @@ -81,7 +88,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver UsingScope oldUsingScope = resolver.UsingScope; try { resolver.UsingScope = usingScope; - extensionMethods = resolver.GetExtensionMethods(targetType, methodName, typeArguments.Count); + extensionMethods = resolver.GetExtensionMethods(this.TargetType, methodName, typeArguments.Count); } finally { resolver.UsingScope = oldUsingScope; resolver = null; @@ -121,6 +128,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } var extOr = new OverloadResolution(context, extArguments, extArgumentNames, typeArgumentArray); extOr.AllowExpandingParams = allowExpandingParams; + extOr.IsExtensionMethodInvocation = true; foreach (var g in extensionMethods) { foreach (var m in g) { diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs index 7a8105a8f1..ff8acc14d3 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs @@ -114,12 +114,28 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } #endregion + /// + /// Gets/Sets whether the methods are extension methods that are being called using extension method syntax. + /// + /// + /// Setting this property to true restricts the possible conversions on the first argument to + /// implicit identity, reference, or boxing conversions. + /// + public bool IsExtensionMethodInvocation { get; set; } + /// /// Gets/Sets whether expanding 'params' into individual elements is allowed. /// The default value is true. /// public bool AllowExpandingParams { get; set; } + /// + /// Gets the arguments for which this OverloadResolution instance was created. + /// + public IList Arguments { + get { return arguments; } + } + #region AddCandidate public OverloadResolutionErrors AddCandidate(IParameterizedMember member) { @@ -348,10 +364,17 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (candidate.Parameters[parameterIndex].IsOut || candidate.Parameters[parameterIndex].IsRef) candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch); } - Conversion c = conversions.ImplicitConversion(arguments[i], candidate.ParameterTypes[parameterIndex]); + IType parameterType = candidate.ParameterTypes[parameterIndex]; + Conversion c = conversions.ImplicitConversion(arguments[i], parameterType); candidate.ArgumentConversions[i] = c; - if (!c) - candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch); + if (IsExtensionMethodInvocation && parameterIndex == 0) { + // First parameter to extension method must be an identity, reference or boxing conversion + if (!(c == Conversion.IdentityConversion || c == Conversion.ImplicitReferenceConversion || c == Conversion.BoxingConversion)) + candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch); + } else { + if (!c) + candidate.AddError(OverloadResolutionErrors.ArgumentTypeMismatch); + } } } #endregion @@ -576,5 +599,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver return new Conversion[arguments.Length]; } } + + /// + /// Gets an array that maps argument indices to parameter indices. + /// For arguments that could not be mapped to any parameter, the value will be -1. + /// + /// parameterIndex = GetArgumentToParameterMap()[argumentIndex] + /// + public IList GetArgumentToParameterMap() + { + if (bestCandidate != null) + return bestCandidate.ArgumentToParameterMap; + else + return null; + } } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs index 98d157ab8b..6009d8b44f 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs @@ -332,7 +332,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (resolverEnabled) { if (variableInitializer.Parent is FieldDeclaration) { if (resolver.CurrentMember != null) - return new MemberResolveResult(resolver.CurrentMember, resolver.CurrentMember.ReturnType.Resolve(resolver.Context)); + return new MemberResolveResult(null, resolver.CurrentMember, resolver.CurrentMember.ReturnType.Resolve(resolver.Context)); } else { string identifier = variableInitializer.Name; foreach (IVariable v in resolver.LocalVariables) { @@ -353,7 +353,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ScanChildren(fixedVariableInitializer); if (resolverEnabled) { if (resolver.CurrentMember != null) - return new MemberResolveResult(resolver.CurrentMember, resolver.CurrentMember.ReturnType.Resolve(resolver.Context)); + return new MemberResolveResult(null, resolver.CurrentMember, resolver.CurrentMember.ReturnType.Resolve(resolver.Context)); else return errorResult; } else { @@ -371,7 +371,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ScanChildren(member); if (resolverEnabled && resolver.CurrentMember != null) - return new MemberResolveResult(resolver.CurrentMember, resolver.Context); + return new MemberResolveResult(null, resolver.CurrentMember, resolver.Context); else return errorResult; } finally { @@ -418,7 +418,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } if (resolverEnabled && resolver.CurrentMember != null) - return new MemberResolveResult(resolver.CurrentMember, resolver.Context); + return new MemberResolveResult(null, resolver.CurrentMember, resolver.Context); else return errorResult; } finally { @@ -453,7 +453,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } if (resolverEnabled && resolver.CurrentMember != null) - return new MemberResolveResult(resolver.CurrentMember, resolver.Context); + return new MemberResolveResult(null, resolver.CurrentMember, resolver.Context); else return errorResult; } finally { @@ -522,7 +522,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ScanChildren(enumMemberDeclaration); if (resolverEnabled && resolver.CurrentMember != null) - return new MemberResolveResult(resolver.CurrentMember, resolver.Context); + return new MemberResolveResult(null, resolver.CurrentMember, resolver.Context); else return errorResult; } finally { diff --git a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index 072da8cfe6..786db88365 100644 --- a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -104,6 +104,7 @@ +