Browse Source

Added delegate compatibility check to method-group conversions.

newNRvisualizers
Daniel Grunwald 13 years ago
parent
commit
59cc439a30
  1. 48
      ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs
  2. 14
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ComTests.cs
  3. 180
      ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs
  4. 17
      ICSharpCode.NRefactory/Semantics/Conversion.cs

48
ICSharpCode.NRefactory.CSharp/Resolver/CSharpConversions.cs

@ -906,13 +906,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -906,13 +906,13 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
MethodGroupResolveResult rr = resolveResult as MethodGroupResolveResult;
if (rr == null)
return Conversion.None;
IMethod m = toType.GetDelegateInvokeMethod();
if (m == null)
IMethod invoke = toType.GetDelegateInvokeMethod();
if (invoke == null)
return Conversion.None;
ResolveResult[] args = new ResolveResult[m.Parameters.Count];
ResolveResult[] args = new ResolveResult[invoke.Parameters.Count];
for (int i = 0; i < args.Length; i++) {
IParameter param = m.Parameters[i];
IParameter param = invoke.Parameters[i];
IType parameterType = param.Type;
if ((param.IsRef || param.IsOut) && parameterType.Kind == TypeKind.ByReference) {
parameterType = ((ByReferenceType)parameterType).ElementType;
@ -926,11 +926,49 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -926,11 +926,49 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
IMethod method = (IMethod)or.GetBestCandidateWithSubstitutedTypeArguments();
var thisRR = rr.TargetResult as ThisResolveResult;
bool isVirtual = method.IsOverridable && !(thisRR != null && thisRR.CausesNonVirtualInvocation);
return Conversion.MethodGroupConversion(method, isVirtual);
if (IsDelegateCompatible(method, invoke))
return Conversion.MethodGroupConversion(method, isVirtual);
else
return Conversion.InvalidMethodGroupConversion(method, isVirtual);
} else {
return Conversion.None;
}
}
/// <summary>
/// Gets whether a method <paramref name="m"/> is compatible with a delegate type.
/// §15.2 Delegate compatibility
/// </summary>
/// <param name="m">The method to test for compatibility</param>
/// <param name="invoke">The invoke method of the delegate</param>
public bool IsDelegateCompatible(IMethod m, IMethod invoke)
{
if (m == null)
throw new ArgumentNullException("m");
if (invoke == null)
throw new ArgumentNullException("invoke");
if (m.Parameters.Count != invoke.Parameters.Count)
return false;
for (int i = 0; i < m.Parameters.Count; i++) {
var pm = m.Parameters[i];
var pd = invoke.Parameters[i];
// ret/out must match
if (pm.IsRef != pd.IsRef || pm.IsOut != pd.IsOut)
return false;
if (pm.IsRef || pm.IsOut) {
// ref/out parameters must have same types
if (!pm.Type.Equals(pd.Type))
return false;
} else {
// non-ref/out parameters must have an identity or reference conversion from pd to pm
if (!IdentityConversion(pd.Type, pm.Type) && !IsImplicitReferenceConversion(pd.Type, pm.Type))
return false;
}
}
// check return type compatibility
return IdentityConversion(m.ReturnType, invoke.ReturnType)
|| IsImplicitReferenceConversion(m.ReturnType, invoke.ReturnType);
}
#endregion
#region BetterConversion

14
ICSharpCode.NRefactory.Tests/CSharp/Resolver/ComTests.cs

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using NUnit.Framework;
@ -47,5 +48,18 @@ public class Test : Dummy { @@ -47,5 +48,18 @@ public class Test : Dummy {
Assert.AreEqual("Dummy", rr.Type.ReflectionName);
Assert.AreEqual(1, rr.Member.Parameters.Count);
}
[Test]
public void CyclicCoClass()
{
string program = @"using System;
using System.Runtime.InteropServices;
[ComImport, Guid(""698D8281-3890-41A6-8A2F-DBC29CBAB8BC""), CoClass(typeof(Dummy))]
public interface $Dummy { }";
var trr = ResolveAtLocation<TypeResolveResult>(program);
Assert.AreEqual(0, trr.Type.GetConstructors().Count());
}
}
}

180
ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs

