/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Semantic/Semantics/ConditionalOperatorTests.cs
1 565 строк
56 KB
Jan Jones
Avoid assert on erroneous target-typed conditionals (#84752)
05 авг 2026, 11:00
Не верифицирован
05 авг 2026, 11:00
934af28
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { /// <summary> /// Test binding of the conditional (aka ternary) operator. /// </summary> public class ConditionalOperatorTests : CSharpTestBase { /// <summary> /// Both branches have the same type, so no conversion is necessary. /// </summary> [Fact] public void TestSameType() { TestConditional("true ? 1 : 2", expectedType: "System.Int32"); TestConditional("false ? 'a' : 'b'", expectedType: "System.Char"); TestConditional("true ? 1.5 : GetDouble()", expectedType: "System.Double"); TestConditional("false ? GetObject() : GetObject()", expectedType: "System.Object"); TestConditional("true ? GetUserGeneric<T>() : GetUserGeneric<T>()", expectedType: "D<T>"); TestConditional("false ? GetTypeParameter<T>() : GetTypeParameter<T>()", expectedType: "T"); } /// <summary> /// Both branches have types and exactly one expression is convertible to the type of the other. /// </summary> [Fact] public void TestOneConversion() { TestConditional("true ? GetShort() : GetInt()", expectedType: "System.Int32"); TestConditional("false ? \"string\" : GetObject()", expectedType: "System.Object"); TestConditional("true ? GetVariantInterface<string, int>() : GetVariantInterface<object, int>()", expectedType: "I<System.String, System.Int32>"); TestConditional("false ? GetVariantInterface<int, object>() : GetVariantInterface<int, string>()", expectedType: "I<System.Int32, System.Object>"); } /// <summary> /// Both branches have types and both expression are convertible to the type of the other. /// The wider type is preferred. /// </summary> /// <remarks> /// Cases where both conversions are possible and neither is preferred as the /// wider of the two are possible only in the presence of user-defined implicit /// conversions. Such cases are tested separately. /// See SemanticErrorTests.CS0172ERR_AmbigQM. /// </remarks> [Fact] public void TestAmbiguousPreferWider() { TestConditional("true ? 1 : (short)2", expectedType: "System.Int32"); TestConditional("false ? (float)2 : 1", expectedType: "System.Single"); TestConditional("true ? 1.5d : (double)2", expectedType: "System.Double"); } /// <summary> /// Both branches have types but neither expression is convertible to the type /// of the other. /// </summary> [Fact] public void TestNoConversion() { TestConditional("true ? T : U", null, parseOptions: TestOptions.Regular8, Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type"), Diagnostic(ErrorCode.ERR_BadSKunknown, "U").WithArguments("U", "type")); TestConditional("true ? T : U", null, parseOptions: TestOptions.Regular8.WithLanguageVersion(MessageID.IDS_FeatureTargetTypedConditional.RequiredVersion()), Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type"), Diagnostic(ErrorCode.ERR_BadSKunknown, "U").WithArguments("U", "type")); TestConditional("false ? T : 1", null, parseOptions: TestOptions.Regular8, Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type")); TestConditional("false ? T : 1", null, parseOptions: TestOptions.Regular8.WithLanguageVersion(MessageID.IDS_FeatureTargetTypedConditional.RequiredVersion()), Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type")); TestConditional("true ? GetUserGeneric<char>() : GetUserNonGeneric()", null, Diagnostic(ErrorCode.ERR_InvalidQM, "true ? GetUserGeneric<char>() : GetUserNonGeneric()").WithArguments("D<char>", "C")); } /// <summary> /// Exactly one branch has a type and the other expression is convertible to that type. /// </summary> [Fact] public void TestOneUntypedSuccess() { TestConditional("true ? GetObject() : null", expectedType: "System.Object"); //null literal TestConditional("false ? GetString : (System.Func<string>)null", expectedType: "System.Func<System.String>"); //method group TestConditional("true ? (System.Func<int, int>)null : x => x", expectedType: "System.Func<System.Int32, System.Int32>"); //lambda } /// <summary> /// Exactly one branch has a type but the other expression is not convertible to that type. /// </summary> [Fact] public void TestOneUntypedFailure() { TestConditional("true ? GetInt() : null", null, Diagnostic(ErrorCode.ERR_InvalidQM, "true ? GetInt() : null").WithArguments("int", "<null>")); TestConditional("false ? GetString : (System.Func<int>)null", null, TestOptions.WithoutImprovedOverloadCandidates, Diagnostic(ErrorCode.ERR_BadRetType, "GetString").WithArguments("C.GetString()", "string")); TestConditional("false ? GetString : (System.Func<int>)null", null, // (6,13): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'method group' and 'Func<int>' // _ = false ? GetString : (System.Func<int>)null; Diagnostic(ErrorCode.ERR_InvalidQM, "false ? GetString : (System.Func<int>)null").WithArguments("method group", "System.Func<int>")); TestConditional("true ? (System.Func<int, short>)null : x => x", null, Diagnostic(ErrorCode.ERR_InvalidQM, "true ? (System.Func<int, short>)null : x => x").WithArguments("System.Func<int, short>", "lambda expression")); } [Fact] public void TestBothUntyped() { TestConditional("true ? null : null", null, Diagnostic(ErrorCode.ERR_InvalidQM, "true ? null : null").WithArguments("<null>", "<null>")); TestConditional("false ? null : GetInt", null, Diagnostic(ErrorCode.ERR_InvalidQM, "false ? null : GetInt").WithArguments("<null>", "method group")); TestConditional("true ? null : x => x", null, Diagnostic(ErrorCode.ERR_InvalidQM, "true ? null : x => x").WithArguments("<null>", "lambda expression")); TestConditional("false ? GetInt : GetInt", null, Diagnostic(ErrorCode.ERR_InvalidQM, "false ? GetInt : GetInt").WithArguments("method group", "method group")); TestConditional("true ? GetInt : x => x", null, Diagnostic(ErrorCode.ERR_InvalidQM, "true ? GetInt : x => x").WithArguments("method group", "lambda expression")); TestConditional("false ? x => x : x => x", null, Diagnostic(ErrorCode.ERR_InvalidQM, "false ? x => x : x => x").WithArguments("lambda expression", "lambda expression")); } [Fact] public void TestFunCall() { TestConditional("true ? GetVoid() : GetInt()", null, Diagnostic(ErrorCode.ERR_InvalidQM, "true ? GetVoid() : GetInt()").WithArguments("void", "int")); TestConditional("GetVoid() ? 1 : 2", null, Diagnostic(ErrorCode.ERR_NoImplicitConv, "GetVoid()").WithArguments("void", "bool")); TestConditional("GetInt() ? 1 : 2", null, Diagnostic(ErrorCode.ERR_NoImplicitConv, "GetInt()").WithArguments("int", "bool")); TestConditional("GetBool() ? 1 : 2", "System.Int32"); } [Fact] public void TestEmptyExpression() { TestConditional("true ? : GetInt()", null, Diagnostic(ErrorCode.ERR_InvalidExprTerm, ":").WithArguments(":")); TestConditional("true ? GetInt() : ", null, Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";")); } [Fact] public void TestEnum() { TestConditional("true? 0 : color.Blue", "color"); TestConditional("true? 5 : color.Blue", null, Diagnostic(ErrorCode.ERR_InvalidQM, "true? 5 : color.Blue").WithArguments("int", "color")); TestConditional("true? null : color.Blue", null, Diagnostic(ErrorCode.ERR_InvalidQM, "true? null : color.Blue").WithArguments("<null>", "color")); } [Fact] public void TestAs() { TestConditional(@"(1 < 2) ? ""MyString"" as string : "" """, "System.String"); TestConditional(@"(1 > 2) ? "" "" : ""MyString"" as string", "System.String"); } [Fact] public void TestGeneric() { TestConditional(@"GetUserNonGeneric()? 1 : 2", null, Diagnostic(ErrorCode.ERR_NoImplicitConv, "GetUserNonGeneric()").WithArguments("C", "bool")); TestConditional(@"GetUserGeneric<T>()? 1 : 2", null, Diagnostic(ErrorCode.ERR_NoImplicitConv, "GetUserGeneric<T>()").WithArguments("D<T>", "bool")); TestConditional(@"GetTypeParameter<T>()? 1 : 2", null, Diagnostic(ErrorCode.ERR_NoImplicitConv, "GetTypeParameter<T>()").WithArguments("T", "bool")); TestConditional(@"GetVariantInterface<T, U>()? 1 : 2", null, Diagnostic(ErrorCode.ERR_NoImplicitConv, "GetVariantInterface<T, U>()").WithArguments("I<T, U>", "bool")); } [Fact] public void TestInvalidCondition() { // CONSIDER: dev10 reports ERR_ConstOutOfRange TestConditional("1 ? 2 : 3", null, Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "bool")); TestConditional("goo ? 'a' : 'b'", null, Diagnostic(ErrorCode.ERR_NameNotInContext, "goo").WithArguments("goo")); TestConditional("new Goo() ? GetObject() : null", null, Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Goo").WithArguments("Goo")); // CONSIDER: dev10 reports ERR_ConstOutOfRange TestConditional("1 ? null : null", null, parseOptions: TestOptions.Regular8, Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "bool") ); TestConditional("1 ? null : null", null, parseOptions: TestOptions.Regular.WithLanguageVersion(MessageID.IDS_FeatureTargetTypedConditional.RequiredVersion()), Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "bool") ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/84641")] public void InvalidCondition_UnconvertedConditionalOperator() { var source = """ class C { string F = string.Join("", 2 ? "" : ToString); } """; CreateCompilation(source).VerifyDiagnostics( // (3,32): error CS0029: Cannot implicitly convert type 'int' to 'bool' // string F = string.Join("", 2 ? "" : ToString); Diagnostic(ErrorCode.ERR_NoImplicitConv, "2").WithArguments("int", "bool").WithLocation(3, 32), // (3,41): error CS0236: A field initializer cannot reference the non-static field, method, or property 'object.ToString()' // string F = string.Join("", 2 ? "" : ToString); Diagnostic(ErrorCode.ERR_FieldInitRefNonstatic, "ToString").WithArguments("object.ToString()").WithLocation(3, 41)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67975")] public void SumTypeInTuple() { var source = """ #nullable enable class C1 { } class C2 { } class C3 { } struct SumType<T1, T2> where T1 : notnull where T2 : notnull { public static implicit operator SumType<T1, T2>(T1 _) => throw null!; public static implicit operator SumType<T1, T2>(T2 _) => throw null!; } class D { public (C1, SumType<C2, C3>) F; public void M(C1 one, C2? two, C3? three) { if (three == null) return; F = (one, two is not null ? two : three); } } """; CreateCompilation(source).VerifyDiagnostics(); } [WorkItem(545408, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545408")] [Fact] public void TestDelegateCovarianceConversions() { var source = @" using System; using System.Collections.Generic; delegate void D<out T>(); class Base { } class Derived : Base { } class Program { static void Main() { bool testFlag = true; D<Base> baseDelegate = () => Console.WriteLine(""B""); D<Derived> derivedDelegate = () => Console.WriteLine(""D""); D<Base> fcn; fcn = testFlag ? baseDelegate : derivedDelegate; fcn(); fcn = testFlag ? derivedDelegate : baseDelegate; fcn(); fcn = baseDelegate ?? derivedDelegate; fcn(); fcn = derivedDelegate ?? baseDelegate; fcn(); IEnumerable<Base> baseSequence = null; List<Derived> derivedList = null; IEnumerable<Base> result = testFlag ? baseSequence : derivedList; } } "; var verifier = CompileAndVerify(source, expectedOutput: @"B D B D"); // Note no castclass instructions // to be completely sure that stack states merge with expected types // we use "stloc;ldloc" as a surrogate "static cast" for values // in different branches verifier.VerifyIL("Program.Main", @" { // Code size 133 (0x85) .maxstack 3 .locals init (D<Base> V_0, //baseDelegate D<Derived> V_1, //derivedDelegate System.Collections.Generic.IEnumerable<Base> V_2, //baseSequence System.Collections.Generic.List<Derived> V_3, //derivedList D<Base> V_4) IL_0000: ldc.i4.1 IL_0001: ldsfld ""D<Base> Program.<>c.<>9__0_0"" IL_0006: dup IL_0007: brtrue.s IL_0020 IL_0009: pop IL_000a: ldsfld ""Program.<>c Program.<>c.<>9"" IL_000f: ldftn ""void Program.<>c.<Main>b__0_0()"" IL_0015: newobj ""D<Base>..ctor(object, System.IntPtr)"" IL_001a: dup IL_001b: stsfld ""D<Base> Program.<>c.<>9__0_0"" IL_0020: stloc.0 IL_0021: ldsfld ""D<Derived> Program.<>c.<>9__0_1"" IL_0026: dup IL_0027: brtrue.s IL_0040 IL_0029: pop IL_002a: ldsfld ""Program.<>c Program.<>c.<>9"" IL_002f: ldftn ""void Program.<>c.<Main>b__0_1()"" IL_0035: newobj ""D<Derived>..ctor(object, System.IntPtr)"" IL_003a: dup IL_003b: stsfld ""D<Derived> Program.<>c.<>9__0_1"" IL_0040: stloc.1 IL_0041: dup IL_0042: brtrue.s IL_004b IL_0044: ldloc.1 IL_0045: stloc.s V_4 IL_0047: ldloc.s V_4 IL_0049: br.s IL_004c IL_004b: ldloc.0 IL_004c: callvirt ""void D<Base>.Invoke()"" IL_0051: dup IL_0052: brtrue.s IL_0057 IL_0054: ldloc.0 IL_0055: br.s IL_005c IL_0057: ldloc.1 IL_0058: stloc.s V_4 IL_005a: ldloc.s V_4 IL_005c: callvirt ""void D<Base>.Invoke()"" IL_0061: ldloc.0 IL_0062: dup IL_0063: brtrue.s IL_006b IL_0065: pop IL_0066: ldloc.1 IL_0067: stloc.s V_4 IL_0069: ldloc.s V_4 IL_006b: callvirt ""void D<Base>.Invoke()"" IL_0070: ldloc.1 IL_0071: stloc.s V_4 IL_0073: ldloc.s V_4 IL_0075: dup IL_0076: brtrue.s IL_007a IL_0078: pop IL_0079: ldloc.0 IL_007a: callvirt ""void D<Base>.Invoke()"" IL_007f: ldnull IL_0080: stloc.2 IL_0081: ldnull IL_0082: stloc.3 IL_0083: pop IL_0084: ret }"); } [WorkItem(545408, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545408")] [Fact] public void TestDelegateContravarianceConversions() { var source = @" using System; delegate void D<in T>(); class Base { } class Derived : Base { } class Program { static void Main() { bool testFlag = true; D<Base> baseDelegate = () => Console.Write('B'); D<Derived> derivedDelegate = () => Console.Write('D'); D<Derived> fcn; fcn = testFlag ? baseDelegate : derivedDelegate; fcn(); fcn = testFlag ? derivedDelegate : baseDelegate; fcn(); fcn = baseDelegate ?? derivedDelegate; fcn(); fcn = derivedDelegate ?? baseDelegate; fcn(); } } "; var verifier = CompileAndVerify(source, expectedOutput: @"BDBD"); verifier.VerifyIL("Program.Main", @" { // Code size 119 (0x77) .maxstack 3 .locals init (D<Base> V_0, //baseDelegate D<Derived> V_1, //derivedDelegate D<Derived> V_2) IL_0000: ldc.i4.1 IL_0001: ldsfld ""D<Base> Program.<>c.<>9__0_0"" IL_0006: dup IL_0007: brtrue.s IL_0020 IL_0009: pop IL_000a: ldsfld ""Program.<>c Program.<>c.<>9"" IL_000f: ldftn ""void Program.<>c.<Main>b__0_0()"" IL_0015: newobj ""D<Base>..ctor(object, System.IntPtr)"" IL_001a: dup IL_001b: stsfld ""D<Base> Program.<>c.<>9__0_0"" IL_0020: stloc.0 IL_0021: ldsfld ""D<Derived> Program.<>c.<>9__0_1"" IL_0026: dup IL_0027: brtrue.s IL_0040 IL_0029: pop IL_002a: ldsfld ""Program.<>c Program.<>c.<>9"" IL_002f: ldftn ""void Program.<>c.<Main>b__0_1()"" IL_0035: newobj ""D<Derived>..ctor(object, System.IntPtr)"" IL_003a: dup IL_003b: stsfld ""D<Derived> Program.<>c.<>9__0_1"" IL_0040: stloc.1 IL_0041: dup IL_0042: brtrue.s IL_0047 IL_0044: ldloc.1 IL_0045: br.s IL_004a IL_0047: ldloc.0 IL_0048: stloc.2 IL_0049: ldloc.2 IL_004a: callvirt ""void D<Derived>.Invoke()"" IL_004f: brtrue.s IL_0056 IL_0051: ldloc.0 IL_0052: stloc.2 IL_0053: ldloc.2 IL_0054: br.s IL_0057 IL_0056: ldloc.1 IL_0057: callvirt ""void D<Derived>.Invoke()"" IL_005c: ldloc.0 IL_005d: stloc.2 IL_005e: ldloc.2 IL_005f: dup IL_0060: brtrue.s IL_0064 IL_0062: pop IL_0063: ldloc.1 IL_0064: callvirt ""void D<Derived>.Invoke()"" IL_0069: ldloc.1 IL_006a: dup IL_006b: brtrue.s IL_0071 IL_006d: pop IL_006e: ldloc.0 IL_006f: stloc.2 IL_0070: ldloc.2 IL_0071: callvirt ""void D<Derived>.Invoke()"" IL_0076: ret }"); } [WorkItem(545408, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545408")] [Fact] public void TestInterfaceCovarianceConversions() { string source = @" using System; interface I<out T> { } class Base { } class Derived : Base { } class B : I<Base> { } class D : I<Derived> { } class Program { static void Main() { bool testFlag = true; I<Base> baseInstance = new B(); I<Derived> derivedInstance = new D(); I<Base> i; i = testFlag ? baseInstance : derivedInstance; Console.Write(i.GetType().Name); i = testFlag ? derivedInstance : baseInstance; Console.Write(i.GetType().Name); i = baseInstance ?? derivedInstance; Console.Write(i.GetType().Name); i = derivedInstance ?? baseInstance; Console.Write(i.GetType().Name); } } "; string expectedIL = @" { // Code size 107 (0x6b) .maxstack 2 .locals init (I<Base> V_0, //baseInstance I<Derived> V_1, //derivedInstance I<Base> V_2) IL_0000: ldc.i4.1 IL_0001: newobj ""B..ctor()"" IL_0006: stloc.0 IL_0007: newobj ""D..ctor()"" IL_000c: stloc.1 IL_000d: dup IL_000e: brtrue.s IL_0015 IL_0010: ldloc.1 IL_0011: stloc.2 IL_0012: ldloc.2 IL_0013: br.s IL_0016 IL_0015: ldloc.0 IL_0016: callvirt ""System.Type object.GetType()"" IL_001b: callvirt ""string System.Reflection.MemberInfo.Name.get"" IL_0020: call ""void System.Console.Write(string)"" IL_0025: brtrue.s IL_002a IL_0027: ldloc.0 IL_0028: br.s IL_002d IL_002a: ldloc.1 IL_002b: stloc.2 IL_002c: ldloc.2 IL_002d: callvirt ""System.Type object.GetType()"" IL_0032: callvirt ""string System.Reflection.MemberInfo.Name.get"" IL_0037: call ""void System.Console.Write(string)"" IL_003c: ldloc.0 IL_003d: dup IL_003e: brtrue.s IL_0044 IL_0040: pop IL_0041: ldloc.1 IL_0042: stloc.2 IL_0043: ldloc.2 IL_0044: callvirt ""System.Type object.GetType()"" IL_0049: callvirt ""string System.Reflection.MemberInfo.Name.get"" IL_004e: call ""void System.Console.Write(string)"" IL_0053: ldloc.1 IL_0054: stloc.2 IL_0055: ldloc.2 IL_0056: dup IL_0057: brtrue.s IL_005b IL_0059: pop IL_005a: ldloc.0 IL_005b: callvirt ""System.Type object.GetType()"" IL_0060: callvirt ""string System.Reflection.MemberInfo.Name.get"" IL_0065: call ""void System.Console.Write(string)"" IL_006a: ret } "; var verifier = CompileAndVerify(source, expectedOutput: @"BDBD"); verifier.VerifyIL("Program.Main", expectedIL); } [WorkItem(545408, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545408")] [Fact] public void TestInterfaceContravarianceConversions() { string source = @" using System; interface I<in T> { } class Base { } class Derived : Base { } class B : I<Base> { } class D : I<Derived> { } class Program { static void Main() { bool testFlag = true; I<Base> baseInstance = new B(); I<Derived> derivedInstance = new D(); I<Derived> i; i = testFlag ? baseInstance : derivedInstance; Console.Write(i.GetType().Name); i = testFlag ? derivedInstance : baseInstance; Console.Write(i.GetType().Name); i = baseInstance ?? derivedInstance; Console.Write(i.GetType().Name); i = derivedInstance ?? baseInstance; Console.Write(i.GetType().Name); } } "; string expectedIL = @" { // Code size 107 (0x6b) .maxstack 2 .locals init (I<Base> V_0, //baseInstance I<Derived> V_1, //derivedInstance I<Derived> V_2) IL_0000: ldc.i4.1 IL_0001: newobj ""B..ctor()"" IL_0006: stloc.0 IL_0007: newobj ""D..ctor()"" IL_000c: stloc.1 IL_000d: dup IL_000e: brtrue.s IL_0013 IL_0010: ldloc.1 IL_0011: br.s IL_0016 IL_0013: ldloc.0 IL_0014: stloc.2 IL_0015: ldloc.2 IL_0016: callvirt ""System.Type object.GetType()"" IL_001b: callvirt ""string System.Reflection.MemberInfo.Name.get"" IL_0020: call ""void System.Console.Write(string)"" IL_0025: brtrue.s IL_002c IL_0027: ldloc.0 IL_0028: stloc.2 IL_0029: ldloc.2 IL_002a: br.s IL_002d IL_002c: ldloc.1 IL_002d: callvirt ""System.Type object.GetType()"" IL_0032: callvirt ""string System.Reflection.MemberInfo.Name.get"" IL_0037: call ""void System.Console.Write(string)"" IL_003c: ldloc.0 IL_003d: stloc.2 IL_003e: ldloc.2 IL_003f: dup IL_0040: brtrue.s IL_0044 IL_0042: pop IL_0043: ldloc.1 IL_0044: callvirt ""System.Type object.GetType()"" IL_0049: callvirt ""string System.Reflection.MemberInfo.Name.get"" IL_004e: call ""void System.Console.Write(string)"" IL_0053: ldloc.1 IL_0054: dup IL_0055: brtrue.s IL_005b IL_0057: pop IL_0058: ldloc.0 IL_0059: stloc.2 IL_005a: ldloc.2 IL_005b: callvirt ""System.Type object.GetType()"" IL_0060: callvirt ""string System.Reflection.MemberInfo.Name.get"" IL_0065: call ""void System.Console.Write(string)"" IL_006a: ret } "; var verifier = CompileAndVerify(source, expectedOutput: @"BDBD"); verifier.VerifyIL("Program.Main", expectedIL); } [Fact] public void TestBug7196() { string source = @" using System; using System.Linq.Expressions; using System.Linq; using System.Collections; using System.Collections.Generic; using System.Security; [assembly: SecurityTransparent()] class Program { private static bool testFlag = false; static void Main() { IEnumerable<string> v1 = Enumerable.Repeat<string>(""string"", 1); IEnumerable<object> v2 = Enumerable.Empty<object>(); IEnumerable<object> v3 = testFlag ? v2 : v1; if (!testFlag){ Console.WriteLine(v3.Count()); } } } "; string expectedIL = @" { // Code size 51 (0x33) .maxstack 2 .locals init (System.Collections.Generic.IEnumerable<string> V_0, //v1 System.Collections.Generic.IEnumerable<object> V_1, //v2 System.Collections.Generic.IEnumerable<object> V_2, //v3 System.Collections.Generic.IEnumerable<object> V_3) IL_0000: ldstr ""string"" IL_0005: ldc.i4.1 IL_0006: call ""System.Collections.Generic.IEnumerable<string> System.Linq.Enumerable.Repeat<string>(string, int)"" IL_000b: stloc.0 IL_000c: call ""System.Collections.Generic.IEnumerable<object> System.Linq.Enumerable.Empty<object>()"" IL_0011: stloc.1 IL_0012: ldsfld ""bool Program.testFlag"" IL_0017: brtrue.s IL_001e IL_0019: ldloc.0 IL_001a: stloc.3 IL_001b: ldloc.3 IL_001c: br.s IL_001f IL_001e: ldloc.1 IL_001f: stloc.2 IL_0020: ldsfld ""bool Program.testFlag"" IL_0025: brtrue.s IL_0032 IL_0027: ldloc.2 IL_0028: call ""int System.Linq.Enumerable.Count<object>(System.Collections.Generic.IEnumerable<object>)"" IL_002d: call ""void System.Console.WriteLine(int)"" IL_0032: ret } "; var verifier = CompileAndVerify( new string[] { source }, expectedOutput: "1", symbolValidator: validator, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All)); verifier.VerifyIL("Program.Main", expectedIL); void validator(ModuleSymbol module) { var type = module.ContainingAssembly.GetTypeByMetadataName("Program"); Assert.Null(type.GetMember(".cctor")); } } [Fact] public void TestBug7196a() { string source = @" using System; using System.Linq.Expressions; using System.Linq; using System.Collections; using System.Collections.Generic; using System.Security; [assembly: SecurityTransparent()] class Program { private static bool testFlag = true; static void Main() { IEnumerable<string> v1 = Enumerable.Repeat<string>(""string"", 1); IEnumerable<object> v2 = Enumerable.Empty<object>(); IEnumerable<object> v3 = v1 ?? v2; if (testFlag){ Console.WriteLine(v3.Count()); } } } "; string expectedIL = @" { // Code size 44 (0x2c) .maxstack 2 .locals init (System.Collections.Generic.IEnumerable<object> V_0, //v2 System.Collections.Generic.IEnumerable<object> V_1, //v3 System.Collections.Generic.IEnumerable<object> V_2) IL_0000: ldstr ""string"" IL_0005: ldc.i4.1 IL_0006: call ""System.Collections.Generic.IEnumerable<string> System.Linq.Enumerable.Repeat<string>(string, int)"" IL_000b: call ""System.Collections.Generic.IEnumerable<object> System.Linq.Enumerable.Empty<object>()"" IL_0010: stloc.0 IL_0011: stloc.2 IL_0012: ldloc.2 IL_0013: dup IL_0014: brtrue.s IL_0018 IL_0016: pop IL_0017: ldloc.0 IL_0018: stloc.1 IL_0019: ldsfld ""bool Program.testFlag"" IL_001e: brfalse.s IL_002b IL_0020: ldloc.1 IL_0021: call ""int System.Linq.Enumerable.Count<object>(System.Collections.Generic.IEnumerable<object>)"" IL_0026: call ""void System.Console.WriteLine(int)"" IL_002b: ret } "; var verifier = CompileAndVerify(new string[] { source }, expectedOutput: "1"); verifier.VerifyIL("Program.Main", expectedIL); } [Fact] public void TestBug7196b() { string source = @" using System; using System.Linq.Expressions; using System.Linq; using System.Collections; using System.Collections.Generic; using System.Security; [assembly: SecurityTransparent()] class Program { private static bool testFlag = true; static void Main() { string[] v1 = Enumerable.Repeat<string>(""string"", 1).ToArray(); object[] v2 = Enumerable.Empty<object>().ToArray(); object[] v3 = v1 ?? v2; if (testFlag){ Console.WriteLine(v3.Length); } } } "; string expectedIL = @" { // Code size 51 (0x33) .maxstack 2 .locals init (object[] V_0, //v2 object[] V_1, //v3 object[] V_2) IL_0000: ldstr ""string"" IL_0005: ldc.i4.1 IL_0006: call ""System.Collections.Generic.IEnumerable<string> System.Linq.Enumerable.Repeat<string>(string, int)"" IL_000b: call ""string[] System.Linq.Enumerable.ToArray<string>(System.Collections.Generic.IEnumerable<string>)"" IL_0010: call ""System.Collections.Generic.IEnumerable<object> System.Linq.Enumerable.Empty<object>()"" IL_0015: call ""object[] System.Linq.Enumerable.ToArray<object>(System.Collections.Generic.IEnumerable<object>)"" IL_001a: stloc.0 IL_001b: stloc.2 IL_001c: ldloc.2 IL_001d: dup IL_001e: brtrue.s IL_0022 IL_0020: pop IL_0021: ldloc.0 IL_0022: stloc.1 IL_0023: ldsfld ""bool Program.testFlag"" IL_0028: brfalse.s IL_0032 IL_002a: ldloc.1 IL_002b: ldlen IL_002c: conv.i4 IL_002d: call ""void System.Console.WriteLine(int)"" IL_0032: ret } "; var verifier = CompileAndVerify(new string[] { source }, expectedOutput: "1"); verifier.VerifyIL("Program.Main", expectedIL); } [Fact] public void TestBug7196c() { string source = @" using System; using System.Linq.Expressions; using System.Linq; using System.Collections; using System.Collections.Generic; using System.Security; [assembly: SecurityTransparent()] class Program { private static bool testFlag = true; static void Main() { IEnumerable<string>[] v1 = new IEnumerable<string>[] { Enumerable.Repeat<string>(""string"", 1)}; IEnumerable<object>[] v2 = new IEnumerable<object>[] { Enumerable.Empty<object>()}; IEnumerable<object>[] v3 = v1 ?? v2; if (testFlag){ Console.WriteLine(v3.Length); } } } "; string expectedIL = @" { // Code size 61 (0x3d) .maxstack 5 .locals init (System.Collections.Generic.IEnumerable<string>[] V_0, //v1 System.Collections.Generic.IEnumerable<object>[] V_1, //v2 System.Collections.Generic.IEnumerable<object>[] V_2, //v3 System.Collections.Generic.IEnumerable<object>[] V_3) IL_0000: ldc.i4.1 IL_0001: newarr ""System.Collections.Generic.IEnumerable<string>"" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldstr ""string"" IL_000d: ldc.i4.1 IL_000e: call ""System.Collections.Generic.IEnumerable<string> System.Linq.Enumerable.Repeat<string>(string, int)"" IL_0013: stelem.ref IL_0014: stloc.0 IL_0015: ldc.i4.1 IL_0016: newarr ""System.Collections.Generic.IEnumerable<object>"" IL_001b: dup IL_001c: ldc.i4.0 IL_001d: call ""System.Collections.Generic.IEnumerable<object> System.Linq.Enumerable.Empty<object>()"" IL_0022: stelem.ref IL_0023: stloc.1 IL_0024: ldloc.0 IL_0025: stloc.3 IL_0026: ldloc.3 IL_0027: dup IL_0028: brtrue.s IL_002c IL_002a: pop IL_002b: ldloc.1 IL_002c: stloc.2 IL_002d: ldsfld ""bool Program.testFlag"" IL_0032: brfalse.s IL_003c IL_0034: ldloc.2 IL_0035: ldlen IL_0036: conv.i4 IL_0037: call ""void System.Console.WriteLine(int)"" IL_003c: ret } "; var verifier = CompileAndVerify(new string[] { source }, expectedOutput: "1"); verifier.VerifyIL("Program.Main", expectedIL); } [Fact] public void TestBug7196d() { string source = @" using System; using System.Linq.Expressions; using System.Linq; using System.Collections; using System.Collections.Generic; using System.Security; [assembly: SecurityTransparent()] class Program { private static bool testFlag = true; static void Main() { IEnumerable<string>[] v1 = new IEnumerable<string>[] { Enumerable.Repeat<string>(""string"", 1)}; IEnumerable[] v2 = new IEnumerable<object>[] { Enumerable.Empty<object>()}; IEnumerable[] v3 = v1 ?? v2; if (testFlag){ Console.WriteLine(v3.Length); } } } "; string expectedIL = @" { // Code size 63 (0x3f) .maxstack 5 .locals init (System.Collections.Generic.IEnumerable<string>[] V_0, //v1 System.Collections.IEnumerable[] V_1, //v2 System.Collections.IEnumerable[] V_2, //v3 System.Collections.IEnumerable[] V_3) IL_0000: ldc.i4.1 IL_0001: newarr ""System.Collections.Generic.IEnumerable<string>"" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldstr ""string"" IL_000d: ldc.i4.1 IL_000e: call ""System.Collections.Generic.IEnumerable<string> System.Linq.Enumerable.Repeat<string>(string, int)"" IL_0013: stelem.ref IL_0014: stloc.0 IL_0015: ldc.i4.1 IL_0016: newarr ""System.Collections.Generic.IEnumerable<object>"" IL_001b: dup IL_001c: ldc.i4.0 IL_001d: call ""System.Collections.Generic.IEnumerable<object> System.Linq.Enumerable.Empty<object>()"" IL_0022: stelem.ref IL_0023: stloc.3 IL_0024: ldloc.3 IL_0025: stloc.1 IL_0026: ldloc.0 IL_0027: stloc.3 IL_0028: ldloc.3 IL_0029: dup IL_002a: brtrue.s IL_002e IL_002c: pop IL_002d: ldloc.1 IL_002e: stloc.2 IL_002f: ldsfld ""bool Program.testFlag"" IL_0034: brfalse.s IL_003e IL_0036: ldloc.2 IL_0037: ldlen IL_0038: conv.i4 IL_0039: call ""void System.Console.WriteLine(int)"" IL_003e: ret } "; var verifier = CompileAndVerify(new string[] { source }, expectedOutput: "1"); verifier.VerifyIL("Program.Main", expectedIL); } [WorkItem(545408, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545408")] [Fact] public void TestVarianceConversions() { string source = @" using System; using System.Linq.Expressions; using System.Linq; using System.Collections; using System.Collections.Generic; using System.Security; [assembly: SecurityTransparent()] namespace TernaryAndVarianceConversion { delegate void CovariantDelegateWithVoidReturn<out T>(); delegate T CovariantDelegateWithValidReturn<out T>(); delegate void ContravariantDelegateVoidReturn<in T>(); delegate void ContravariantDelegateWithValidInParm<in T>(T inVal); interface ICovariantInterface<out T> { void CovariantInterfaceMethodWithVoidReturn(); T CovariantInterfaceMethodWithValidReturn(); T CovariantInterfacePropertyWithValidGetter { get; } void Test(); } interface IContravariantInterface<in T> { void ContravariantInterfaceMethodWithVoidReturn(); void ContravariantInterfaceMethodWithValidInParm(T inVal); T ContravariantInterfacePropertyWithValidSetter { set; } void Test(); } class CovariantInterfaceImpl<T> : ICovariantInterface<T> { public void CovariantInterfaceMethodWithVoidReturn() { } public T CovariantInterfaceMethodWithValidReturn() { return default(T); } public T CovariantInterfacePropertyWithValidGetter { get { return default(T); } } public void Test() { Console.WriteLine(""{0}"", typeof(T)); } } class ContravariantInterfaceImpl<T> : IContravariantInterface<T> { public void ContravariantInterfaceMethodWithVoidReturn() { } public void ContravariantInterfaceMethodWithValidInParm(T inVal) { } public T ContravariantInterfacePropertyWithValidSetter { set { } } public void Test() { Console.WriteLine(""{0}"", typeof(T)); } } class Animal { } class Mammal : Animal { } class Program { static void Test(bool testFlag) { Console.WriteLine(""Testing with ternary test flag == {0}"", testFlag); // Repro case for bug 7196 IEnumerable<object> EnumerableOfObject = (testFlag ? Enumerable.Repeat<string>(""string"", 1) : Enumerable.Empty<object>()); Console.WriteLine(""{0}"", EnumerableOfObject.Count()); // Covariant implicit conversion for delegates CovariantDelegateWithVoidReturn<Animal> covariantDelegateWithVoidReturnOfAnimal = () => { Console.WriteLine(""{0}"", typeof(Animal)); }; CovariantDelegateWithVoidReturn<Mammal> covariantDelegateWithVoidReturnOfMammal = () => { Console.WriteLine(""{0}"", typeof(Mammal)); }; CovariantDelegateWithVoidReturn<Animal> covariantDelegateWithVoidReturnOfAnimalTest; covariantDelegateWithVoidReturnOfAnimalTest = testFlag ? covariantDelegateWithVoidReturnOfMammal : covariantDelegateWithVoidReturnOfAnimal; covariantDelegateWithVoidReturnOfAnimalTest(); covariantDelegateWithVoidReturnOfAnimalTest = testFlag ? covariantDelegateWithVoidReturnOfAnimal : covariantDelegateWithVoidReturnOfMammal; covariantDelegateWithVoidReturnOfAnimalTest(); CovariantDelegateWithValidReturn<Animal> covariantDelegateWithValidReturnOfAnimal = () => { Console.WriteLine(""{0}"", typeof(Animal)); return default(Animal); }; CovariantDelegateWithValidReturn<Mammal> covariantDelegateWithValidReturnOfMammal = () => { Console.WriteLine(""{0}"", typeof(Mammal)); return default(Mammal); }; CovariantDelegateWithValidReturn<Animal> covariantDelegateWithValidReturnOfAnimalTest; covariantDelegateWithValidReturnOfAnimalTest = testFlag ? covariantDelegateWithValidReturnOfMammal : covariantDelegateWithValidReturnOfAnimal; covariantDelegateWithValidReturnOfAnimalTest(); covariantDelegateWithValidReturnOfAnimalTest = testFlag ? covariantDelegateWithValidReturnOfAnimal : covariantDelegateWithValidReturnOfMammal; covariantDelegateWithValidReturnOfAnimalTest(); // Contravariant implicit conversion for delegates ContravariantDelegateVoidReturn<Animal> contravariantDelegateVoidReturnOfAnimal = () => { Console.WriteLine(""{0}"", typeof(Animal)); }; ContravariantDelegateVoidReturn<Mammal> contravariantDelegateVoidReturnOfMammal = () => { Console.WriteLine(""{0}"", typeof(Mammal)); }; ContravariantDelegateVoidReturn<Mammal> contravariantDelegateVoidReturnOfMammalTest; contravariantDelegateVoidReturnOfMammalTest = testFlag ? contravariantDelegateVoidReturnOfMammal : contravariantDelegateVoidReturnOfAnimal; contravariantDelegateVoidReturnOfMammalTest(); contravariantDelegateVoidReturnOfMammalTest = testFlag ? contravariantDelegateVoidReturnOfAnimal : contravariantDelegateVoidReturnOfMammal; contravariantDelegateVoidReturnOfMammalTest(); ContravariantDelegateWithValidInParm<Animal> contravariantDelegateWithValidInParmOfAnimal = (Animal) => { Console.WriteLine(""{0}"", typeof(Animal)); }; ContravariantDelegateWithValidInParm<Mammal> contravariantDelegateWithValidInParmOfMammal = (Mammal) => { Console.WriteLine(""{0}"", typeof(Mammal)); }; ContravariantDelegateWithValidInParm<Mammal> contravariantDelegateWithValidInParmOfMammalTest; contravariantDelegateWithValidInParmOfMammalTest = testFlag ? contravariantDelegateWithValidInParmOfMammal : contravariantDelegateWithValidInParmOfAnimal; contravariantDelegateWithValidInParmOfMammalTest(default(Mammal)); contravariantDelegateWithValidInParmOfMammalTest = testFlag ? contravariantDelegateWithValidInParmOfAnimal : contravariantDelegateWithValidInParmOfMammal; contravariantDelegateWithValidInParmOfMammalTest(default(Mammal)); // Covariant implicit conversion for interfaces ICovariantInterface<Animal> covariantInterfaceOfAnimal = new CovariantInterfaceImpl<Animal>(); ICovariantInterface<Mammal> covariantInterfaceOfMammal = new CovariantInterfaceImpl<Mammal>(); ICovariantInterface<Animal> covariantInterfaceOfAnimalTest; covariantInterfaceOfAnimalTest = testFlag ? covariantInterfaceOfMammal : covariantInterfaceOfAnimal; covariantInterfaceOfAnimalTest.Test(); covariantInterfaceOfAnimalTest = testFlag ? covariantInterfaceOfAnimal : covariantInterfaceOfMammal; covariantInterfaceOfAnimalTest.Test(); // Contravariant implicit conversion for interfaces IContravariantInterface<Animal> contravariantInterfaceOfAnimal = new ContravariantInterfaceImpl<Animal>(); IContravariantInterface<Mammal> contravariantInterfaceOfMammal = new ContravariantInterfaceImpl<Mammal>(); IContravariantInterface<Mammal> contravariantInterfaceOfMammalTest; contravariantInterfaceOfMammalTest = testFlag ? contravariantInterfaceOfMammal : contravariantInterfaceOfAnimal; contravariantInterfaceOfMammalTest.Test(); contravariantInterfaceOfMammalTest = testFlag ? contravariantInterfaceOfAnimal : contravariantInterfaceOfMammal; contravariantInterfaceOfMammalTest.Test(); // With explicit casting covariantDelegateWithVoidReturnOfAnimalTest = testFlag ? (CovariantDelegateWithVoidReturn<Animal>)covariantDelegateWithVoidReturnOfMammal : covariantDelegateWithVoidReturnOfAnimal; covariantDelegateWithVoidReturnOfAnimalTest(); covariantDelegateWithVoidReturnOfAnimalTest = testFlag ? covariantDelegateWithVoidReturnOfAnimal : (CovariantDelegateWithVoidReturn<Animal>)covariantDelegateWithVoidReturnOfMammal; covariantDelegateWithVoidReturnOfAnimalTest(); // With parens covariantDelegateWithVoidReturnOfAnimalTest = testFlag ? (covariantDelegateWithVoidReturnOfMammal) : covariantDelegateWithVoidReturnOfAnimal; covariantDelegateWithVoidReturnOfAnimalTest(); covariantDelegateWithVoidReturnOfAnimalTest = testFlag ? covariantDelegateWithVoidReturnOfAnimal : (covariantDelegateWithVoidReturnOfMammal); covariantDelegateWithVoidReturnOfAnimalTest(); covariantDelegateWithVoidReturnOfAnimalTest = testFlag ? ((CovariantDelegateWithVoidReturn<Animal>)covariantDelegateWithVoidReturnOfMammal) : covariantDelegateWithVoidReturnOfAnimal; covariantDelegateWithVoidReturnOfAnimalTest(); covariantDelegateWithVoidReturnOfAnimalTest = testFlag ? covariantDelegateWithVoidReturnOfAnimal : ((CovariantDelegateWithVoidReturn<Animal>)covariantDelegateWithVoidReturnOfMammal); covariantDelegateWithVoidReturnOfAnimalTest(); // Bug 291602 int[] intarr = { 1, 2, 3 }; IList<int> intlist = new List<int>(intarr); IList<int> intternary = testFlag ? intarr : intlist; Console.WriteLine(intternary); } static void Main(string[] args) { Test(true); Test(false); } } } "; var compilation = CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.ReleaseExe); CompileAndVerify(compilation, expectedOutput: @"Testing with ternary test flag == True 1 TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal System.Int32[] Testing with ternary test flag == False 0 TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal TernaryAndVarianceConversion.Animal TernaryAndVarianceConversion.Mammal System.Collections.Generic.List`1[System.Int32] "); } [WorkItem(528424, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528424")] [Fact()] public void TestErrorOperand() { var source = @"class C { static object M(bool b, C c, D d) { return b ? c : d; } }"; CreateCompilation(source).VerifyDiagnostics( // (3,34): error CS0246: The type or namespace name 'D' could not be found (are you missing a using directive or an assembly reference?) Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "D").WithArguments("D")); } private static void TestConditional(string conditionalExpression, string? expectedType, params DiagnosticDescription[] expectedDiagnostics) { TestConditional(conditionalExpression, expectedType, null, expectedDiagnostics); } private static void TestConditional(string conditionalExpression, string? expectedType, CSharpParseOptions? parseOptions, params DiagnosticDescription[] expectedDiagnostics) { if (parseOptions is null) { TestConditionalCore(conditionalExpression, expectedType, TestOptions.Regular8, expectedDiagnostics); TestConditionalCore(conditionalExpression, expectedType, TestOptions.Regular8.WithLanguageVersion(MessageID.IDS_FeatureTargetTypedConditional.RequiredVersion()), expectedDiagnostics); } else { TestConditionalCore(conditionalExpression, expectedType, parseOptions, expectedDiagnostics); } } private static void TestConditionalCore(string conditionalExpression, string? expectedType, CSharpParseOptions parseOptions, params DiagnosticDescription[] expectedDiagnostics) { string source = $@" class C {{ void Test<T, U>() {{ _ = {conditionalExpression}; }} int GetInt() {{ return 1; }} void GetVoid() {{ return ; }} bool GetBool() {{ return true; }} short GetShort() {{ return 1; }} char GetChar() {{ return 'a'; }} double GetDouble() {{ return 1.5; }} string GetString() {{ return ""hello""; }} object GetObject() {{ return new object(); }} C GetUserNonGeneric() {{ return new C(); }} D<T> GetUserGeneric<T>() {{ return new D<T>(); }} T GetTypeParameter<T>() {{ return default(T); }} I<T, U> GetVariantInterface<T, U>() {{ return null; }} }} class D<T> {{ }} public enum @color {{ Red, Blue, Green }}; interface I<in T, out U> {{ }}"; var tree = Parse(source, options: parseOptions); var comp = CreateCompilation(tree); comp.VerifyDiagnostics(expectedDiagnostics); var compUnit = tree.GetCompilationUnitRoot(); var classC = (TypeDeclarationSyntax)compUnit.Members.First(); var methodTest = (MethodDeclarationSyntax)classC.Members.First(); var stmt = (ExpressionStatementSyntax)methodTest.Body!.Statements.First(); var assignment = (AssignmentExpressionSyntax)stmt.Expression; var conditionalExpr = (ConditionalExpressionSyntax)assignment.Right; var model = comp.GetSemanticModel(tree); if (expectedType != null) { Assert.Equal(expectedType, model.GetTypeInfo(conditionalExpr).Type.ToTestDisplayString()); if (!expectedDiagnostics.Any()) { Assert.Equal(SpecialType.System_Boolean, model.GetTypeInfo(conditionalExpr.Condition).Type!.SpecialType); Assert.Equal(expectedType, model.GetTypeInfo(conditionalExpr.WhenTrue).ConvertedType.ToTestDisplayString()); //in parent to catch conversion Assert.Equal(expectedType, model.GetTypeInfo(conditionalExpr.WhenFalse).ConvertedType.ToTestDisplayString()); //in parent to catch conversion } } } [Fact, WorkItem(4028, "https://github.com/dotnet/roslyn/issues/4028")] public void ConditionalAccessToEvent_01() { string source = @" using System; class TestClass { event Action test; public static void Test(TestClass receiver) { Console.WriteLine(receiver?.test); } static void Main() { Console.WriteLine(""----""); Test(null); Console.WriteLine(""----""); Test(new TestClass() {test = Main}); Console.WriteLine(""----""); } } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); CompileAndVerify(compilation, expectedOutput: @"---- ---- System.Action ----"); var tree = compilation.SyntaxTrees.Single(); var memberBinding = tree.GetRoot().DescendantNodes().OfType<MemberBindingExpressionSyntax>().Single(); var access = (ConditionalAccessExpressionSyntax)memberBinding.Parent!; Assert.Equal(".test", memberBinding.ToString()); Assert.Equal("receiver?.test", access.ToString()); var model = compilation.GetSemanticModel(tree); Assert.Equal("event System.Action TestClass.test", model.GetSymbolInfo(memberBinding).Symbol.ToTestDisplayString()); Assert.Equal("event System.Action TestClass.test", model.GetSymbolInfo(memberBinding.Name).Symbol.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(access).Symbol); } [Fact, WorkItem(4028, "https://github.com/dotnet/roslyn/issues/4028")] public void ConditionalAccessToEvent_02() { string source = @" using System; class TestClass { event Action test; public static void Test(TestClass receiver) { receiver?.test(); } static void Main() { Console.WriteLine(""----""); Test(null); Console.WriteLine(""----""); Test(new TestClass() {test = Target}); Console.WriteLine(""----""); } static void Target() { Console.WriteLine(""Target""); } } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); CompileAndVerify(compilation, expectedOutput: @"---- ---- Target ----"); var tree = compilation.SyntaxTrees.Single(); var memberBinding = tree.GetRoot().DescendantNodes().OfType<MemberBindingExpressionSyntax>().Single(); var invocation = (InvocationExpressionSyntax)memberBinding.Parent!; var access = (ConditionalAccessExpressionSyntax)invocation.Parent!; Assert.Equal(".test", memberBinding.ToString()); Assert.Equal(".test()", invocation.ToString()); Assert.Equal("receiver?.test()", access.ToString()); var model = compilation.GetSemanticModel(tree); Assert.Equal("event System.Action TestClass.test", model.GetSymbolInfo(memberBinding).Symbol.ToTestDisplayString()); Assert.Equal("event System.Action TestClass.test", model.GetSymbolInfo(memberBinding.Name).Symbol.ToTestDisplayString()); Assert.Equal("void System.Action.Invoke()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(access).Symbol); } [Fact, WorkItem(4028, "https://github.com/dotnet/roslyn/issues/4028")] public void ConditionalAccessToEvent_03() { string source = @" using System; class TestClass { event Action test; public static void Test(TestClass receiver) { receiver?.test += Main; } static void Main() { } } "; var compilation = CreateCompilation(source, options: TestOptions.DebugDll); compilation.VerifyDiagnostics( // (6,18): warning CS0067: The event 'TestClass.test' is never used // event Action test; Diagnostic(ErrorCode.WRN_UnreferencedEvent, "test").WithArguments("TestClass.test").WithLocation(6, 18) ); var tree = compilation.SyntaxTrees.Single(); var memberBinding = tree.GetRoot().DescendantNodes().OfType<MemberBindingExpressionSyntax>().Single(); var assignment = (AssignmentExpressionSyntax)memberBinding.Parent!; var access = (ConditionalAccessExpressionSyntax)assignment.Parent!; Assert.Equal(".test", memberBinding.ToString()); Assert.Equal(".test += Main", assignment.ToString()); Assert.Equal("receiver?.test += Main", access.ToString()); var model = compilation.GetSemanticModel(tree); Assert.Equal("event System.Action TestClass.test", model.GetSymbolInfo(memberBinding).Symbol.ToTestDisplayString()); Assert.Equal("event System.Action TestClass.test", model.GetSymbolInfo(memberBinding.Name).Symbol.ToTestDisplayString()); Assert.Equal("void TestClass.test.add", model.GetSymbolInfo(assignment).Symbol.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(access).Symbol); } [Fact(), WorkItem(4615, "https://github.com/dotnet/roslyn/issues/4615")] public void ConditionalAndConditionalMethods() { string source = @" class Program { static void Main(string[] args) { TestClass.Create().Test(); TestClass.Create().Self().Test(); System.Console.WriteLine(""---""); TestClass.Create()?.Test(); TestClass.Create()?.Self().Test(); TestClass.Create()?.Self()?.Test(); } } class TestClass { [System.Diagnostics.Conditional(""DEBUG"")] public void Test() { System.Console.WriteLine(""Test""); } public static TestClass Create() { System.Console.WriteLine(""Create""); return new TestClass(); } public TestClass Self() { System.Console.WriteLine(""Self""); return this; } } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: CSharpParseOptions.Default.WithPreprocessorSymbols("DEBUG")); CompileAndVerify(compilation, expectedOutput: @"Create Test Create Self Test --- Create Test Create Self Test Create Self Test "); compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); CompileAndVerify(compilation, expectedOutput: "---"); } [Fact, WorkItem(1198816, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1198816/")] public void DefiniteAssignment_UnconvertedConditionalOperator() { var source = @"class Program { static void Main() { _ = new(bad) ? null : new object(); } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (5,17): error CS0103: The name 'bad' does not exist in the current context // _ = new(bad) ? null : new object(); Diagnostic(ErrorCode.ERR_NameNotInContext, "bad").WithArguments("bad").WithLocation(5, 17) ); } } }