You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
644 lines
25 KiB
644 lines
25 KiB
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this |
|
// software and associated documentation files (the "Software"), to deal in the Software |
|
// without restriction, including without limitation the rights to use, copy, modify, merge, |
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
|
// to whom the Software is furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in all copies or |
|
// substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
// DEALINGS IN THE SOFTWARE. |
|
|
|
using System; |
|
using ICSharpCode.NRefactory.Semantics; |
|
using ICSharpCode.NRefactory.TypeSystem; |
|
using NUnit.Framework; |
|
|
|
namespace ICSharpCode.NRefactory.CSharp.Resolver |
|
{ |
|
// assign short name to the fake reflection type |
|
using dynamic = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.Dynamic; |
|
|
|
[TestFixture] |
|
public unsafe class UnaryOperatorTests : ResolverTestBase |
|
{ |
|
CSharpResolver resolver; |
|
|
|
public override void SetUp() |
|
{ |
|
base.SetUp(); |
|
resolver = new CSharpResolver(compilation); |
|
} |
|
|
|
[Test] |
|
public void TestAddressOf() |
|
{ |
|
TestOperator(UnaryOperatorType.AddressOf, MakeResult(typeof(int)), |
|
Conversion.IdentityConversion, typeof(int*)); |
|
|
|
TestOperator(UnaryOperatorType.AddressOf, MakeResult(typeof(byte*)), |
|
Conversion.IdentityConversion, typeof(byte**)); |
|
|
|
TestOperator(UnaryOperatorType.AddressOf, MakeResult(typeof(dynamic)), |
|
Conversion.IdentityConversion, typeof(dynamic)); |
|
} |
|
|
|
[Test] |
|
public void TestDereference() |
|
{ |
|
TestOperator(UnaryOperatorType.Dereference, MakeResult(typeof(int*)), |
|
Conversion.IdentityConversion, typeof(int)); |
|
|
|
TestOperator(UnaryOperatorType.Dereference, MakeResult(typeof(long**)), |
|
Conversion.IdentityConversion, typeof(long*)); |
|
|
|
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.Dereference, MakeResult(typeof(int))).IsError); |
|
|
|
TestOperator(UnaryOperatorType.Dereference, MakeResult(typeof(dynamic)), |
|
Conversion.IdentityConversion, typeof(dynamic)); |
|
} |
|
|
|
[Test] |
|
public void TestIncrementDecrement() |
|
{ |
|
TestOperator(UnaryOperatorType.Increment, MakeResult(typeof(byte)), |
|
Conversion.IdentityConversion, typeof(byte)); |
|
|
|
TestOperator(UnaryOperatorType.PostIncrement, MakeResult(typeof(char)), |
|
Conversion.IdentityConversion, typeof(char)); |
|
|
|
TestOperator(UnaryOperatorType.PostIncrement, MakeResult(typeof(float)), |
|
Conversion.IdentityConversion, typeof(float)); |
|
|
|
TestOperator(UnaryOperatorType.PostIncrement, MakeResult(typeof(decimal)), |
|
Conversion.IdentityConversion, typeof(decimal)); |
|
|
|
TestOperator(UnaryOperatorType.Decrement, MakeResult(typeof(ulong)), |
|
Conversion.IdentityConversion, typeof(ulong)); |
|
|
|
TestOperator(UnaryOperatorType.PostDecrement, MakeResult(typeof(short?)), |
|
Conversion.IdentityConversion, typeof(short?)); |
|
|
|
TestOperator(UnaryOperatorType.PostIncrement, MakeResult(typeof(TypeCode)), |
|
Conversion.IdentityConversion, typeof(TypeCode)); |
|
|
|
TestOperator(UnaryOperatorType.PostIncrement, MakeResult(typeof(TypeCode?)), |
|
Conversion.IdentityConversion, typeof(TypeCode?)); |
|
|
|
TestOperator(UnaryOperatorType.PostIncrement, MakeResult(typeof(dynamic)), |
|
Conversion.IdentityConversion, typeof(dynamic)); |
|
|
|
AssertError(typeof(object), resolver.ResolveUnaryOperator(UnaryOperatorType.Increment, MakeResult(typeof(object)))); |
|
|
|
TestOperator(UnaryOperatorType.Increment, MakeResult(typeof(int*)), |
|
Conversion.IdentityConversion, typeof(int*)); |
|
|
|
TestOperator(UnaryOperatorType.PostDecrement, MakeResult(typeof(uint*)), |
|
Conversion.IdentityConversion, typeof(uint*)); |
|
} |
|
|
|
[Test] |
|
public void TestUnaryPlus() |
|
{ |
|
AssertConstant(1, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((sbyte)1))); |
|
AssertConstant(1, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((byte)1))); |
|
AssertConstant(1, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((short)1))); |
|
AssertConstant(1, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((ushort)1))); |
|
AssertConstant(65, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant('A'))); |
|
AssertConstant(1, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant(1))); |
|
AssertConstant((uint)1, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((uint)1))); |
|
AssertConstant(1L, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((long)1))); |
|
AssertConstant((ulong)1, resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant((ulong)1))); |
|
|
|
TestOperator(UnaryOperatorType.Plus, MakeResult(typeof(dynamic)), |
|
Conversion.IdentityConversion, typeof(dynamic)); |
|
|
|
TestOperator(UnaryOperatorType.Plus, MakeResult(typeof(ushort?)), |
|
Conversion.ImplicitNullableConversion, typeof(int?)); |
|
} |
|
|
|
[Test] |
|
public void TestUnaryMinus() |
|
{ |
|
AssertConstant(-1, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(1))); |
|
AssertConstant(-1L, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant((uint)1))); |
|
AssertConstant(-2147483648L, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(2147483648))); |
|
AssertConstant(-1.0f, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(1.0f))); |
|
AssertConstant(-1.0, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(1.0))); |
|
AssertConstant(1m, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(-1m))); |
|
AssertConstant(-65, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant('A'))); |
|
|
|
TestOperator(UnaryOperatorType.Minus, MakeResult(typeof(dynamic)), |
|
Conversion.IdentityConversion, typeof(dynamic)); |
|
|
|
TestOperator(UnaryOperatorType.Minus, MakeResult(typeof(uint?)), |
|
Conversion.ImplicitNullableConversion, typeof(long?)); |
|
} |
|
|
|
[Test] |
|
public void TestUnaryMinusUncheckedOverflow() |
|
{ |
|
AssertConstant(-2147483648, resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(-2147483648))); |
|
} |
|
|
|
[Test] |
|
public void TestUnaryMinusCheckedOverflow() |
|
{ |
|
AssertError(typeof(int), resolver.WithCheckForOverflow(true).ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(-2147483648))); |
|
} |
|
|
|
[Test] |
|
public void TestBitwiseNot() |
|
{ |
|
AssertConstant(1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant(-2))); |
|
AssertConstant(~'A', resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant('A'))); |
|
AssertConstant(~(sbyte)1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant((sbyte)1))); |
|
AssertConstant(~(byte)1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant((byte)1))); |
|
AssertConstant(~(short)1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant((short)1))); |
|
AssertConstant(~(ushort)1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant((ushort)1))); |
|
AssertConstant(~(uint)1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant((uint)1))); |
|
AssertConstant(~(long)1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant((long)1))); |
|
AssertConstant(~(ulong)1, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant((ulong)1))); |
|
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant(1.0)).IsError); |
|
|
|
TestOperator(UnaryOperatorType.BitNot, MakeResult(typeof(dynamic)), |
|
Conversion.IdentityConversion, typeof(dynamic)); |
|
|
|
TestOperator(UnaryOperatorType.BitNot, MakeResult(typeof(uint)), |
|
Conversion.IdentityConversion, typeof(uint)); |
|
|
|
TestOperator(UnaryOperatorType.BitNot, MakeResult(typeof(sbyte)), |
|
Conversion.ImplicitNumericConversion, typeof(int)); |
|
|
|
TestOperator(UnaryOperatorType.BitNot, MakeResult(typeof(ushort?)), |
|
Conversion.ImplicitNullableConversion, typeof(int?)); |
|
} |
|
|
|
[Test] |
|
public void TestLogicalNot() |
|
{ |
|
AssertConstant(true, resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeConstant(false))); |
|
AssertConstant(false, resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeConstant(true))); |
|
|
|
TestOperator(UnaryOperatorType.Not, MakeResult(typeof(dynamic)), |
|
Conversion.IdentityConversion, typeof(dynamic)); |
|
|
|
TestOperator(UnaryOperatorType.Not, MakeResult(typeof(bool)), |
|
Conversion.IdentityConversion, typeof(bool)); |
|
|
|
TestOperator(UnaryOperatorType.Not, MakeResult(typeof(bool?)), |
|
Conversion.IdentityConversion, typeof(bool?)); |
|
} |
|
|
|
[Test] |
|
public void TestInvalidUnaryOperatorsOnEnum() |
|
{ |
|
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeConstant(StringComparison.Ordinal)).IsError); |
|
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeConstant(StringComparison.Ordinal)).IsError); |
|
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeConstant(StringComparison.Ordinal)).IsError); |
|
|
|
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeResult(typeof(StringComparison))).IsError); |
|
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeResult(typeof(StringComparison))).IsError); |
|
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeResult(typeof(StringComparison))).IsError); |
|
|
|
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.Not, MakeResult(typeof(StringComparison?))).IsError); |
|
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.Plus, MakeResult(typeof(StringComparison?))).IsError); |
|
Assert.IsTrue(resolver.ResolveUnaryOperator(UnaryOperatorType.Minus, MakeResult(typeof(StringComparison?))).IsError); |
|
} |
|
|
|
[Test] |
|
public void TestBitwiseNotOnEnum() |
|
{ |
|
AssertConstant(~StringComparison.Ordinal, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant(StringComparison.Ordinal))); |
|
AssertConstant(~StringComparison.CurrentCultureIgnoreCase, resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeConstant(StringComparison.CurrentCultureIgnoreCase))); |
|
AssertType(typeof(StringComparison), resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeResult(typeof(StringComparison)))); |
|
AssertType(typeof(StringComparison?), resolver.ResolveUnaryOperator(UnaryOperatorType.BitNot, MakeResult(typeof(StringComparison?)))); |
|
} |
|
|
|
[Ignore("Broken on mcs")] |
|
[Test] |
|
public void IntMinValue() |
|
{ |
|
// int: |
|
AssertConstant(-2147483648, Resolve("class A { object x = $-2147483648$; }")); |
|
AssertConstant(-/**/2147483648, Resolve("class A { object x = $-/**/2147483648$; }")); |
|
// long: |
|
AssertConstant(-2147483648L, Resolve("class A { object x = $-2147483648L$; }")); |
|
AssertConstant(-(2147483648), Resolve("class A { object x = $-(2147483648)$; }")); |
|
} |
|
|
|
[Test] |
|
public void LongMinValue() |
|
{ |
|
// long: |
|
AssertConstant(-9223372036854775808, Resolve("class A { object x = $-9223372036854775808$; }")); |
|
// compiler error: |
|
AssertError(typeof(ulong), Resolve("class A { object x = $-(9223372036854775808)$; }")); |
|
} |
|
|
|
[Test] |
|
public void IsLiftedProperty() |
|
{ |
|
string program = @" |
|
class Test { |
|
static void Inc() { |
|
int? a = 0; |
|
a = $-a$; |
|
} |
|
}"; |
|
var irr = Resolve<OperatorResolveResult>(program); |
|
Assert.IsFalse(irr.IsError); |
|
Assert.IsTrue(irr.IsLiftedOperator); |
|
} |
|
|
|
[Test] |
|
public void UShortEnumNegation() |
|
{ |
|
string program = @" |
|
class Test { |
|
enum UShortEnum : ushort { Three = 3 } |
|
static void Inc() { |
|
checked { // even in checked context, the implicit cast back to enum is unchecked |
|
var a = $~UShortEnum.Three$; |
|
} |
|
} |
|
}"; |
|
var rr = Resolve<ConstantResolveResult>(program); |
|
Assert.IsFalse(rr.IsError); |
|
Assert.AreEqual(unchecked( (ushort)~3 ), rr.ConstantValue); |
|
} |
|
|
|
[Test] |
|
public void Await() { |
|
string program = @" |
|
using System; |
|
class MyAwaiter { |
|
public bool IsCompleted { get { return false; } } |
|
public void OnCompleted(Action continuation) {} |
|
public int GetResult() { return 0; } |
|
} |
|
class MyAwaitable { |
|
public MyAwaiter GetAwaiter() { return null; } |
|
public MyAwaiter GetAwaiter(int i) { return null; } |
|
} |
|
public class C { |
|
public async void M() { |
|
MyAwaitable x = null; |
|
int i = $await x$; |
|
} |
|
}"; |
|
|
|
var rr = Resolve<AwaitResolveResult>(program); |
|
Assert.IsFalse(rr.IsError); |
|
Assert.IsTrue(rr.Type.IsKnownType(KnownTypeCode.Int32)); |
|
Assert.IsInstanceOf<CSharpInvocationResolveResult>(rr.GetAwaiterInvocation); |
|
var getAwaiterInvocation = (CSharpInvocationResolveResult)rr.GetAwaiterInvocation; |
|
Assert.IsFalse(rr.GetAwaiterInvocation.IsError); |
|
Assert.AreEqual(0, getAwaiterInvocation.Arguments.Count); |
|
Assert.AreEqual("MyAwaitable.GetAwaiter", getAwaiterInvocation.Member.FullName); |
|
Assert.AreEqual(0, getAwaiterInvocation.Member.Parameters.Count); |
|
|
|
Assert.AreEqual("MyAwaiter", rr.AwaiterType.FullName); |
|
|
|
Assert.IsNotNull(rr.IsCompletedProperty); |
|
Assert.AreEqual("MyAwaiter.IsCompleted", rr.IsCompletedProperty.FullName); |
|
|
|
Assert.IsNotNull(rr.OnCompletedMethod); |
|
Assert.AreEqual("MyAwaiter.OnCompleted", rr.OnCompletedMethod.FullName); |
|
|
|
Assert.IsNotNull(rr.GetResultMethod); |
|
Assert.AreEqual("MyAwaiter.GetResult", rr.GetResultMethod.FullName); |
|
} |
|
|
|
[Test] |
|
public void AwaitWhenGetAwaiterIsAnExtensionMethod() { |
|
string program = @" |
|
using System; |
|
namespace N { |
|
class MyAwaiter { |
|
public bool IsCompleted { get { return false; } } |
|
public void OnCompleted(Action continuation) {} |
|
public int GetResult() { return 0; } |
|
} |
|
class MyAwaitable { |
|
} |
|
static class MyAwaitableExtensions { |
|
public static MyAwaiter GetAwaiter(this MyAwaitable x) { return null; } |
|
} |
|
public class C { |
|
public async void M() { |
|
MyAwaitable x = null; |
|
int i = $await x$; |
|
} |
|
} |
|
}"; |
|
|
|
var rr = Resolve<AwaitResolveResult>(program); |
|
Assert.IsFalse(rr.IsError); |
|
Assert.IsTrue(rr.Type.IsKnownType(KnownTypeCode.Int32)); |
|
Assert.IsInstanceOf<CSharpInvocationResolveResult>(rr.GetAwaiterInvocation); |
|
var getAwaiterInvocation = (CSharpInvocationResolveResult)rr.GetAwaiterInvocation; |
|
Assert.IsFalse(rr.GetAwaiterInvocation.IsError); |
|
Assert.AreEqual(1, getAwaiterInvocation.Arguments.Count); |
|
Assert.AreEqual("N.MyAwaitableExtensions.GetAwaiter", getAwaiterInvocation.Member.FullName); |
|
Assert.AreEqual(1, getAwaiterInvocation.Member.Parameters.Count); |
|
Assert.IsTrue(getAwaiterInvocation.Arguments[0] is LocalResolveResult && ((LocalResolveResult)getAwaiterInvocation.Arguments[0]).Variable.Name == "x"); |
|
|
|
Assert.AreEqual("N.MyAwaiter", rr.AwaiterType.FullName); |
|
|
|
Assert.IsNotNull(rr.IsCompletedProperty); |
|
Assert.AreEqual("N.MyAwaiter.IsCompleted", rr.IsCompletedProperty.FullName); |
|
|
|
Assert.IsNotNull(rr.OnCompletedMethod); |
|
Assert.AreEqual("N.MyAwaiter.OnCompleted", rr.OnCompletedMethod.FullName); |
|
|
|
Assert.IsNotNull(rr.GetResultMethod); |
|
Assert.AreEqual("N.MyAwaiter.GetResult", rr.GetResultMethod.FullName); |
|
} |
|
|
|
[Test, Ignore("TODO: MS C# (at least the RC version) refuses to use default values in GetAwaiter(). I do not know, however, if this is by design, and I could not find a simple, nice way to do the implementation")] |
|
public void GetAwaiterMethodWithDefaultArgumentCannotBeUsed() { |
|
string program = @" |
|
using System; |
|
class MyAwaiter { |
|
public bool IsCompleted { get { return false; } } |
|
public void OnCompleted(Action continuation) {} |
|
public int GetResult() { return 0; } |
|
} |
|
class MyAwaitable { |
|
public MyAwaiter GetAwaiter(int i = 0) { return null; } |
|
} |
|
public class C { |
|
public async void M() { |
|
MyAwaitable x = null; |
|
int i = $await x$; |
|
} |
|
}"; |
|
|
|
var rr = Resolve<AwaitResolveResult>(program); |
|
Assert.IsFalse(rr.IsError); |
|
Assert.AreEqual(SpecialType.UnknownType, rr.Type); |
|
Assert.IsInstanceOf<CSharpInvocationResolveResult>(rr.GetAwaiterInvocation); |
|
Assert.IsTrue(rr.GetAwaiterInvocation.IsError); |
|
|
|
Assert.AreEqual(rr.AwaiterType, SpecialType.UnknownType); |
|
|
|
Assert.IsNull(rr.IsCompletedProperty); |
|
Assert.IsNull(rr.OnCompletedMethod); |
|
Assert.IsNull(rr.GetResultMethod); |
|
} |
|
|
|
[Test, Ignore("TODO: MS C# (at least the RC version) refuses to use default values in GetAwaiter(). I do not know, however, if this is by design, and I could not find a simple, nice way to do the implementation")] |
|
public void GetAwaiterMethodWithDefaultArgumentHidesExtensionMethodAndResultsInError() { |
|
string program = @" |
|
using System; |
|
namespace N { |
|
class MyAwaiter { |
|
public bool IsCompleted { get { return false; } } |
|
public void OnCompleted(Action continuation) {} |
|
public int GetResult() { return 0; } |
|
} |
|
class MyAwaitable { |
|
public MyAwaiter GetAwaiter(int i = 0) { return null; } |
|
} |
|
static class MyAwaitableExtensions { |
|
public static MyAwaiter GetAwaiter(this MyAwaitable x) { return null; } |
|
} |
|
public class C { |
|
public async void M() { |
|
MyAwaitable x = null; |
|
int i = $await x$; |
|
} |
|
} |
|
}"; |
|
|
|
var rr = Resolve<AwaitResolveResult>(program); |
|
Assert.IsFalse(rr.IsError); |
|
Assert.AreEqual(SpecialType.UnknownType, rr.Type); |
|
Assert.IsInstanceOf<CSharpInvocationResolveResult>(rr.GetAwaiterInvocation); |
|
Assert.IsTrue(rr.GetAwaiterInvocation.IsError); |
|
|
|
Assert.AreEqual(rr.AwaiterType, SpecialType.UnknownType); |
|
|
|
Assert.IsNull(rr.IsCompletedProperty); |
|
Assert.IsNull(rr.OnCompletedMethod); |
|
Assert.IsNull(rr.GetResultMethod); |
|
} |
|
|
|
[Test] |
|
public void GetAwaiterMethodWithArgumentDoesNotHideExtensionMethod() { |
|
string program = @" |
|
using System; |
|
namespace N { |
|
class MyAwaiter { |
|
public bool IsCompleted { get { return false; } } |
|
public void OnCompleted(Action continuation) {} |
|
public int GetResult() { return 0; } |
|
} |
|
class MyAwaitable { |
|
public static MyAwaiter GetAwaiter(int i) { return null; } |
|
} |
|
static class MyAwaitableExtensions { |
|
public static MyAwaiter GetAwaiter(this MyAwaitable x) { return null; } |
|
} |
|
public class C { |
|
public async void M() { |
|
MyAwaitable x = null; |
|
int i = $await x$; |
|
} |
|
} |
|
}"; |
|
|
|
var rr = Resolve<AwaitResolveResult>(program); |
|
Assert.IsFalse(rr.IsError); |
|
Assert.IsTrue(rr.Type.IsKnownType(KnownTypeCode.Int32)); |
|
Assert.IsInstanceOf<CSharpInvocationResolveResult>(rr.GetAwaiterInvocation); |
|
var getAwaiterInvocation = (CSharpInvocationResolveResult)rr.GetAwaiterInvocation; |
|
Assert.IsFalse(rr.GetAwaiterInvocation.IsError); |
|
Assert.AreEqual(1, getAwaiterInvocation.Arguments.Count); |
|
Assert.AreEqual("N.MyAwaitableExtensions.GetAwaiter", getAwaiterInvocation.Member.FullName); |
|
Assert.AreEqual(1, getAwaiterInvocation.Member.Parameters.Count); |
|
Assert.IsTrue(getAwaiterInvocation.Arguments[0] is LocalResolveResult && ((LocalResolveResult)getAwaiterInvocation.Arguments[0]).Variable.Name == "x"); |
|
|
|
Assert.AreEqual("N.MyAwaiter", rr.AwaiterType.FullName); |
|
|
|
Assert.IsNotNull(rr.IsCompletedProperty); |
|
Assert.AreEqual("N.MyAwaiter.IsCompleted", rr.IsCompletedProperty.FullName); |
|
|
|
Assert.IsNotNull(rr.OnCompletedMethod); |
|
Assert.AreEqual("N.MyAwaiter.OnCompleted", rr.OnCompletedMethod.FullName); |
|
|
|
Assert.IsNotNull(rr.GetResultMethod); |
|
Assert.AreEqual("N.MyAwaiter.GetResult", rr.GetResultMethod.FullName); |
|
} |
|
|
|
[Test] |
|
public void AwaiterWithNoSuitableGetResult() { |
|
string program = @" |
|
using System; |
|
namespace N { |
|
class MyAwaiter { |
|
public bool IsCompleted { get { return false; } } |
|
public void OnCompleted(Action continuation) {} |
|
public int GetResult(int i) { return 0; } |
|
} |
|
class MyAwaitable { |
|
public static MyAwaiter GetAwaiter(int i) { return null; } |
|
} |
|
static class MyAwaitableExtensions { |
|
public static MyAwaiter GetAwaiter(this MyAwaitable x) { return null; } |
|
} |
|
public class C { |
|
public async void M() { |
|
MyAwaitable x = null; |
|
int i = $await x$; |
|
} |
|
} |
|
}"; |
|
|
|
var rr = Resolve<AwaitResolveResult>(program); |
|
Assert.IsTrue(rr.IsError); |
|
Assert.AreEqual(SpecialType.UnknownType, rr.Type); |
|
Assert.IsInstanceOf<CSharpInvocationResolveResult>(rr.GetAwaiterInvocation); |
|
var getAwaiterInvocation = (CSharpInvocationResolveResult)rr.GetAwaiterInvocation; |
|
Assert.IsFalse(rr.GetAwaiterInvocation.IsError); |
|
Assert.AreEqual(1, getAwaiterInvocation.Arguments.Count); |
|
Assert.AreEqual("N.MyAwaitableExtensions.GetAwaiter", getAwaiterInvocation.Member.FullName); |
|
Assert.AreEqual(1, getAwaiterInvocation.Member.Parameters.Count); |
|
Assert.IsTrue(getAwaiterInvocation.Arguments[0] is LocalResolveResult && ((LocalResolveResult)getAwaiterInvocation.Arguments[0]).Variable.Name == "x"); |
|
|
|
Assert.AreEqual("N.MyAwaiter", rr.AwaiterType.FullName); |
|
|
|
Assert.IsNotNull(rr.IsCompletedProperty); |
|
Assert.AreEqual("N.MyAwaiter.IsCompleted", rr.IsCompletedProperty.FullName); |
|
|
|
Assert.IsNotNull(rr.OnCompletedMethod); |
|
Assert.AreEqual("N.MyAwaiter.OnCompleted", rr.OnCompletedMethod.FullName); |
|
|
|
Assert.IsNull(rr.GetResultMethod); |
|
} |
|
|
|
[Test] |
|
public void AwaiterWithNoIsCompletedProperty() { |
|
string program = @" |
|
using System; |
|
namespace N { |
|
class MyAwaiter { |
|
public bool IsCompleted() { return false; } |
|
public void OnCompleted(Action continuation) {} |
|
public int GetResult(int i) { return 0; } |
|
} |
|
class MyAwaitable { |
|
public static MyAwaiter GetAwaiter(int i) { return null; } |
|
} |
|
static class MyAwaitableExtensions { |
|
public static MyAwaiter GetAwaiter(this MyAwaitable x) { return null; } |
|
} |
|
public class C { |
|
public async void M() { |
|
MyAwaitable x = null; |
|
int i = $await x$; |
|
} |
|
} |
|
}"; |
|
|
|
var rr = Resolve<AwaitResolveResult>(program); |
|
Assert.IsTrue(rr.IsError); |
|
Assert.AreEqual(SpecialType.UnknownType, rr.Type); |
|
Assert.IsInstanceOf<CSharpInvocationResolveResult>(rr.GetAwaiterInvocation); |
|
var getAwaiterInvocation = (CSharpInvocationResolveResult)rr.GetAwaiterInvocation; |
|
Assert.IsFalse(rr.GetAwaiterInvocation.IsError); |
|
Assert.AreEqual(1, getAwaiterInvocation.Arguments.Count); |
|
Assert.AreEqual("N.MyAwaitableExtensions.GetAwaiter", getAwaiterInvocation.Member.FullName); |
|
Assert.AreEqual(1, getAwaiterInvocation.Member.Parameters.Count); |
|
Assert.IsTrue(getAwaiterInvocation.Arguments[0] is LocalResolveResult && ((LocalResolveResult)getAwaiterInvocation.Arguments[0]).Variable.Name == "x"); |
|
|
|
Assert.AreEqual("N.MyAwaiter", rr.AwaiterType.FullName); |
|
|
|
Assert.IsNull(rr.IsCompletedProperty); |
|
|
|
Assert.IsNotNull(rr.OnCompletedMethod); |
|
Assert.AreEqual("N.MyAwaiter.OnCompleted", rr.OnCompletedMethod.FullName); |
|
|
|
Assert.IsNull(rr.GetResultMethod); |
|
} |
|
|
|
[Test] |
|
public void AwaiterWithNoOnCompletedMethodWithSuitableSignature() { |
|
string program = @" |
|
using System; |
|
class MyAwaiter { |
|
public bool IsCompleted { get { return false; } } |
|
public void OnCompleted(Func<int> continuation) {} |
|
public int GetResult() { return 0; } |
|
} |
|
class MyAwaitable { |
|
public MyAwaiter GetAwaiter() { return null; } |
|
public MyAwaiter GetAwaiter(int i) { return null; } |
|
} |
|
public class C { |
|
public async void M() { |
|
MyAwaitable x = null; |
|
int i = $await x$; |
|
} |
|
}"; |
|
|
|
var rr = Resolve<AwaitResolveResult>(program); |
|
Assert.IsTrue(rr.IsError); |
|
Assert.IsTrue(rr.Type.IsKnownType(KnownTypeCode.Int32)); |
|
Assert.IsInstanceOf<CSharpInvocationResolveResult>(rr.GetAwaiterInvocation); |
|
var getAwaiterInvocation = (CSharpInvocationResolveResult)rr.GetAwaiterInvocation; |
|
Assert.IsFalse(rr.GetAwaiterInvocation.IsError); |
|
Assert.AreEqual(0, getAwaiterInvocation.Arguments.Count); |
|
Assert.AreEqual("MyAwaitable.GetAwaiter", getAwaiterInvocation.Member.FullName); |
|
Assert.AreEqual(0, getAwaiterInvocation.Member.Parameters.Count); |
|
|
|
Assert.AreEqual("MyAwaiter", rr.AwaiterType.FullName); |
|
|
|
Assert.IsNotNull(rr.IsCompletedProperty); |
|
Assert.AreEqual("MyAwaiter.IsCompleted", rr.IsCompletedProperty.FullName); |
|
|
|
Assert.IsNull(rr.OnCompletedMethod); |
|
|
|
Assert.IsNotNull(rr.GetResultMethod); |
|
Assert.AreEqual("MyAwaiter.GetResult", rr.GetResultMethod.FullName); |
|
} |
|
|
|
[Test] |
|
public void AwaitDynamic() { |
|
string program = @" |
|
public class C { |
|
public async void M() { |
|
dynamic x = null; |
|
int i = $await x$; |
|
} |
|
}"; |
|
|
|
var rr = Resolve<AwaitResolveResult>(program); |
|
Assert.IsFalse(rr.IsError); |
|
Assert.AreEqual(SpecialType.Dynamic, rr.Type); |
|
Assert.IsInstanceOf<DynamicInvocationResolveResult>(rr.GetAwaiterInvocation); |
|
var getAwaiterInvocation = (DynamicInvocationResolveResult)rr.GetAwaiterInvocation; |
|
Assert.IsFalse(rr.GetAwaiterInvocation.IsError); |
|
Assert.AreEqual(DynamicInvocationType.Invocation, getAwaiterInvocation.InvocationType); |
|
Assert.AreEqual(0, getAwaiterInvocation.Arguments.Count); |
|
Assert.IsInstanceOf<DynamicMemberResolveResult>(getAwaiterInvocation.Target); |
|
var target = (DynamicMemberResolveResult)getAwaiterInvocation.Target; |
|
Assert.IsInstanceOf<LocalResolveResult>(target.Target); |
|
Assert.AreEqual("GetAwaiter", target.Member); |
|
|
|
Assert.AreEqual(SpecialType.Dynamic, rr.AwaiterType); |
|
|
|
Assert.IsNull(rr.IsCompletedProperty); |
|
Assert.IsNull(rr.OnCompletedMethod); |
|
Assert.IsNull(rr.GetResultMethod); |
|
} |
|
} |
|
}
|
|
|