@ -602,5 +602,185 @@ class Test { @@ -602,5 +602,185 @@ class Test {
Assert.AreEqual(C.ImplicitReferenceConversion, GetConversion(program));
}
[Test]
public void MethodGroupConversion_Void()
{
string program = @"using System;
delegate void D();
class Test {
D d = $M$;
public static void M() {}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsMethodGroupConversion);
Assert.IsNotNull(c.Method);
}
[Test]
public void MethodGroupConversion_MatchingSignature()
{
string program = @"using System;
delegate object D(int argument);
class Test {
D d = $M$;
public static object M(int argument) {}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsMethodGroupConversion);
}
[Test]
public void MethodGroupConversion_InvalidReturnType()
{
string program = @"using System;
delegate object D(int argument);
class Test {
D d = $M$;
public static int M(int argument) {}
}";
var c = GetConversion(program);
Assert.IsFalse(c.IsValid);
Assert.IsTrue(c.IsMethodGroupConversion);
}
[Test]
public void MethodGroupConversion_CovariantReturnType()
{
string program = @"using System;
delegate object D(int argument);
class Test {
D d = $M$;
public static string M(int argument) {}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsMethodGroupConversion);
}
[Test]
public void MethodGroupConversion_RefArgumentTypesEqual()
{
string program = @"using System;
delegate void D(ref object o);
class Test {
D d = $M$;
public static void M(ref object o) {}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsMethodGroupConversion);
}
[Test]
public void MethodGroupConversion_RefArgumentObjectVsDynamic()
{
string program = @"using System;
delegate void D(ref object o);
class Test {
D d = $M$;
public static void M(ref dynamic o) {}
}";
var c = GetConversion(program);
Assert.IsFalse(c.IsValid);
Assert.IsTrue(c.IsMethodGroupConversion);
}
[Test]
public void MethodGroupConversion_RefVsOut()
{
string program = @"using System;
delegate void D(ref object o);
class Test {
D d = $M$;
public static void M(out object o) {}
}";
var c = GetConversion(program);
Assert.IsFalse(c.IsValid);
}
[Test]
public void MethodGroupConversion_RefVsNormal()
{
string program = @"using System;
delegate void D(ref object o);
class Test {
D d = $M$;
public static void M(object o) {}
}";
var c = GetConversion(program);
Assert.IsFalse(c.IsValid);
}
[Test]
public void MethodGroupConversion_NormalVsOut()
{
string program = @"using System;
delegate void D(object o);
class Test {
D d = $M$;
public static void M(out object o) {}
}";
var c = GetConversion(program);
Assert.IsFalse(c.IsValid);
}
[Test]
public void MethodGroupConversion_MatchingNormalParameter()
{
string program = @"using System;
delegate void D(object o);
class Test {
D d = $M$;
public static void M(object o) {}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsMethodGroupConversion);
}
[Test]
public void MethodGroupConversion_IdentityConversion()
{
string program = @"using System;
delegate void D(object o);
class Test {
D d = $M$;
public static void M(dynamic o) {}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsMethodGroupConversion);
}
[Test]
public void MethodGroupConversion_Contravariance()
{
string program = @"using System;
delegate void D(string o);
class Test {
D d = $M$;
public static void M(object o) {}
}";
var c = GetConversion(program);
Assert.IsTrue(c.IsValid);
Assert.IsTrue(c.IsMethodGroupConversion);
}
[Test, Ignore("Not sure if this conversion should be valid or not... NR and mcs both accept it as valid, csc treats it as invalid")]
public void MethodGroupConversion_NoContravarianceDynamic()
{
string program = @"using System;
delegate void D(string o);
class Test {
D d = $M$;
public static void M(dynamic o) {}
}";
var c = GetConversion(program);
//Assert.IsFrue(c.IsValid);
Assert.IsTrue(c.IsMethodGroupConversion);
}
}
}

17
ICSharpCode.NRefactory/Semantics/Conversion.cs

@ -92,7 +92,14 @@ namespace ICSharpCode.NRefactory.Semantics @@ -92,7 +92,14 @@ namespace ICSharpCode.NRefactory.Semantics
{
if (chosenMethod == null)
throw new ArgumentNullException("chosenMethod");
return new MethodGroupConv(chosenMethod, isVirtualMethodLookup);
return new MethodGroupConv(chosenMethod, isVirtualMethodLookup, isValid: true);
}
public static Conversion InvalidMethodGroupConversion(IMethod chosenMethod, bool isVirtualMethodLookup)
{
if (chosenMethod == null)
throw new ArgumentNullException("chosenMethod");
return new MethodGroupConv(chosenMethod, isVirtualMethodLookup, isValid: false);
}
#endregion
@ -311,11 +318,17 @@ namespace ICSharpCode.NRefactory.Semantics @@ -311,11 +318,17 @@ namespace ICSharpCode.NRefactory.Semantics
{
readonly IMethod method;
readonly bool isVirtualMethodLookup;
readonly bool isValid;
public MethodGroupConv(IMethod method, bool isVirtualMethodLookup)
public MethodGroupConv(IMethod method, bool isVirtualMethodLookup, bool isValid)
{
this.method = method;
this.isVirtualMethodLookup = isVirtualMethodLookup;
this.isValid = isValid;
}
public override bool IsValid {
get { return isValid; }
}
public override bool IsImplicit {

Loading…
Cancel
Save