// Copyright (c) 2010-2013 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 System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.Tests.TypeSystem; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests.Semantics { // assign short names to the fake reflection types using C = Conversion; using dynamic = ICSharpCode.Decompiler.TypeSystem.ReflectionHelper.Dynamic; using nint = ICSharpCode.Decompiler.TypeSystem.ReflectionHelper.NInt; using nuint = ICSharpCode.Decompiler.TypeSystem.ReflectionHelper.NUInt; using Null = ICSharpCode.Decompiler.TypeSystem.ReflectionHelper.Null; [TestFixture, Parallelizable(ParallelScope.All)] public unsafe class ConversionTest { CSharpConversions conversions; ICompilation compilation; [OneTimeSetUp] public void SetUp() { compilation = new SimpleCompilation(TypeSystemLoaderTests.TestAssembly, TypeSystemLoaderTests.Mscorlib, TypeSystemLoaderTests.SystemCore); conversions = new CSharpConversions(compilation); } Conversion ImplicitConversion(Type from, Type to) { IType from2 = compilation.FindType(from); IType to2 = compilation.FindType(to); return conversions.ImplicitConversion(from2, to2); } Conversion ExplicitConversion(Type from, Type to) { IType from2 = compilation.FindType(from); IType to2 = compilation.FindType(to); return conversions.ExplicitConversion(from2, to2); } [Test] public void IdentityConversions() { Assert.That(ImplicitConversion(typeof(char), typeof(char)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(string), typeof(string)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(object), typeof(object)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(bool), typeof(char)), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(SpecialType.Dynamic, SpecialType.Dynamic), Is.EqualTo(C.IdentityConversion)); Assert.That(conversions.ImplicitConversion(SpecialType.UnknownType, SpecialType.UnknownType), Is.EqualTo(C.IdentityConversion)); Assert.That(conversions.ImplicitConversion(SpecialType.NullType, SpecialType.NullType), Is.EqualTo(C.IdentityConversion)); } [Test] public void DynamicIdentityConversions() { Assert.That(ImplicitConversion(typeof(object), typeof(dynamic)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(dynamic), typeof(object)), Is.EqualTo(C.IdentityConversion)); } [Test] public void ComplexDynamicIdentityConversions() { Assert.That(ImplicitConversion(typeof(List<object>), typeof(List<dynamic>)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(List<dynamic>), typeof(List<object>)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(List<string>), typeof(List<dynamic>)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(List<dynamic>), typeof(List<string>)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(List<List<dynamic>[]>), typeof(List<List<object>[]>)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(List<List<object>[]>), typeof(List<List<dynamic>[]>)), Is.EqualTo(C.IdentityConversion)); Assert.That(ImplicitConversion(typeof(List<List<object>[,]>), typeof(List<List<dynamic>[]>)), Is.EqualTo(C.None)); } [Test] public void TupleIdentityConversions() { var intType = compilation.FindType(typeof(int)); var stringType = compilation.FindType(typeof(string)); Assert.That(conversions.ImplicitConversion( new TupleType(compilation, ImmutableArray.Create(intType, stringType), ImmutableArray.Create("a", "b")), new TupleType(compilation, ImmutableArray.Create(intType, stringType), ImmutableArray.Create("a", "c"))), Is.EqualTo(C.IdentityConversion)); Assert.That(conversions.ImplicitConversion( new TupleType(compilation, ImmutableArray.Create(intType, stringType), ImmutableArray.Create("a", "b")), new TupleType(compilation, ImmutableArray.Create(stringType, intType), ImmutableArray.Create("a", "b"))), Is.EqualTo(C.None)); } [Test] public void TupleConversions() { Assert.That( ImplicitConversion(typeof((int, string)), typeof((long, object))), Is.EqualTo(C.TupleConversion(ImmutableArray.Create(C.ImplicitNumericConversion, C.ImplicitReferenceConversion)))); Assert.That( ImplicitConversion(typeof(ValueTuple<float>), typeof(ValueTuple<double>)), Is.EqualTo(C.TupleConversion(ImmutableArray.Create(C.ImplicitNumericConversion)))); } [Test] public void PrimitiveConversions() { Assert.That(ImplicitConversion(typeof(char), typeof(ushort)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ImplicitConversion(typeof(byte), typeof(char)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(int), typeof(long)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ImplicitConversion(typeof(long), typeof(int)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(int), typeof(float)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ImplicitConversion(typeof(bool), typeof(float)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(float), typeof(double)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ImplicitConversion(typeof(float), typeof(decimal)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(char), typeof(long)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ImplicitConversion(typeof(uint), typeof(long)), Is.EqualTo(C.ImplicitNumericConversion)); } [Test] public void EnumerationConversion() { ResolveResult zero = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 0); ResolveResult one = new ConstantResolveResult(compilation.FindType(KnownTypeCode.Int32), 1); C implicitEnumerationConversion = C.EnumerationConversion(true, false); Assert.That(conversions.ImplicitConversion(zero, compilation.FindType(typeof(StringComparison))), Is.EqualTo(implicitEnumerationConversion)); Assert.That(conversions.ImplicitConversion(one, compilation.FindType(typeof(StringComparison))), Is.EqualTo(C.None)); } [Test] public void NullableConversions() { Assert.That(ImplicitConversion(typeof(char), typeof(ushort?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(byte), typeof(char?)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(int), typeof(long?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(long), typeof(int?)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(int), typeof(float?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(bool), typeof(float?)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(float), typeof(double?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(float), typeof(decimal?)), Is.EqualTo(C.None)); } [Test] public void NullableConversions2() { Assert.That(ImplicitConversion(typeof(char?), typeof(ushort?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(byte?), typeof(char?)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(int?), typeof(long?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(long?), typeof(int?)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(int?), typeof(float?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(bool?), typeof(float?)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(float?), typeof(double?)), Is.EqualTo(C.ImplicitLiftedNumericConversion)); Assert.That(ImplicitConversion(typeof(float?), typeof(decimal?)), Is.EqualTo(C.None)); } [Test] public void NullLiteralConversions() { Assert.That(ImplicitConversion(typeof(Null), typeof(int?)), Is.EqualTo(C.NullLiteralConversion)); Assert.That(ImplicitConversion(typeof(Null), typeof(char?)), Is.EqualTo(C.NullLiteralConversion)); Assert.That(ImplicitConversion(typeof(Null), typeof(int)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(Null), typeof(object)), Is.EqualTo(C.NullLiteralConversion)); Assert.That(ImplicitConversion(typeof(Null), typeof(dynamic)), Is.EqualTo(C.NullLiteralConversion)); Assert.That(ImplicitConversion(typeof(Null), typeof(string)), Is.EqualTo(C.NullLiteralConversion)); Assert.That(ImplicitConversion(typeof(Null), typeof(int[])), Is.EqualTo(C.NullLiteralConversion)); } [Test] public void SimpleReferenceConversions() { Assert.That(ImplicitConversion(typeof(string), typeof(object)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(BitArray), typeof(ICollection)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(IList), typeof(IEnumerable)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(object), typeof(string)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(ICollection), typeof(BitArray)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(IEnumerable), typeof(IList)), Is.EqualTo(C.None)); } [Test] public void ConversionToDynamic() { Assert.That(ImplicitConversion(typeof(string), typeof(dynamic)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(int), typeof(dynamic)), Is.EqualTo(C.BoxingConversion)); } [Test] public void ConversionFromDynamic() { // There is no conversion from the type 'dynamic' to other types (except the identity conversion to object). // Such conversions only exists from dynamic expression. // This is an important distinction for type inference (see TypeInferenceTests.IEnumerableCovarianceWithDynamic) Assert.That(ImplicitConversion(typeof(dynamic), typeof(string)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(dynamic), typeof(int)), Is.EqualTo(C.None)); var dynamicRR = new ResolveResult(SpecialType.Dynamic); Assert.That(conversions.ImplicitConversion(dynamicRR, compilation.FindType(typeof(string))), Is.EqualTo(C.ImplicitDynamicConversion)); Assert.That(conversions.ImplicitConversion(dynamicRR, compilation.FindType(typeof(int))), Is.EqualTo(C.ImplicitDynamicConversion)); } [Test] public void ParameterizedTypeConversions() { Assert.That(ImplicitConversion(typeof(List<string>), typeof(ICollection<string>)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(IList<string>), typeof(ICollection<string>)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(List<string>), typeof(ICollection<object>)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(IList<string>), typeof(ICollection<object>)), Is.EqualTo(C.None)); } [Test] public void ArrayConversions() { Assert.That(ImplicitConversion(typeof(string[]), typeof(object[])), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(string[,]), typeof(object[,])), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(string[]), typeof(object[,])), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(object[]), typeof(string[])), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(string[]), typeof(IList<string>)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(string[,]), typeof(IList<string>)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(string[]), typeof(IList<object>)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(string[]), typeof(Array)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(string[]), typeof(ICloneable)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(Array), typeof(string[])), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(object), typeof(object[])), Is.EqualTo(C.None)); } [Test] public void VarianceConversions() { Assert.That(ImplicitConversion(typeof(List<string>), typeof(IEnumerable<object>)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(List<object>), typeof(IEnumerable<string>)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(IEnumerable<string>), typeof(IEnumerable<object>)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(ICollection<string>), typeof(ICollection<object>)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(Comparer<object>), typeof(IComparer<string>)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(Comparer<object>), typeof(IComparer<Array>)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(Comparer<object>), typeof(Comparer<string>)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(List<object>), typeof(IEnumerable<string>)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(IEnumerable<string>), typeof(IEnumerable<object>)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(Func<ICollection, ICollection>), typeof(Func<IList, IEnumerable>)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(Func<IEnumerable, IList>), typeof(Func<ICollection, ICollection>)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(ImplicitConversion(typeof(Func<ICollection, ICollection>), typeof(Func<IEnumerable, IList>)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(Func<IList, IEnumerable>), typeof(Func<ICollection, ICollection>)), Is.EqualTo(C.None)); } [Test] public void ImplicitPointerConversion() { Assert.That(ImplicitConversion(typeof(Null), typeof(int*)), Is.EqualTo(C.ImplicitPointerConversion)); Assert.That(ImplicitConversion(typeof(int*), typeof(void*)), Is.EqualTo(C.ImplicitPointerConversion)); } [Test] public void NoConversionFromPointerTypeToObject() { Assert.That(ImplicitConversion(typeof(int*), typeof(object)), Is.EqualTo(C.None)); Assert.That(ImplicitConversion(typeof(int*), typeof(dynamic)), Is.EqualTo(C.None)); } [Test] public void ConversionToNInt() { // Test based on the table in https://github.com/dotnet/csharplang/blob/master/proposals/native-integers.md Assert.That(ExplicitConversion(typeof(object), typeof(nint)), Is.EqualTo(C.UnboxingConversion)); Assert.That(ExplicitConversion(typeof(void*), typeof(nint)), Is.EqualTo(C.ExplicitPointerConversion)); Assert.That(ExplicitConversion(typeof(sbyte), typeof(nint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(byte), typeof(nint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(short), typeof(nint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(ushort), typeof(nint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(int), typeof(nint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(uint), typeof(nint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(long), typeof(nint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(ulong), typeof(nint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(char), typeof(nint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(float), typeof(nint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(double), typeof(nint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(decimal), typeof(nint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(IntPtr), typeof(nint)), Is.EqualTo(C.IdentityConversion)); Assert.That(ExplicitConversion(typeof(UIntPtr), typeof(nint)), Is.EqualTo(C.None)); } [Test] public void ConversionToNUInt() { // Test based on the table in https://github.com/dotnet/csharplang/blob/master/proposals/native-integers.md Assert.That(ExplicitConversion(typeof(object), typeof(nuint)), Is.EqualTo(C.UnboxingConversion)); Assert.That(ExplicitConversion(typeof(void*), typeof(nuint)), Is.EqualTo(C.ExplicitPointerConversion)); Assert.That(ExplicitConversion(typeof(sbyte), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(byte), typeof(nuint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(short), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(ushort), typeof(nuint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(int), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(uint), typeof(nuint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(long), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(ulong), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(char), typeof(nuint)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(float), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(double), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(decimal), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(IntPtr), typeof(nuint)), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(UIntPtr), typeof(nuint)), Is.EqualTo(C.IdentityConversion)); } [Test] public void ConversionFromNInt() { // Test based on the table in https://github.com/dotnet/csharplang/blob/master/proposals/native-integers.md Assert.That(ExplicitConversion(typeof(nint), typeof(object)), Is.EqualTo(C.BoxingConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(void*)), Is.EqualTo(C.ExplicitPointerConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(nuint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(sbyte)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(byte)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(short)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(ushort)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(int)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(uint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(long)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(ulong)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(char)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(float)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(double)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(decimal)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(IntPtr)), Is.EqualTo(C.IdentityConversion)); Assert.That(ExplicitConversion(typeof(nint), typeof(UIntPtr)), Is.EqualTo(C.None)); } [Test] public void ConversionFromNUInt() { // Test based on the table in https://github.com/dotnet/csharplang/blob/master/proposals/native-integers.md Assert.That(ExplicitConversion(typeof(nuint), typeof(object)), Is.EqualTo(C.BoxingConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(void*)), Is.EqualTo(C.ExplicitPointerConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(nint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(sbyte)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(byte)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(short)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(ushort)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(int)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(uint)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(long)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(ulong)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(char)), Is.EqualTo(C.ExplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(float)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(double)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(decimal)), Is.EqualTo(C.ImplicitNumericConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(IntPtr)), Is.EqualTo(C.None)); Assert.That(ExplicitConversion(typeof(nuint), typeof(UIntPtr)), Is.EqualTo(C.IdentityConversion)); } [Test] public void NIntEnumConversion() { var explicitEnumConversion = C.EnumerationConversion(isImplicit: false, isLifted: false); Assert.That(ExplicitConversion(typeof(nint), typeof(StringComparison)), Is.EqualTo(explicitEnumConversion)); Assert.That(ExplicitConversion(typeof(nuint), typeof(StringComparison)), Is.EqualTo(explicitEnumConversion)); Assert.That(ExplicitConversion(typeof(StringComparison), typeof(nint)), Is.EqualTo(explicitEnumConversion)); Assert.That(ExplicitConversion(typeof(StringComparison), typeof(nuint)), Is.EqualTo(explicitEnumConversion)); } [Test] public void IntegerLiteralToNIntConversions() { Assert.That(IntegerLiteralConversion(0, typeof(nint))); Assert.That(IntegerLiteralConversion(-1, typeof(nint))); Assert.That(!IntegerLiteralConversion(uint.MaxValue, typeof(nint))); Assert.That(!IntegerLiteralConversion(long.MaxValue, typeof(nint))); } [Test] public void IntegerLiteralToNUIntConversions() { Assert.That(IntegerLiteralConversion(0, typeof(nuint))); Assert.That(!IntegerLiteralConversion(-1, typeof(nuint))); Assert.That(IntegerLiteralConversion(uint.MaxValue, typeof(nuint))); Assert.That(!IntegerLiteralConversion(long.MaxValue, typeof(nuint))); } [Test] public void UnconstrainedTypeParameter() { ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.TypeDefinition, 0, "T"); ITypeParameter t2 = new DefaultTypeParameter(compilation, SymbolKind.TypeDefinition, 1, "T2"); ITypeParameter tm = new DefaultTypeParameter(compilation, SymbolKind.Method, 0, "TM"); Assert.That(conversions.ImplicitConversion(SpecialType.NullType, t), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(KnownTypeCode.Object)), Is.EqualTo(C.BoxingConversion)); Assert.That(conversions.ImplicitConversion(t, SpecialType.Dynamic), Is.EqualTo(C.BoxingConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(ValueType))), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, t), Is.EqualTo(C.IdentityConversion)); Assert.That(conversions.ImplicitConversion(t2, t), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, t2), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, tm), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(tm, t), Is.EqualTo(C.None)); } [Test] public void TypeParameterWithReferenceTypeConstraint() { ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.TypeDefinition, 0, "T", hasReferenceTypeConstraint: true); Assert.That(conversions.ImplicitConversion(SpecialType.NullType, t), Is.EqualTo(C.NullLiteralConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(KnownTypeCode.Object)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(conversions.ImplicitConversion(t, SpecialType.Dynamic), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(ValueType))), Is.EqualTo(C.None)); } [Test] public void TypeParameterWithValueTypeConstraint() { ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.TypeDefinition, 0, "T", hasValueTypeConstraint: true); Assert.That(conversions.ImplicitConversion(SpecialType.NullType, t), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(KnownTypeCode.Object)), Is.EqualTo(C.BoxingConversion)); Assert.That(conversions.ImplicitConversion(t, SpecialType.Dynamic), Is.EqualTo(C.BoxingConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(ValueType))), Is.EqualTo(C.BoxingConversion)); } [Test] public void TypeParameterWithClassConstraint() { ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.TypeDefinition, 0, "T", constraints: new[] { compilation.FindType(typeof(StringComparer)) }); Assert.That(conversions.ImplicitConversion(SpecialType.NullType, t), Is.EqualTo(C.NullLiteralConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(KnownTypeCode.Object)), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(conversions.ImplicitConversion(t, SpecialType.Dynamic), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(ValueType))), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(StringComparer))), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(IComparer))), Is.EqualTo(C.ImplicitReferenceConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(IComparer<int>))), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(IComparer<string>))), Is.EqualTo(C.ImplicitReferenceConversion)); } [Test] public void TypeParameterWithInterfaceConstraint() { ITypeParameter t = new DefaultTypeParameter(compilation, SymbolKind.TypeDefinition, 0, "T", constraints: new[] { compilation.FindType(typeof(IList)) }); Assert.That(conversions.ImplicitConversion(SpecialType.NullType, t), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(KnownTypeCode.Object)), Is.EqualTo(C.BoxingConversion)); Assert.That(conversions.ImplicitConversion(t, SpecialType.Dynamic), Is.EqualTo(C.BoxingConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(ValueType))), Is.EqualTo(C.None)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(IList))), Is.EqualTo(C.BoxingConversion)); Assert.That(conversions.ImplicitConversion(t, compilation.FindType(typeof(IEnumerable))), Is.EqualTo(C.BoxingConversion)); } [Test] public void UserDefinedImplicitConversion() { Conversion c = ImplicitConversion(typeof(DateTime), typeof(DateTimeOffset)); Assert.That(c.IsImplicit && c.IsUserDefined); Assert.That(c.Method.FullName, Is.EqualTo("System.DateTimeOffset.op_Implicit")); Assert.That(ImplicitConversion(typeof(DateTimeOffset), typeof(DateTime)), Is.EqualTo(C.None)); } [Test] public void UserDefinedImplicitNullableConversion() { // User-defined conversion followed by nullable conversion Conversion c = ImplicitConversion(typeof(DateTime), typeof(DateTimeOffset?)); Assert.That(c.IsValid && c.IsUserDefined); Assert.That(!c.IsLifted); // Lifted user-defined conversion c = ImplicitConversion(typeof(DateTime?), typeof(DateTimeOffset?)); Assert.That(c.IsValid && c.IsUserDefined && c.IsLifted); // User-defined conversion doesn't drop the nullability c = ImplicitConversion(typeof(DateTime?), typeof(DateTimeOffset)); Assert.That(!c.IsValid); } bool IntegerLiteralConversion(object value, Type to) { IType fromType = compilation.FindType(value.GetType()); ConstantResolveResult crr = new ConstantResolveResult(fromType, value); IType to2 = compilation.FindType(to); return conversions.ImplicitConversion(crr, to2).IsValid; } [Test] public void IntegerLiteralToEnumConversions() { Assert.That(IntegerLiteralConversion(0, typeof(LoaderOptimization))); Assert.That(IntegerLiteralConversion(0L, typeof(LoaderOptimization))); Assert.That(IntegerLiteralConversion(0, typeof(LoaderOptimization?))); Assert.That(!IntegerLiteralConversion(0, typeof(string))); Assert.That(!IntegerLiteralConversion(1, typeof(LoaderOptimization))); } [Test] public void ImplicitConstantExpressionConversion() { Assert.That(IntegerLiteralConversion(0, typeof(int))); Assert.That(IntegerLiteralConversion(0, typeof(ushort))); Assert.That(IntegerLiteralConversion(0, typeof(sbyte))); Assert.That(IntegerLiteralConversion(-1, typeof(int))); Assert.That(!IntegerLiteralConversion(-1, typeof(ushort))); Assert.That(IntegerLiteralConversion(-1, typeof(sbyte))); Assert.That(IntegerLiteralConversion(200, typeof(int))); Assert.That(IntegerLiteralConversion(200, typeof(ushort))); Assert.That(!IntegerLiteralConversion(200, typeof(sbyte))); } [Test] public void ImplicitLongConstantExpressionConversion() { Assert.That(!IntegerLiteralConversion(0L, typeof(int))); Assert.That(!IntegerLiteralConversion(0L, typeof(short))); Assert.That(IntegerLiteralConversion(0L, typeof(long))); Assert.That(IntegerLiteralConversion(0L, typeof(ulong))); Assert.That(IntegerLiteralConversion(-1L, typeof(long))); Assert.That(!IntegerLiteralConversion(-1L, typeof(ulong))); } [Test] public void ImplicitConstantExpressionConversionToNullable() { Assert.That(IntegerLiteralConversion(0, typeof(uint?))); Assert.That(IntegerLiteralConversion(0, typeof(short?))); Assert.That(IntegerLiteralConversion(0, typeof(byte?))); Assert.That(!IntegerLiteralConversion(-1, typeof(uint?))); Assert.That(IntegerLiteralConversion(-1, typeof(short?))); Assert.That(!IntegerLiteralConversion(-1, typeof(byte?))); Assert.That(IntegerLiteralConversion(200, typeof(uint?))); Assert.That(IntegerLiteralConversion(200, typeof(short?))); Assert.That(IntegerLiteralConversion(200, typeof(byte?))); Assert.That(!IntegerLiteralConversion(0L, typeof(uint?))); Assert.That(IntegerLiteralConversion(0L, typeof(long?))); Assert.That(IntegerLiteralConversion(0L, typeof(ulong?))); Assert.That(IntegerLiteralConversion(-1L, typeof(long?))); Assert.That(!IntegerLiteralConversion(-1L, typeof(ulong?))); } [Test] public void ImplicitConstantExpressionConversionNumberInterfaces() { Assert.That(IntegerLiteralConversion(0, typeof(IFormattable))); Assert.That(IntegerLiteralConversion(0, typeof(IComparable<int>))); Assert.That(!IntegerLiteralConversion(0, typeof(IComparable<short>))); Assert.That(!IntegerLiteralConversion(0, typeof(IComparable<long>))); } int BetterConversion(Type s, Type t1, Type t2) { IType sType = compilation.FindType(s); IType t1Type = compilation.FindType(t1); IType t2Type = compilation.FindType(t2); return conversions.BetterConversion(sType, t1Type, t2Type); } int BetterConversion(object value, Type t1, Type t2) { IType fromType = compilation.FindType(value.GetType()); ConstantResolveResult crr = new ConstantResolveResult(fromType, value); IType t1Type = compilation.FindType(t1); IType t2Type = compilation.FindType(t2); return conversions.BetterConversion(crr, t1Type, t2Type); } [Test] public void BetterConversion() { Assert.That(BetterConversion(typeof(string), typeof(string), typeof(object)), Is.EqualTo(1)); Assert.That(BetterConversion(typeof(string), typeof(object), typeof(IComparable<string>)), Is.EqualTo(2)); Assert.That(BetterConversion(typeof(string), typeof(IEnumerable<char>), typeof(IComparable<string>)), Is.EqualTo(0)); } [Test] public void BetterPrimitiveConversion() { Assert.That(BetterConversion(typeof(short), typeof(int), typeof(long)), Is.EqualTo(1)); Assert.That(BetterConversion(typeof(short), typeof(int), typeof(uint)), Is.EqualTo(1)); Assert.That(BetterConversion(typeof(ushort), typeof(uint), typeof(int)), Is.EqualTo(2)); Assert.That(BetterConversion(typeof(char), typeof(short), typeof(int)), Is.EqualTo(1)); Assert.That(BetterConversion(typeof(char), typeof(ushort), typeof(int)), Is.EqualTo(1)); Assert.That(BetterConversion(typeof(sbyte), typeof(long), typeof(ulong)), Is.EqualTo(1)); Assert.That(BetterConversion(typeof(byte), typeof(ushort), typeof(short)), Is.EqualTo(2)); Assert.That(BetterConversion(1, typeof(sbyte), typeof(byte)), Is.EqualTo(1)); Assert.That(BetterConversion(1, typeof(ushort), typeof(sbyte)), Is.EqualTo(2)); } [Test] public void BetterNullableConversion() { Assert.That(BetterConversion(typeof(byte), typeof(int), typeof(uint?)), Is.EqualTo(0)); Assert.That(BetterConversion(typeof(byte?), typeof(int?), typeof(uint?)), Is.EqualTo(0)); Assert.That(BetterConversion(typeof(byte), typeof(ushort?), typeof(uint?)), Is.EqualTo(1)); Assert.That(BetterConversion(typeof(byte?), typeof(ulong?), typeof(uint?)), Is.EqualTo(2)); Assert.That(BetterConversion(typeof(byte), typeof(ushort?), typeof(uint)), Is.EqualTo(0)); Assert.That(BetterConversion(typeof(byte), typeof(ushort?), typeof(int)), Is.EqualTo(0)); Assert.That(BetterConversion(typeof(byte), typeof(ulong?), typeof(uint)), Is.EqualTo(2)); Assert.That(BetterConversion(typeof(byte), typeof(ulong?), typeof(int)), Is.EqualTo(0)); Assert.That(BetterConversion(typeof(ushort?), typeof(long?), typeof(int?)), Is.EqualTo(2)); Assert.That(BetterConversion(typeof(sbyte), typeof(int?), typeof(uint?)), Is.EqualTo(0)); } /* TODO: we should probably revive these tests somehow [Test] public void ExpansiveInheritance() { var a = new DefaultUnresolvedTypeDefinition(string.Empty, "A"); var b = new DefaultUnresolvedTypeDefinition(string.Empty, "B"); // interface A<in U> a.Kind = TypeKind.Interface; a.TypeParameters.Add(new DefaultUnresolvedTypeParameter(SymbolKind.TypeDefinition, 0, "U") { Variance = VarianceModifier.Contravariant }); // interface B<X> : A<A<B<X>>> { } b.TypeParameters.Add(new DefaultUnresolvedTypeParameter(SymbolKind.TypeDefinition, 0, "X")); b.BaseTypes.Add(new ParameterizedTypeReference( a, new[] { new ParameterizedTypeReference( a, new [] { new ParameterizedTypeReference( b, new [] { new TypeParameterReference(SymbolKind.TypeDefinition, 0) } ) } ) })); ICompilation compilation = TypeSystemHelper.CreateCompilation(a, b); ITypeDefinition resolvedA = compilation.MainAssembly.GetTypeDefinition(a.FullTypeName); ITypeDefinition resolvedB = compilation.MainAssembly.GetTypeDefinition(b.FullTypeName); IType type1 = new ParameterizedType(resolvedB, new[] { compilation.FindType(KnownTypeCode.Double) }); IType type2 = new ParameterizedType(resolvedA, new[] { new ParameterizedType(resolvedB, new[] { compilation.FindType(KnownTypeCode.String) }) }); Assert.That(!conversions.ImplicitConversion(type1, type2).IsValid); } [Test] public void ImplicitTypeParameterConversion() { string program = @"using System; class Test { public void M<T, U>(T t) where T : U { U u = $t$; } }"; Assert.AreEqual(C.BoxingConversion, GetConversion(program)); } [Test] public void InvalidImplicitTypeParameterConversion() { string program = @"using System; class Test { public void M<T, U>(T t) where U : T { U u = $t$; } }"; Assert.AreEqual(C.None, GetConversion(program)); } [Test] public void ImplicitTypeParameterArrayConversion() { string program = @"using System; class Test { public void M<T, U>(T[] t) where T : U { U[] u = $t$; } }"; // invalid, e.g. T=int[], U=object[] Assert.AreEqual(C.None, GetConversion(program)); } [Test] public void ImplicitTypeParameterConversionWithClassConstraint() { string program = @"using System; class Test { public void M<T, U>(T t) where T : class, U where U : class { U u = $t$; } }"; Assert.AreEqual(C.ImplicitReferenceConversion, GetConversion(program)); } [Test] public void ImplicitTypeParameterArrayConversionWithClassConstraint() { string program = @"using System; class Test { public void M<T, U>(T[] t) where T : class, U where U : class { U[] u = $t$; } }"; Assert.AreEqual(C.ImplicitReferenceConversion, GetConversion(program)); } [Test] public void ImplicitTypeParameterConversionWithClassConstraintOnlyOnT() { string program = @"using System; class Test { public void M<T, U>(T t) where T : class, U { U u = $t$; } }"; Assert.AreEqual(C.ImplicitReferenceConversion, GetConversion(program)); } [Test] public void ImplicitTypeParameterArrayConversionWithClassConstraintOnlyOnT() { string program = @"using System; class Test { public void M<T, U>(T[] t) where T : class, U { U[] u = $t$; } }"; 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.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); Assert.That(!c.DelegateCapturesFirstArgument); Assert.IsNotNull(c.Method); } [Test] public void MethodGroupConversion_Void_InstanceMethod() { string program = @"using System; delegate void D(); class Test { D d; public void M() { d = $M$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); Assert.That(c.DelegateCapturesFirstArgument); 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.That(c.IsValid); Assert.That(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.That(!c.IsValid); Assert.That(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.That(c.IsValid); Assert.That(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.That(c.IsValid); Assert.That(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.That(!c.IsValid); Assert.That(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.That(!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.That(!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.That(!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.That(c.IsValid); Assert.That(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.That(c.IsValid); Assert.That(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.That(c.IsValid); Assert.That(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.That(c.IsMethodGroupConversion); } [Test] public void MethodGroupConversion_ExactMatchIsBetter() { string program = @"using System; class Test { delegate void D(string a); D d = $M$; static void M(object x) {} static void M(string x = null) {} }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); Assert.AreEqual("System.String", c.Method.Parameters.Single().Type.FullName); } [Test] public void MethodGroupConversion_CannotLeaveOutOptionalParameters() { string program = @"using System; class Test { delegate void D(string a); D d = $M$; static void M(object x) {} static void M(string x, string y = null) {} }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); Assert.AreEqual("System.Object", c.Method.Parameters.Single().Type.FullName); } [Test] public void MethodGroupConversion_CannotUseExpandedParams() { string program = @"using System; class Test { delegate void D(string a); D d = $M$; static void M(object x) {} static void M(params string[] x) {} }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); Assert.AreEqual("System.Object", c.Method.Parameters.Single().Type.FullName); } [Test] public void MethodGroupConversion_ExtensionMethod() { string program = @"using System; static class Ext { public static void M(this string s, int x) {} } class Test { delegate void D(int a); void F() { string s = """"; D d = $s.M$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); Assert.That(c.DelegateCapturesFirstArgument); } [Test] public void MethodGroupConversion_ExtensionMethodUsedAsStaticMethod() { string program = @"using System; static class Ext { public static void M(this string s, int x) {} } class Test { delegate void D(string s, int a); void F() { D d = $Ext.M$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsMethodGroupConversion); Assert.That(!c.DelegateCapturesFirstArgument); } [Test] public void MethodGroupConversion_ObjectToDynamic() { string program = @"using System; class Test { public void F(object o) {} public void M() { Action<dynamic> x = $F$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); } [Test] public void MethodGroupConversion_ObjectToDynamicGenericArgument() { string program = @"using System; using System.Collections.Generic; class Test { public void F(List<object> l) {} public void M() { Action<List<dynamic>> x = $F$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); } [Test] public void MethodGroupConversion_ObjectToDynamicReturnValue() { string program = @"using System; class Test { public object F() {} public void M() { Func<dynamic> x = $F$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); } [Test] public void MethodGroupConversion_DynamicToObject() { string program = @"using System; class Test { public void F(dynamic o) {} public void M() { Action<object> x = $F$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); } [Test] public void MethodGroupConversion_DynamicToObjectGenericArgument() { string program = @"using System; using System.Collections.Generic; class Test { public void F(List<dynamic> l) {} public void M() { Action<List<object>> x = $F$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); } [Test] public void MethodGroupConversion_DynamicToObjectReturnValue() { string program = @"using System; class Test { public dynamic F() {} public void M() { Func<object> x = $F$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); } [Test] public void UserDefined_IntLiteral_ViaUInt_ToCustomStruct() { string program = @"using System; struct T { public static implicit operator T(uint a) { return new T(); } } class Test { static void M() { T t = $1$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); } [Test] public void UserDefined_NullLiteral_ViaString_ToCustomStruct() { string program = @"using System; struct T { public static implicit operator T(string a) { return new T(); } } class Test { static void M() { T t = $null$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); } [Test] public void UserDefined_CanUseLiftedEvenIfReturnTypeAlreadyNullable() { string program = @"using System; struct S { public static implicit operator short?(S s) { return 0; } } class Test { static void M(S? s) { int? i = $s$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.That(c.IsLifted); } [Test] public void UserDefinedImplicitConversion_PicksExactSourceTypeIfPossible() { string program = @"using System; class Convertible { public static implicit operator Convertible(int i) {return new Convertible(); } public static implicit operator Convertible(short s) {return new Convertible(); } } class Test { public void M() { Convertible a = $33$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("i", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_PicksMostEncompassedSourceType() { string program = @"using System; class Convertible { public static implicit operator Convertible(long l) {return new Convertible(); } public static implicit operator Convertible(uint ui) {return new Convertible(); } } class Test { public void M() { Convertible a = $(ushort)33$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("ui", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_NoMostEncompassedSourceTypeIsInvalid() { string program = @"using System; class Convertible { public static implicit operator Convertible(ulong l) {return new Convertible(); } public static implicit operator Convertible(int ui) {return new Convertible(); } } class Test { public void M() { Convertible a = $(ushort)33$; } }"; var c = GetConversion(program); Assert.That(!c.IsValid); } [Test] public void UserDefinedImplicitConversion_PicksExactTargetTypeIfPossible() { string program = @"using System; class Convertible { public static implicit operator int(Convertible i) {return 0; } public static implicit operator short(Convertible s) {return 0; } } class Test { public void M() { int a = $new Convertible()$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("i", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_PicksMostEncompassingTargetType() { string program = @"using System; class Convertible { public static implicit operator int(Convertible i) {return 0; } public static implicit operator ushort(Convertible us) {return 0; } } class Test { public void M() { ulong a = $new Convertible()$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("us", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_NoMostEncompassingTargetTypeIsInvalid() { string program = @"using System; class Convertible { public static implicit operator uint(Convertible i) {return 0; } public static implicit operator short(Convertible us) {return 0; } } class Test { public void M() { long a = $new Convertible()$; } }"; var c = GetConversion(program); Assert.That(!c.IsValid); } [Test] public void UserDefinedImplicitConversion_AmbiguousIsInvalid() { string program = @"using System; class Convertible1 { public static implicit operator Convertible2(Convertible1 c) {return 0; } } class Convertible2 { public static implicit operator Convertible2(Convertible1 c) {return 0; } } class Test { public void M() { Convertible2 a = $new Convertible1()$; } }"; var c = GetConversion(program); Assert.That(!c.IsValid); } [Test] public void UserDefinedImplicitConversion_DefinedNullableTakesPrecedenceOverLifted() { string program = @"using System; struct Convertible { public static implicit operator Convertible(int i) {return new Convertible(); } public static implicit operator Convertible?(int? ni) {return new Convertible(); } } class Test { public void M() { Convertible? a = $(int?)33$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.That(!c.IsLifted); Assert.AreEqual("ni", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_UIntConstant() { string program = @"using System; class Convertible { public static implicit operator Convertible(long l) {return new Convertible(); } public static implicit operator Convertible(uint ui) {return new Convertible(); } } class Test { public void M() { Convertible a = $33$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("ui", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_NullableUIntConstant() { string program = @"using System; class Convertible { public static implicit operator Convertible(long? l) {return new Convertible(); } public static implicit operator Convertible(uint? ui) {return new Convertible(); } } class Test { public void M() { Convertible a = $33$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("ui", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_UseShortResult_BecauseNullableCannotBeUnpacked() { string program = @"using System; class Test { public static implicit operator int?(Test i) { return 0; } public static implicit operator short(Test s) { return 0; } } class Program { public static void Main(string[] args) { int x = $new Test()$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("System.Int16", c.Method.ReturnType.FullName); } [Test] public void UserDefinedImplicitConversion_Short_Or_NullableByte_Target() { string program = @"using System; class Test { public static implicit operator short(Test s) { return 0; } public static implicit operator byte?(Test b) { return 0; } } class Program { public static void Main(string[] args) { int? x = $new Test()$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("System.Int16", c.Method.ReturnType.FullName); } [Test] public void UserDefinedImplicitConversion_Byte_Or_NullableShort_Target() { string program = @"using System; class Test { public static implicit operator byte(Test b) { return 0; } public static implicit operator short?(Test s) { return 0; } } class Program { public static void Main(string[] args) { int? x = $new Test()$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("s", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_Int_Or_NullableLong_Source() { string program = @"using System; class Test { public static implicit operator Test(int i) { return new Test(); } public static implicit operator Test(long? l) { return new Test(); } } class Program { static void Main() { short s = 0; Test t = $s$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("i", c.Method.Parameters[0].Name); } [Test] public void UserDefinedImplicitConversion_NullableInt_Or_Long_Source() { string program = @"using System; class Test { public static implicit operator Test(int? i) { return new Test(); } public static implicit operator Test(long l) { return new Test(); } } class Program { static void Main() { short s = 0; Test t = $s$; } }"; var c = GetConversion(program); Assert.That(!c.IsValid); Assert.That(c.IsUserDefined); } [Test] public void UserDefinedImplicitConversion_NullableInt_Or_Long_Constant_Source() { string program = @"using System; class Test { public static implicit operator Test(int? i) { return new Test(); } public static implicit operator Test(long l) { return new Test(); } } class Program { static void Main() { Test t = $1$; } }"; var c = GetConversion(program); Assert.That(!c.IsValid); Assert.That(c.IsUserDefined); } [Test] public void UserDefinedImplicitConversion_NullableInt_Or_NullableLong_Source() { string program = @"using System; class Test { public static implicit operator Test(int? i) { return new Test(); } public static implicit operator Test(long? l) { return new Test(); } } class Program { static void Main() { short s = 0; Test t = $s$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsUserDefined); Assert.AreEqual("i", c.Method.Parameters[0].Name); } [Test] public void PreferUserDefinedConversionOverReferenceConversion() { // actually this is not because user-defined conversions are better; // but because string is a better conversion target string program = @" class AA { public static implicit operator string(AA a) { return null; } } class Test { static void M(object obj) {} static void M(string str) {} static void Main() { $M(new AA())$; } }"; var rr = Resolve<CSharpInvocationResolveResult>(program); Assert.That(!rr.IsError); Assert.AreEqual("str", rr.Member.Parameters[0].Name); } [Test] public void PreferAmbiguousConversionOverReferenceConversion() { // Ambiguous conversions are a compiler error; but they are not // preventing the overload from being chosen. // The user-defined conversion wins because BB is a better conversion target than object. string program = @" class AA { public static implicit operator BB(AA a) { return null; } } class BB { public static implicit operator BB(AA a) { return null; } } class Test { static void M(BB b) {} static void M(object o) {} static void Main() { M($new AA()$); } }"; var c = GetConversion(program); Assert.That(c.IsUserDefined); Assert.That(!c.IsValid); } [Test] public void UserDefinedImplicitConversion_ConversionBeforeUserDefinedOperatorIsCorrect() { string program = @"using System; class Convertible { public static implicit operator Convertible(long l) {return new Convertible(); } } class Test { public void M() { int i = 33; Convertible a = $i$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.ConversionBeforeUserDefinedOperator.IsImplicit); Assert.That(c.ConversionBeforeUserDefinedOperator.IsNumericConversion); Assert.That(c.ConversionBeforeUserDefinedOperator.IsValid); Assert.That(c.ConversionAfterUserDefinedOperator.IsIdentityConversion); } [Test] public void UserDefinedImplicitConversion_ConversionAfterUserDefinedOperatorIsCorrect() { string program = @"using System; class Convertible { public static implicit operator int(Convertible i) {return 0; } } class Test { public void M() { long a = $new Convertible()$; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.ConversionBeforeUserDefinedOperator.IsIdentityConversion); Assert.That(c.ConversionAfterUserDefinedOperator.IsImplicit); Assert.That(c.ConversionAfterUserDefinedOperator.IsNumericConversion); Assert.That(c.ConversionAfterUserDefinedOperator.IsValid); } [Test] public void UserDefinedImplicitConversion_IsImplicit() { // Bug icsharpcode/NRefactory#183: conversions from constant expressions were incorrectly marked as explicit string program = @"using System; class Test { void Hello(JsNumber3 x) { Hello($7$); } } public class JsNumber3 { public static implicit operator JsNumber3(int d) { return null; } }"; var c = GetConversion(program); Assert.That(c.IsValid); Assert.That(c.IsImplicit); Assert.That(!c.IsExplicit); Assert.AreEqual(Conversion.IdentityConversion, c.ConversionBeforeUserDefinedOperator); Assert.AreEqual(Conversion.IdentityConversion, c.ConversionAfterUserDefinedOperator); } */ } }