diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs index 0111335222..d984d80330 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs @@ -1900,11 +1900,33 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver // C# 4.0 spec: §7.6.5 if (target.Type.Kind == TypeKind.Dynamic) { - return new DynamicInvocationResolveResult(target, arguments.Select((a, i) => new DynamicInvocationArgument(argumentNames != null ? argumentNames[i] : null, a)).ToList().AsReadOnly()); + return new DynamicInvocationResolveResult(target, DynamicInvocationType.Invocation, arguments.Select((a, i) => new DynamicInvocationArgument(argumentNames != null ? argumentNames[i] : null, a)).ToList().AsReadOnly()); } MethodGroupResolveResult mgrr = target as MethodGroupResolveResult; if (mgrr != null) { + if (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic)) { + // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable method. + var or2 = new OverloadResolution(compilation, arguments, argumentNames, mgrr.TypeArguments.ToArray(), conversions); + var applicableMethods = mgrr.MethodsGroupedByDeclaringType.SelectMany(m => m, (x, m) => new { x.DeclaringType, Method = m }).Where(x => OverloadResolution.IsApplicable(or2.AddCandidate(x.Method))).ToList(); + + if (applicableMethods.Count > 1) { + ResolveResult actualTarget; + if (applicableMethods.All(x => x.Method.IsStatic) && !(mgrr.TargetResult is TypeResolveResult)) + actualTarget = new TypeResolveResult(mgrr.TargetResult.Type); + else + actualTarget = mgrr.TargetResult; + + var l = new List(); + foreach (var m in applicableMethods) { + if (l.Count == 0 || l[l.Count - 1].DeclaringType != m.DeclaringType) + l.Add(new MethodListWithDeclaringType(m.DeclaringType)); + l[l.Count - 1].Add(m.Method); + } + return new DynamicInvocationResolveResult(new MethodGroupResolveResult(actualTarget, mgrr.MethodName, l, mgrr.TypeArguments), DynamicInvocationType.Invocation, arguments.Select((a, i) => new DynamicInvocationArgument(argumentNames != null ? argumentNames[i] : null, a)).ToList().AsReadOnly()); + } + } + OverloadResolution or = mgrr.PerformOverloadResolution(compilation, arguments, argumentNames, checkForOverflow: checkForOverflow, conversions: conversions); if (or.BestCandidate != null) { if (or.BestCandidate.IsStatic && !or.IsExtensionMethodInvocation && !(mgrr.TargetResult is TypeResolveResult)) @@ -2043,10 +2065,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver { switch (target.Type.Kind) { case TypeKind.Dynamic: - for (int i = 0; i < arguments.Length; i++) { - arguments[i] = Convert(arguments[i], SpecialType.Dynamic); - } - return new ArrayAccessResolveResult(SpecialType.Dynamic, target, arguments); + return new DynamicInvocationResolveResult(target, DynamicInvocationType.Indexing, arguments.Select((a, i) => new DynamicInvocationArgument(argumentNames != null ? argumentNames[i] : null, a)).ToList().AsReadOnly()); case TypeKind.Array: case TypeKind.Pointer: @@ -2056,9 +2075,21 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } // §7.6.6.2 Indexer access - OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); + MemberLookup lookup = CreateMemberLookup(); var indexers = lookup.LookupIndexers(target.Type); + + if (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic)) { + // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable indexer. + var or2 = new OverloadResolution(compilation, arguments, argumentNames, null, conversions); + var applicableIndexers = indexers.SelectMany(x => x).Where(m => OverloadResolution.IsApplicable(or2.AddCandidate(m))).ToList(); + + if (applicableIndexers.Count > 1) { + return new DynamicInvocationResolveResult(target, DynamicInvocationType.Indexing, arguments.Select((a, i) => new DynamicInvocationArgument(argumentNames != null ? argumentNames[i] : null, a)).ToList().AsReadOnly()); + } + } + + OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); or.AddMethodLists(indexers); if (or.BestCandidate != null) { return or.CreateResolveResult(target); @@ -2122,12 +2153,22 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } OverloadResolution or = CreateOverloadResolution(arguments, argumentNames); MemberLookup lookup = CreateMemberLookup(); + var allApplicable = (arguments.Any(a => a.Type.Kind == TypeKind.Dynamic) ? new List() : null); foreach (IMethod ctor in type.GetConstructors()) { - if (lookup.IsAccessible(ctor, allowProtectedAccess)) - or.AddCandidate(ctor); + if (lookup.IsAccessible(ctor, allowProtectedAccess)) { + var orErrors = or.AddCandidate(ctor); + if (allApplicable != null && OverloadResolution.IsApplicable(orErrors)) + allApplicable.Add(ctor); + } else or.AddCandidate(ctor, OverloadResolutionErrors.Inaccessible); } + + if (allApplicable != null && allApplicable.Count > 1) { + // If we have dynamic arguments, we need to represent the invocation as a dynamic invocation if there is more than one applicable constructor. + return new DynamicInvocationResolveResult(new MethodGroupResolveResult(null, allApplicable[0].Name, new[] { new MethodListWithDeclaringType(type, allApplicable) }, null), DynamicInvocationType.ObjectCreation, arguments.Select((a, i) => new DynamicInvocationArgument(argumentNames != null ? argumentNames[i] : null, a)).ToList().AsReadOnly(), initializerStatements); + } + if (or.BestCandidate != null) { return or.CreateResolveResult(null, initializerStatements); } else { diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/DynamicInvocationResolveResult.cs b/ICSharpCode.NRefactory.CSharp/Resolver/DynamicInvocationResolveResult.cs index 6139be02c0..b720d68b74 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/DynamicInvocationResolveResult.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/DynamicInvocationResolveResult.cs @@ -45,24 +45,57 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } + public enum DynamicInvocationType { + /// + /// The invocation is a normal invocation ( 'a(b)' ). + /// + Invocation, + + /// + /// The invocation is an indexing ( 'a[b]' ). + /// + Indexing, + + /// + /// The invocation is an object creation ( 'new a(b)' ). Also used when invoking a base constructor ( ' : base(a) ' ) and chaining constructors ( ' : this(a) '). + /// + ObjectCreation, + } + /// /// Represents the result of an invocation of a member of a dynamic object. /// public class DynamicInvocationResolveResult : ResolveResult { /// - /// Target of the invocation (a dynamic object). + /// Target of the invocation. Can be a dynamic expression or a . /// public readonly ResolveResult Target; + /// + /// Type of the invocation. + /// + public readonly DynamicInvocationType InvocationType; + /// /// Arguments for the call. /// public readonly IList Arguments; - public DynamicInvocationResolveResult(ResolveResult target, IList arguments) : base(SpecialType.Dynamic) { - this.Target = target; - this.Arguments = arguments ?? EmptyList.Instance; + /// + /// Gets the list of initializer statements that are appplied to the result of this invocation. + /// This is used to represent object and collection initializers. + /// With the initializer statements, the is used + /// to refer to the result of this invocation. + /// Initializer statements can only exist if the is . + /// + public readonly IList InitializerStatements; + + public DynamicInvocationResolveResult(ResolveResult target, DynamicInvocationType invocationType, IList arguments, IList initializerStatements = null) : base(SpecialType.Dynamic) { + this.Target = target; + this.InvocationType = invocationType; + this.Arguments = arguments ?? EmptyList.Instance; + this.InitializerStatements = initializerStatements ?? EmptyList.Instance; } public override string ToString() diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs index 48fc294abb..243be0577e 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs @@ -86,8 +86,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public MethodGroupResolveResult(ResolveResult targetResult, string methodName, IList methods, IList typeArguments) : base(SpecialType.UnknownType) { - if (targetResult == null) - throw new ArgumentNullException("targetResult"); if (methods == null) throw new ArgumentNullException("methods"); this.targetResult = targetResult; diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs index 088064320c..1c02f6c114 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/DynamicTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using System.Text; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; @@ -37,6 +38,7 @@ class TestClass { }"; var rr = Resolve(program); Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); Assert.That(rr.Target, Is.InstanceOf()); var dynamicMember = (DynamicMemberResolveResult)rr.Target; Assert.That(dynamicMember.Target is LocalResolveResult && ((LocalResolveResult)dynamicMember.Target).Variable.Name == "obj"); @@ -61,6 +63,7 @@ class TestClass { }"; var rr = Resolve(program); Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); Assert.That(rr.Target, Is.InstanceOf()); var dynamicMember = (DynamicMemberResolveResult)rr.Target; Assert.That(dynamicMember.Target is LocalResolveResult && ((LocalResolveResult)dynamicMember.Target).Variable.Name == "obj"); @@ -86,12 +89,14 @@ class TestClass { }"; var rr = Resolve(program); Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); Assert.That(rr.Target, Is.InstanceOf()); var innerInvocation = (DynamicInvocationResolveResult)rr.Target; Assert.That(innerInvocation.Target, Is.InstanceOf()); var dynamicMember = (DynamicMemberResolveResult)innerInvocation.Target; Assert.That(dynamicMember.Target is LocalResolveResult && ((LocalResolveResult)dynamicMember.Target).Variable.Name == "obj"); Assert.That(dynamicMember.Member, Is.EqualTo("SomeMethod")); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); Assert.That(innerInvocation.Arguments.Count, Is.EqualTo(1)); Assert.That(innerInvocation.Arguments[0].Name, Is.Null); Assert.That(innerInvocation.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)innerInvocation.Arguments[0].Value).Variable.Name == "a"); @@ -99,5 +104,612 @@ class TestClass { Assert.That(rr.Arguments[0].Name, Is.Null); Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "b"); } + + [Test] + public void InvocationWithDynamicArgumentWithOneApplicableMethod() { + string program = @"using System; +class TestClass { + public void SomeMethod(int a) {} + public void SomeMethod(int a, string b) {} + + void F() { + dynamic obj = null; + var x = $this.SomeMethod(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Member.Name, Is.EqualTo("SomeMethod")); + Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + var cr = rr.Arguments[0] as ConversionResolveResult; + Assert.That(cr, Is.Not.Null); + Assert.That(cr.Conversion.IsImplicit, Is.True); + Assert.That(cr.Conversion.IsDynamicConversion, Is.True); + Assert.That(cr.Input is LocalResolveResult && ((LocalResolveResult)cr.Input).Variable.Name == "obj"); + } + + [Test] + public void InvocationWithDynamicArgumentWhenBothAnOwnAndABaseMethodAreApplicable() { + string program = @"using System; +class TestBase { + public void SomeMethod(int a) {} +} + +class TestClass : TestBase { + public void SomeMethod(string a) {} + public void SomeMethod(string a, int b) {} + + void F() { + dynamic obj = null; + var x = $this.SomeMethod(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.InstanceOf()); + Assert.That(mg.MethodName, Is.EqualTo("SomeMethod")); + Assert.That(mg.Methods.Count(), Is.EqualTo(2)); + Assert.That(mg.Methods.Any(m => m.Parameters.Count == 1 && m.DeclaringType.Name == "TestBase" && m.Name == "SomeMethod" && m.Parameters[0].Type.Name == "Int32")); + Assert.That(mg.Methods.Any(m => m.Parameters.Count == 1 && m.DeclaringType.Name == "TestClass" && m.Name == "SomeMethod" && m.Parameters[0].Type.Name == "String")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + } + + [Test, Ignore("Fails")] + public void InvocationWithDynamicArgumentWhenABaseMethodIsShadowed() { + string program = @"using System; +class TestBase { + public void SomeMethod(int a) {} +} + +class TestClass : TestBase { + public void SomeMethod(int a) {} + public void SomeMethod(string a, int b) {} + + void F() { + dynamic obj = null; + var x = $this.SomeMethod(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Member.Name, Is.EqualTo("SomeMethod")); + Assert.That(rr.Member.DeclaringType.Name, Is.EqualTo("TestClass")); + Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + var cr = rr.Arguments[0] as ConversionResolveResult; + Assert.That(cr, Is.Not.Null); + Assert.That(cr.Conversion.IsImplicit, Is.True); + Assert.That(cr.Conversion.IsDynamicConversion, Is.True); + Assert.That(cr.Input is LocalResolveResult && ((LocalResolveResult)cr.Input).Variable.Name == "obj"); + } + + [Test] + public void InvocationWithDynamicArgumentWithTwoApplicableMethods() { + string program = @"using System; +class TestClass { + public void SomeMethod(int a) {} + public void SomeMethod(string a) {} + public void SomeMethod(int a, string b) {} + + void F() { + dynamic obj = null; + var x = $SomeMethod(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.InstanceOf()); + Assert.That(mg.MethodName, Is.EqualTo("SomeMethod")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 1)); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == "SomeMethod" && m.DeclaringType.Name == "TestClass")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + } + + [Test] + public void InvocationWithDynamicArgumentWithTwoApplicableStaticMethods() { + string program = @"using System; +class TestClass { + public static void SomeMethod(int a) {} + public static void SomeMethod(string a) {} + public static void SomeMethod(int a, string b) {} + + void F() { + dynamic obj = null; + var x = $SomeMethod(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.InstanceOf()); + Assert.That(mg.MethodName, Is.EqualTo("SomeMethod")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 1)); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == "SomeMethod" && m.DeclaringType.Name == "TestClass")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + } + + [Test] + public void InvocationWithDynamicArgumentWithApplicableStaticAndNonStaticMethodsFavorTheNonStaticOne() { + string program = @"using System; +class TestClass { + public static void SomeMethod(int a) {} + public void SomeMethod(string a) {} + public static void SomeMethod(int a, string b) {} + + void F() { + dynamic obj = null; + var x = $SomeMethod(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.InstanceOf()); + Assert.That(mg.MethodName, Is.EqualTo("SomeMethod")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 1)); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == "SomeMethod" && m.DeclaringType.Name == "TestClass")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + } + + [Test] + public void InvocationWithDynamicArgumentWhenTheOnlyApplicableMethodIsAnExtensionMethod() { + string program = @"using System; +static class OtherClass { + public void SomeMethod(this TestClass x, int a) {} + public void SomeMethod(this TestClass x, string a) {} + public void SomeMethod(this TestClass x, int a, string b) {} +} +class TestClass { + void F() { + dynamic obj = null; + var x = $this.SomeMethod(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.IsError, Is.True); + } + + [Test] + public void InvocationWithDynamicArgumentWithTwoApplicableMethodsAndNamedArguments() { + string program = @"using System; +class TestClass { + public void SomeMethod(int a, int i) {} + public void SomeMethod(string a, int i) {} + public void SomeMethod(int a, string b, int i) {} + + void F() { + dynamic obj = null; + int idx = 0; + var x = $this.SomeMethod(a: obj, i: idx)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Invocation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.InstanceOf()); + Assert.That(mg.MethodName, Is.EqualTo("SomeMethod")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 2) && mg.Methods.All(m => m.Parameters[1].Type.Name == "Int32")); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == "SomeMethod" && m.DeclaringType.Name == "TestClass")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments[0].Name, Is.EqualTo("a")); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + Assert.That(rr.Arguments[1].Name, Is.EqualTo("i")); + Assert.That(rr.Arguments[1].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[1].Value).Variable.Name == "idx"); + } + + [Test] + public void IndexingDynamicObjectWithUnnamedArguments() { + string program = @"using System; +class TestClass { + void F() { + dynamic obj = null; + int a = 0, b = 0; + object o = $obj[a]$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Indexing)); + Assert.That(rr.Target is LocalResolveResult && ((LocalResolveResult)rr.Target).Variable.Name == "obj"); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments[0].Name, Is.Null); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "a"); + } + + [Test] + public void IndexingDynamicObjectWithNamedArguments() { + string program = @"using System; +class TestClass { + void F() { + dynamic obj = null; + int a = 0, b = 0; + $obj[arg1: a, arg2: b]$ = 1; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Indexing)); + Assert.That(rr.Target is LocalResolveResult && ((LocalResolveResult)rr.Target).Variable.Name == "obj"); + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments[0].Name, Is.EqualTo("arg1")); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "a"); + Assert.That(rr.Arguments[1].Name, Is.EqualTo("arg2")); + Assert.That(rr.Arguments[1].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[1].Value).Variable.Name == "b"); + } + + [Test] + public void IndexingWithDynamicArgumentWithOneApplicableIndexer() { + string program = @"using System; +class TestClass { + public int this[int a] { get { return 0; } } + public int this[int a, string b] { get { return 0; } } + + void F() { + dynamic obj = null; + var x = $this[obj]$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Member.Name, Is.EqualTo("Item")); + Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + var cr = rr.Arguments[0] as ConversionResolveResult; + Assert.That(cr, Is.Not.Null); + Assert.That(cr.Conversion.IsImplicit, Is.True); + Assert.That(cr.Conversion.IsDynamicConversion, Is.True); + Assert.That(cr.Input is LocalResolveResult && ((LocalResolveResult)cr.Input).Variable.Name == "obj"); + } + + [Test] + public void IndexingWithDynamicArgumentWithTwoApplicableIndexersAndUnnamedArguments() { + string program = @"using System; +class TestClass { + public int this[int a] { get { return 0; } } + public int this[string a] { get { return 0; } } + void F() { + dynamic obj = null; + var x = $this[obj]$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Indexing)); + Assert.That(rr.Target, Is.InstanceOf()); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments[0].Name, Is.Null); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + } + + [Test] + public void IndexingWithDynamicArgumentWithAnApplicableBaseIndexer() { + string program = @"using System; +class TestBase { + public int this[int a] { get { return 0; } } +} + +class TestClass : TestBase { + public int this[string a] { get { return 0; } } + public int this[string a, int b] { get { return 0; } } + void F() { + dynamic obj = null; + var x = $this[obj]$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Indexing)); + Assert.That(rr.Target, Is.InstanceOf()); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments[0].Name, Is.Null); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + } + + [Test, Ignore("Fails")] + public void IndexingWithDynamicArgumentWithTheOnlyApplicableIndexerShadowingABaseIndexer() { + string program = @"using System; +class TestBase { + public int this[int a] { get { return 0; } } +} + +class TestClass : TestBase { + public new int this[int a] { get { return 0; } } + public int this[int a, string b] { get { return 0; } } + + void F() { + dynamic obj = null; + var x = $this[obj]$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Member.Name, Is.EqualTo("Item")); + Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + var cr = rr.Arguments[0] as ConversionResolveResult; + Assert.That(cr, Is.Not.Null); + Assert.That(cr.Conversion.IsImplicit, Is.True); + Assert.That(cr.Conversion.IsDynamicConversion, Is.True); + Assert.That(cr.Input is LocalResolveResult && ((LocalResolveResult)cr.Input).Variable.Name == "obj"); + } + + [Test] + public void IndexingWithDynamicArgumentWithTwoApplicableIndexersAndNamedArguments() { + string program = @"using System; +class TestClass { + public int this[int a, int i] { get { return 0; } } + public int this[string a, int i] { get { return 0; } } + void F() { + dynamic obj = null; + int idx = 0; + var x = $this[a: obj, i: idx]$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Type.Kind, Is.EqualTo(TypeKind.Dynamic)); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.Indexing)); + Assert.That(rr.Target, Is.InstanceOf()); + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments[0].Name, Is.EqualTo("a")); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + Assert.That(rr.Arguments[1].Name, Is.EqualTo("i")); + Assert.That(rr.Arguments[1].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[1].Value).Variable.Name == "idx"); + } + + [Test] + public void ConstructingObjectWithDynamicArgumentWithOneApplicableConstructor() { + string program = @"using System; +class TestClass { + public TestClass(int a) {} + public void TestClass(int a, string b) {} + + void F() { + dynamic obj = null; + var x = $new TestClass(obj)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.Member.Name, Is.EqualTo(".ctor")); + Assert.That(rr.TargetResult, Is.Null); + Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(1)); + Assert.That(rr.Arguments.Count, Is.EqualTo(1)); + var cr = rr.Arguments[0] as ConversionResolveResult; + Assert.That(cr, Is.Not.Null); + Assert.That(cr.Input is LocalResolveResult && ((LocalResolveResult)cr.Input).Variable.Name == "obj"); + } + + [Test] + public void ConstructingObjectWithDynamicArgumentWithTwoApplicableConstructors() { + string program = @"using System; +class TestClass { + public TestClass(int a, int b) {} + public TestClass(string a, int b) {} + public void TestClass(int a, string b) {} + + void F() { + dynamic obj = null; + int i = 0; + var x = $new TestClass(obj, i)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.ObjectCreation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.Null); + Assert.That(mg.MethodName, Is.EqualTo(".ctor")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 2 && m.Parameters[1].Type.Name == "Int32")); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == ".ctor" && m.DeclaringType.Name == "TestClass")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + Assert.That(rr.Arguments[1].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[1].Value).Variable.Name == "i"); + } + + [Test] + public void ConstructingObjectWithDynamicArgumentWithTwoApplicableConstructorsAndNamedArguments() { + string program = @"using System; +class TestClass { + public TestClass(int arg1, int arg2) {} + public TestClass(string arg1, int arg2) {} + public void TestClass(int a) {} + + void F() { + dynamic obj = null; + int i = 0; + var x = $new TestClass(arg1: obj, arg2: i)$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.ObjectCreation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.Null); + Assert.That(mg.MethodName, Is.EqualTo(".ctor")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 2 && m.Parameters[1].Type.Name == "Int32")); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == ".ctor" && m.DeclaringType.Name == "TestClass")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments[0].Name, Is.EqualTo("arg1")); + Assert.That(rr.Arguments[0].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[0].Value).Variable.Name == "obj"); + Assert.That(rr.Arguments[1].Name, Is.EqualTo("arg2")); + Assert.That(rr.Arguments[1].Value is LocalResolveResult && ((LocalResolveResult)rr.Arguments[1].Value).Variable.Name == "i"); + } + + [Test] + public void ConstructingObjectWithDynamicArgumentWithTwoApplicableConstructorsAndInitializerStatements() { + string program = @"using System; +class TestClass { + public TestClass(int a, int b) {} + public TestClass(string a, int b) {} + + public int A { get; set; } + + void F() { + dynamic obj = null; + int i = 0; + int j = 0; + var x = $new TestClass(obj, i) { A = j }$; + } +}"; + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.ObjectCreation)); + + Assert.That(rr.InitializerStatements.Count, Is.EqualTo(1)); + var or = rr.InitializerStatements[0] as OperatorResolveResult; + Assert.That(or, Is.Not.Null); + Assert.That(or.OperatorType, Is.EqualTo(ExpressionType.Assign)); + var mrr = or.Operands[0] as MemberResolveResult; + Assert.That(mrr, Is.Not.Null); + Assert.That(mrr.TargetResult, Is.InstanceOf()); + Assert.That(mrr.Member.Name, Is.EqualTo("A")); + Assert.That(or.Operands[1], Is.InstanceOf()); + Assert.That(((LocalResolveResult)or.Operands[1]).Variable.Name, Is.EqualTo("j")); + } + + [Test] + public void InitializingBaseWithDynamicArgumentAndOneApplicableConstructor() { + string program = @"using System; +class TestBase { + public TestBase(int a, int b) {} + public TestBase(string a) {} +} + +class TestClass : TestBase { + private static dynamic d; + private static int i; + + public TestClass() : $base(d, i)$ {} +}"; + + var rr = Resolve(program); + + Assert.That(rr.Member.Name, Is.EqualTo(".ctor")); + Assert.That(rr.TargetResult, Is.Null); + Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Member.DeclaringType.Name, Is.EqualTo("TestBase")); + var cr = rr.Arguments[0] as ConversionResolveResult; + Assert.That(cr, Is.Not.Null); + Assert.That(cr.Input is MemberResolveResult && ((MemberResolveResult)cr.Input).Member.Name == "d"); + Assert.That(rr.Arguments[1] is MemberResolveResult && ((MemberResolveResult)rr.Arguments[1]).Member.Name == "i"); + } + + [Test] + public void InitializingBaseWithDynamicArgumentAndTwoApplicableConstructors() { + string program = @"using System; +class TestBase { + public TestBase(int a, int b) {} + public TestBase(string a, int b) {} + public TestBase(string a) {} +} + +class TestClass : TestBase { + private static dynamic d; + private static int i; + + public TestClass() : $base(d, i)$ {} +}"; + + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.ObjectCreation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.Null); + Assert.That(mg.MethodName, Is.EqualTo(".ctor")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 2 && m.Parameters[1].Type.Name == "Int32")); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == ".ctor" && m.DeclaringType.Name == "TestBase")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments[0].Value is MemberResolveResult && ((MemberResolveResult)rr.Arguments[0].Value).Member.Name == "d"); + Assert.That(rr.Arguments[1].Value is MemberResolveResult && ((MemberResolveResult)rr.Arguments[1].Value).Member.Name == "i"); + } + + [Test] + public void ConstructorChainingWithDynamicArgumentAndOneApplicableConstructor() { + string program = @"using System; +class TestClass { + private static dynamic d; + private static int i; + + public TestClass(int a, int b) {} + public TestClass(string a) {} + + public TestClass() : $this(d, i)$ {} +}"; + + var rr = Resolve(program); + + Assert.That(rr.Member.Name, Is.EqualTo(".ctor")); + Assert.That(rr.TargetResult, Is.Null); + Assert.That(((IParameterizedMember)rr.Member).Parameters.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Member.DeclaringType.Name, Is.EqualTo("TestClass")); + var cr = rr.Arguments[0] as ConversionResolveResult; + Assert.That(cr, Is.Not.Null); + Assert.That(cr.Input is MemberResolveResult && ((MemberResolveResult)cr.Input).Member.Name == "d"); + Assert.That(rr.Arguments[1] is MemberResolveResult && ((MemberResolveResult)rr.Arguments[1]).Member.Name == "i"); + } + + [Test] + public void ConstructorChainingWithDynamicArgumentAndTwoApplicableConstructors() { + string program = @"using System; +class TestBase { +} + +class TestClass { + private static dynamic d; + private static int i; + + public TestClass(int a, int b) {} + public TestClass(string a, int b) {} + public TestClass(string a) {} + + public TestClass() : $this(d, i)$ {} +}"; + + var rr = Resolve(program); + Assert.That(rr.InvocationType, Is.EqualTo(DynamicInvocationType.ObjectCreation)); + + var mg = rr.Target as MethodGroupResolveResult; + Assert.That(mg, Is.Not.Null, "Expected a MethodGroup"); + Assert.That(mg.TargetResult, Is.Null); + Assert.That(mg.MethodName, Is.EqualTo(".ctor")); + Assert.That(mg.Methods.All(m => m.Parameters.Count == 2 && m.Parameters[1].Type.Name == "Int32")); + Assert.That(mg.Methods.Select(m => m.Parameters[0].Type.Name), Is.EquivalentTo(new[] { "Int32", "String" })); + Assert.That(mg.Methods.All(m => m.Name == ".ctor" && m.DeclaringType.Name == "TestClass")); + + Assert.That(rr.Arguments.Count, Is.EqualTo(2)); + Assert.That(rr.Arguments[0].Value is MemberResolveResult && ((MemberResolveResult)rr.Arguments[0].Value).Member.Name == "d"); + Assert.That(rr.Arguments[1].Value is MemberResolveResult && ((MemberResolveResult)rr.Arguments[1].Value).Member.Name == "i"); + } } }