File: Semantics\NullCoalesceAssignmentTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Semantic\Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests)
// 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.
 
#nullable disable
 
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
{
    [CompilerTrait(CompilerFeature.NullCoalescingAssignment)]
    public partial class NullCoalesceAssignmentTests : SemanticModelTestBase
    {
        [Fact]
        public void CoalescingAssignment_NoConversion()
        {
            var source = @"
class C
{
    void M(C c1, C c2)
    {
        c1 ??= c2;
    }
}";
 
            var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
            comp.VerifyDiagnostics();
            var cType = comp.GetTypeByMetadataName("C");
 
            var syntaxTree = comp.SyntaxTrees.Single();
            var syntaxRoot = syntaxTree.GetRoot();
            var semanticModel = comp.GetSemanticModel(syntaxTree);
            var coalesceAssignment = syntaxRoot.DescendantNodes().OfType<AssignmentExpressionSyntax>().Single();
 
            assertTypeInfo(coalesceAssignment);
            assertTypeInfo(coalesceAssignment.Left);
            assertTypeInfo(coalesceAssignment.Right);
 
            void assertTypeInfo(SyntaxNode syntax)
            {
                var typeInfo = semanticModel.GetTypeInfo(syntax);
                Assert.NotEqual(default, typeInfo);
                Assert.NotNull(typeInfo.Type);
                Assert.Equal(cType.GetPublicSymbol(), typeInfo.Type);
                Assert.Equal(cType.GetPublicSymbol(), typeInfo.ConvertedType);
 
            }
        }
 
        [Fact]
        public void CoalescingAssignment_ValueConversion()
        {
            var source = @"
class C
{
    void M(C c1, D d1)
    {
        c1 ??= d1;
    }
}
class D : C {}";
 
            var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
            comp.VerifyDiagnostics();
            var cType = comp.GetTypeByMetadataName("C");
            var dType = comp.GetTypeByMetadataName("D");
 
            var syntaxTree = comp.SyntaxTrees.Single();
            var syntaxRoot = syntaxTree.GetRoot();
            var semanticModel = comp.GetSemanticModel(syntaxTree);
            var coalesceAssignment = syntaxRoot.DescendantNodes().OfType<AssignmentExpressionSyntax>().Single();
 
            assertTypeInfo(coalesceAssignment);
            assertTypeInfo(coalesceAssignment.Left);
 
            var whenNullTypeInfo = semanticModel.GetTypeInfo(coalesceAssignment.Right);
            Assert.NotEqual(default, whenNullTypeInfo);
            Assert.Equal(dType.GetPublicSymbol(), whenNullTypeInfo.Type);
            Assert.Equal(cType, whenNullTypeInfo.ConvertedType.GetSymbol());
 
            void assertTypeInfo(SyntaxNode syntax)
            {
                var typeInfo = semanticModel.GetTypeInfo(syntax);
                Assert.NotEqual(default, typeInfo);
                Assert.NotNull(typeInfo.Type);
                Assert.Equal(cType, typeInfo.Type.GetSymbol());
                Assert.Equal(cType.GetPublicSymbol(), typeInfo.ConvertedType);
 
            }
        }
 
        [Fact]
        public void CoalescingAssignment_AsConvertedExpression()
        {
            var source = @"
class C
{
    void M(D d1, D d2)
    {
        M2(d1 ??= d1);
    }
    void M2(C c) {}
}
class D : C {}";
 
            var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
            comp.VerifyDiagnostics();
            var cType = comp.GetTypeByMetadataName("C");
            var dType = comp.GetTypeByMetadataName("D");
 
            var syntaxTree = comp.SyntaxTrees.Single();
            var syntaxRoot = syntaxTree.GetRoot();
            var semanticModel = comp.GetSemanticModel(syntaxTree);
            var coalesceAssignment = syntaxRoot.DescendantNodes().OfType<AssignmentExpressionSyntax>().Single();
 
            var whenNullTypeInfo = semanticModel.GetTypeInfo(coalesceAssignment);
            Assert.NotEqual(default, whenNullTypeInfo);
            Assert.Equal(dType, whenNullTypeInfo.Type.GetSymbol());
            Assert.Equal(cType.GetPublicSymbol(), whenNullTypeInfo.ConvertedType);
 
            assertTypeInfo(coalesceAssignment.Right);
            assertTypeInfo(coalesceAssignment.Left);
 
            void assertTypeInfo(SyntaxNode syntax)
            {
                var typeInfo = semanticModel.GetTypeInfo(syntax);
                Assert.NotEqual(default, typeInfo);
                Assert.NotNull(typeInfo.Type);
                Assert.Equal(dType.GetPublicSymbol(), typeInfo.Type);
                Assert.Equal(dType, typeInfo.ConvertedType.GetSymbol());
 
            }
        }
 
        [Fact]
        public void CoalesceAssignment_ConvertedToNonNullable()
        {
            var source = @"
class C
{
    void M(int? a, int b)
    {
        a ??= b;
    }
}";
 
            var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
            comp.VerifyDiagnostics();
 
            var syntaxTree = comp.SyntaxTrees.Single();
            var syntaxRoot = syntaxTree.GetRoot();
            var semanticModel = comp.GetSemanticModel(syntaxTree);
            var coalesceAssignment = syntaxRoot.DescendantNodes().OfType<AssignmentExpressionSyntax>().Single();
 
            var int32 = comp.GetSpecialType(SpecialType.System_Int32);
            var coalesceType = semanticModel.GetTypeInfo(coalesceAssignment).Type;
 
            Assert.Equal(int32.GetPublicSymbol(), coalesceType);
        }
 
        [Fact]
        public void CoalesceAssignment_DefaultConvertedToNonNullable()
        {
            var source = @"
class C
{
    void M(int? a)
    {
        a ??= default;
    }
}";
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics();
 
            var syntaxTree = comp.SyntaxTrees.Single();
            var syntaxRoot = syntaxTree.GetRoot();
            var semanticModel = comp.GetSemanticModel(syntaxTree);
            var defaultLiteral = syntaxRoot.DescendantNodes().OfType<LiteralExpressionSyntax>().Where(expr => expr.IsKind(SyntaxKind.DefaultLiteralExpression)).Single();
 
            Assert.Equal(SpecialType.System_Int32, semanticModel.GetTypeInfo(defaultLiteral).Type.SpecialType);
            Assert.Equal(SpecialType.System_Int32, semanticModel.GetTypeInfo(defaultLiteral).ConvertedType.SpecialType);
        }
 
        [Fact]
        public void ErrorRecovery_ImplicitObjectCreation_ReferenceType()
        {
            var source = """
                class C
                {
                    void M()
                    {
                        C c = default;
                        c ??= new(a);
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (6,19): error CS0103: The name 'a' does not exist in the current context
                //         c ??= new(a);
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 19));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var objectCreationExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.Equal("C", objectCreationExpressionTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("C", objectCreationExpressionTypeInfo.ConvertedType.ToTestDisplayString());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.Null(objectCreationExpressionSymbolInfo.Symbol);
            Assert.Equal(CandidateReason.OverloadResolutionFailure, objectCreationExpressionSymbolInfo.CandidateReason);
            AssertEx.SetEqual(["C..ctor()"], objectCreationExpressionSymbolInfo.CandidateSymbols.ToTestDisplayStrings());
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            AssertEx.SetEqual(["C..ctor()"], objectCreationExpressionMemberGroup.ToTestDisplayStrings());
        }
 
        [Fact]
        public void ErrorRecovery_ImplicitObjectCreation_NullableValueType()
        {
            var source = """
                struct S
                {
                    void M()
                    {
                        S? s = default;
                        s ??= new(a);
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (6,19): error CS0103: The name 'a' does not exist in the current context
                //         s ??= new(a);
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 19));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var objectCreationExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.Equal("S", objectCreationExpressionTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("S", objectCreationExpressionTypeInfo.ConvertedType.ToTestDisplayString());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.Null(objectCreationExpressionSymbolInfo.Symbol);
            Assert.Equal(CandidateReason.OverloadResolutionFailure, objectCreationExpressionSymbolInfo.CandidateReason);
            AssertEx.SetEqual(["S..ctor()"], objectCreationExpressionSymbolInfo.CandidateSymbols.ToTestDisplayStrings());
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            AssertEx.SetEqual(["S..ctor()"], objectCreationExpressionMemberGroup.ToTestDisplayStrings());
        }
 
        [Fact]
        public void ErrorRecovery_ImplicitObjectCreation_NotNullableValueType()
        {
            var source = """
                struct S
                {
                    void M()
                    {
                        S s = default;
                        s ??= new(a);
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (6,19): error CS0103: The name 'a' does not exist in the current context
                //         s ??= new(a);
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 19));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var objectCreationExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.True(objectCreationExpressionTypeInfo.Type.IsErrorType());
            Assert.True(objectCreationExpressionTypeInfo.ConvertedType.IsErrorType());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.True(objectCreationExpressionSymbolInfo.IsEmpty);
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            Assert.Empty(objectCreationExpressionMemberGroup);
        }
 
        [Fact]
        public void ErrorRecovery_ImplicitObjectCreation_UnconstrainedGenericType()
        {
            var source = """
                class C
                {
                    void M<T>() where T : new()
                    {
                        T t = default;
                        t ??= new(a);
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (6,19): error CS0103: The name 'a' does not exist in the current context
                //         t ??= new(a);
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 19));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var objectCreationExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.Equal("T", objectCreationExpressionTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("T", objectCreationExpressionTypeInfo.ConvertedType.ToTestDisplayString());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.Null(objectCreationExpressionSymbolInfo.Symbol);
            Assert.Equal(CandidateReason.NotCreatable, objectCreationExpressionSymbolInfo.CandidateReason);
            AssertEx.SetEqual(["T"], objectCreationExpressionSymbolInfo.CandidateSymbols.ToTestDisplayStrings());
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            Assert.Empty(objectCreationExpressionMemberGroup);
        }
 
        [Fact]
        public void ErrorRecovery_ImplicitObjectCreation_ReferenceGenericType()
        {
            var source = """
                class C
                {
                    void M<T>() where T : class, new()
                    {
                        T t = default;
                        t ??= /*<bind>*/new(a)/*</bind>*/;
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (6,29): error CS0103: The name 'a' does not exist in the current context
                //         t ??= /*<bind>*/new(a)/*</bind>*/;
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 29));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var objectCreationExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.Equal("T", objectCreationExpressionTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("T", objectCreationExpressionTypeInfo.ConvertedType.ToTestDisplayString());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.Null(objectCreationExpressionSymbolInfo.Symbol);
            Assert.Equal(CandidateReason.NotCreatable, objectCreationExpressionSymbolInfo.CandidateReason);
            AssertEx.SetEqual(["T"], objectCreationExpressionSymbolInfo.CandidateSymbols.ToTestDisplayStrings());
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            Assert.Empty(objectCreationExpressionMemberGroup);
        }
 
        [Fact]
        public void ErrorRecovery_ImplicitObjectCreation_ValueGenericType_Nullable()
        {
            var source = """
                class C
                {
                    void M<T>() where T : struct
                    {
                        T? t = default;
                        t ??= new(a);
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (6,19): error CS0103: The name 'a' does not exist in the current context
                //         t ??= new(a);
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 19));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var objectCreationExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.Equal("T", objectCreationExpressionTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("T", objectCreationExpressionTypeInfo.ConvertedType.ToTestDisplayString());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.Null(objectCreationExpressionSymbolInfo.Symbol);
            Assert.Equal(CandidateReason.NotCreatable, objectCreationExpressionSymbolInfo.CandidateReason);
            AssertEx.SetEqual(["T"], objectCreationExpressionSymbolInfo.CandidateSymbols.ToTestDisplayStrings());
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            Assert.Empty(objectCreationExpressionMemberGroup);
        }
 
        [Fact]
        public void ErrorRecovery_ImplicitObjectCreation_ValueGenericType_NotNullable()
        {
            var source = """
                class C
                {
                    void M<T>() where T : struct
                    {
                        T t = default;
                        t ??= new(a);
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (6,19): error CS0103: The name 'a' does not exist in the current context
                //         t ??= new(a);
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 19));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var objectCreationExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.True(objectCreationExpressionTypeInfo.Type.IsErrorType());
            Assert.True(objectCreationExpressionTypeInfo.ConvertedType.IsErrorType());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.True(objectCreationExpressionSymbolInfo.IsEmpty);
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            Assert.Empty(objectCreationExpressionMemberGroup);
        }
 
        [Fact]
        public void ErrorRecovery_CollectionExpression_ReferenceType()
        {
            var source = """
                class C
                {
                    void M()
                    {
                        C[] arr = default;
                        arr ??= [new(a)];
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (6,22): error CS0103: The name 'a' does not exist in the current context
                //         arr ??= [new(a)];
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 22));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var collectionExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single();
            var collectionExpressionTypeInfo = model.GetTypeInfo(collectionExpression);
            Assert.Null(collectionExpressionTypeInfo.Type);
            Assert.Equal("C[]", collectionExpressionTypeInfo.ConvertedType.ToTestDisplayString());
 
            var objectCreationExpression = collectionExpression.DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.Equal("C", objectCreationExpressionTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("C", objectCreationExpressionTypeInfo.ConvertedType.ToTestDisplayString());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.Null(objectCreationExpressionSymbolInfo.Symbol);
            Assert.Equal(CandidateReason.OverloadResolutionFailure, objectCreationExpressionSymbolInfo.CandidateReason);
            AssertEx.SetEqual(["C..ctor()"], objectCreationExpressionSymbolInfo.CandidateSymbols.ToTestDisplayStrings());
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            AssertEx.SetEqual(["C..ctor()"], objectCreationExpressionMemberGroup.ToTestDisplayStrings());
        }
 
        [Fact]
        public void ErrorRecovery_CollectionExpression_NullableValueType()
        {
            var source = """
                using System.Collections.Immutable;
 
                class C
                {
                    void M()
                    {
                        ImmutableArray<C>? arr = default;
                        arr ??= [new(a)];
                    }
                }
                """;
 
            var comp = CreateCompilation(source, targetFramework: TargetFramework.Net100);
            comp.VerifyDiagnostics(
                // (8,22): error CS0103: The name 'a' does not exist in the current context
                //         arr ??= [new(a)];
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(8, 22));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var collectionExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single();
            var collectionExpressionTypeInfo = model.GetTypeInfo(collectionExpression);
            Assert.Null(collectionExpressionTypeInfo.Type);
            Assert.Equal("System.Collections.Immutable.ImmutableArray<C>", collectionExpressionTypeInfo.ConvertedType.ToTestDisplayString());
 
            var objectCreationExpression = collectionExpression.DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.Equal("C", objectCreationExpressionTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("C", objectCreationExpressionTypeInfo.ConvertedType.ToTestDisplayString());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.Null(objectCreationExpressionSymbolInfo.Symbol);
            Assert.Equal(CandidateReason.OverloadResolutionFailure, objectCreationExpressionSymbolInfo.CandidateReason);
            AssertEx.SetEqual(["C..ctor()"], objectCreationExpressionSymbolInfo.CandidateSymbols.ToTestDisplayStrings());
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            AssertEx.SetEqual(["C..ctor()"], objectCreationExpressionMemberGroup.ToTestDisplayStrings());
        }
 
        [Fact]
        public void ErrorRecovery_CollectionExpression_NotNullableValueType()
        {
            var source = """
                using System.Collections.Immutable;
 
                class C
                {
                    void M()
                    {
                        ImmutableArray<int> arr = default;
                        arr ??= [new(a)];
                    }
                }
                """;
 
            var comp = CreateCompilation(source, targetFramework: TargetFramework.Net100);
            comp.VerifyDiagnostics(
                // (8,22): error CS0103: The name 'a' does not exist in the current context
                //         arr ??= [new(a)];
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(8, 22));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var collectionExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single();
            var collectionExpressionTypeInfo = model.GetTypeInfo(collectionExpression);
            Assert.Null(collectionExpressionTypeInfo.Type);
            Assert.Null(collectionExpressionTypeInfo.ConvertedType);
 
            var objectCreationExpression = collectionExpression.DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.True(objectCreationExpressionTypeInfo.Type.IsErrorType());
            Assert.True(objectCreationExpressionTypeInfo.ConvertedType.IsErrorType());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.True(objectCreationExpressionSymbolInfo.IsEmpty);
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            Assert.Empty(objectCreationExpressionMemberGroup);
        }
 
        [Fact]
        public void ErrorRecovery_TargetTypedTuple()
        {
            var source = """
                class C
                {
                    void M()
                    {
                        (C, int)? t = default;
                        t ??= (new(a), 1);
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (6,20): error CS0103: The name 'a' does not exist in the current context
                //         t ??= (new(a), 1);
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 20));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var tupleExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<TupleExpressionSyntax>().Single();
            var tupleTypeInfo = model.GetTypeInfo(tupleExpression);
            Assert.Null(tupleTypeInfo.Type);
            Assert.Equal("(C, System.Int32)", tupleTypeInfo.ConvertedType.ToTestDisplayString());
 
            var objectCreationExpression = tupleExpression.DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.Equal("C", objectCreationExpressionTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("C", objectCreationExpressionTypeInfo.ConvertedType.ToTestDisplayString());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.Null(objectCreationExpressionSymbolInfo.Symbol);
            Assert.Equal(CandidateReason.OverloadResolutionFailure, objectCreationExpressionSymbolInfo.CandidateReason);
            AssertEx.SetEqual(["C..ctor()"], objectCreationExpressionSymbolInfo.CandidateSymbols.ToTestDisplayStrings());
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            AssertEx.SetEqual(["C..ctor()"], objectCreationExpressionMemberGroup.ToTestDisplayStrings());
        }
 
        [Fact]
        public void ErrorRecovery_TargetTypedConditionalExpression_ReferenceType()
        {
            var source = """
                class C
                {
                    void M(bool b)
                    {
                        C c = default;
                        c ??= b ? new(a) : default;
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (6,23): error CS0103: The name 'a' does not exist in the current context
                //         c ??= b ? new(a) : default;
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 23));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var conditionalExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<ConditionalExpressionSyntax>().Single();
            var conditionalExpressionTypeInfo = model.GetTypeInfo(conditionalExpression);
            Assert.Null(conditionalExpressionTypeInfo.Type);
            Assert.Equal("C", conditionalExpressionTypeInfo.ConvertedType.ToTestDisplayString());
 
            var objectCreationExpression = conditionalExpression.DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.Equal("C", objectCreationExpressionTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("C", objectCreationExpressionTypeInfo.ConvertedType.ToTestDisplayString());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.Null(objectCreationExpressionSymbolInfo.Symbol);
            Assert.Equal(CandidateReason.OverloadResolutionFailure, objectCreationExpressionSymbolInfo.CandidateReason);
            AssertEx.SetEqual(["C..ctor()"], objectCreationExpressionSymbolInfo.CandidateSymbols.ToTestDisplayStrings());
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            AssertEx.SetEqual(["C..ctor()"], objectCreationExpressionMemberGroup.ToTestDisplayStrings());
        }
 
        [Fact]
        public void ErrorRecovery_TargetTypedConditionalExpression_NullableValueType()
        {
            var source = """
                struct S
                {
                    void M(bool b)
                    {
                        S? s = default;
                        s ??= b ? new(a) : default;
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (6,23): error CS0103: The name 'a' does not exist in the current context
                //         s ??= b ? new(a) : default;
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 23));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var conditionalExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<ConditionalExpressionSyntax>().Single();
            var conditionalExpressionTypeInfo = model.GetTypeInfo(conditionalExpression);
            Assert.Null(conditionalExpressionTypeInfo.Type);
            Assert.Equal("S", conditionalExpressionTypeInfo.ConvertedType.ToTestDisplayString());
 
            var objectCreationExpression = conditionalExpression.DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.Equal("S", objectCreationExpressionTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("S", objectCreationExpressionTypeInfo.ConvertedType.ToTestDisplayString());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.Null(objectCreationExpressionSymbolInfo.Symbol);
            Assert.Equal(CandidateReason.OverloadResolutionFailure, objectCreationExpressionSymbolInfo.CandidateReason);
            AssertEx.SetEqual(["S..ctor()"], objectCreationExpressionSymbolInfo.CandidateSymbols.ToTestDisplayStrings());
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            AssertEx.SetEqual(["S..ctor()"], objectCreationExpressionMemberGroup.ToTestDisplayStrings());
        }
 
        [Fact]
        public void ErrorRecovery_TargetTypedConditionalExpression_NotNullableValueType()
        {
            var source = """
                struct S
                {
                    void M(bool b)
                    {
                        S s = default;
                        s ??= b ? new(a) : default;
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (6,23): error CS0103: The name 'a' does not exist in the current context
                //         s ??= b ? new(a) : default;
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 23));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var conditionalExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<ConditionalExpressionSyntax>().Single();
            var conditionalExpressionTypeInfo = model.GetTypeInfo(conditionalExpression);
            Assert.True(conditionalExpressionTypeInfo.Type.IsErrorType());
            Assert.True(conditionalExpressionTypeInfo.ConvertedType.IsErrorType());
 
            var objectCreationExpression = conditionalExpression.DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.True(objectCreationExpressionTypeInfo.Type.IsErrorType());
            Assert.True(objectCreationExpressionTypeInfo.ConvertedType.IsErrorType());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.True(objectCreationExpressionSymbolInfo.IsEmpty);
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            Assert.Empty(objectCreationExpressionMemberGroup);
        }
 
        [Fact]
        public void ErrorRecovery_TargetTypedSwitchExpression_ReferenceType()
        {
            var source = """
                class C
                {
                    void M(int i)
                    {
                        C c = default;
                        c ??= i switch
                        {
                            1 => new(a),
                            _ => default,
                        };
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (8,22): error CS0103: The name 'a' does not exist in the current context
                //             1 => new(a),
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(8, 22));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var switchExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<SwitchExpressionSyntax>().Single();
            var switchExpressionTypeInfo = model.GetTypeInfo(switchExpression);
            Assert.Null(switchExpressionTypeInfo.Type);
            Assert.Equal("C", switchExpressionTypeInfo.ConvertedType.ToTestDisplayString());
 
            var objectCreationExpression = switchExpression.DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.Equal("C", objectCreationExpressionTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("C", objectCreationExpressionTypeInfo.ConvertedType.ToTestDisplayString());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.Null(objectCreationExpressionSymbolInfo.Symbol);
            Assert.Equal(CandidateReason.OverloadResolutionFailure, objectCreationExpressionSymbolInfo.CandidateReason);
            AssertEx.SetEqual(["C..ctor()"], objectCreationExpressionSymbolInfo.CandidateSymbols.ToTestDisplayStrings());
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            AssertEx.SetEqual(["C..ctor()"], objectCreationExpressionMemberGroup.ToTestDisplayStrings());
        }
 
        [Fact]
        public void ErrorRecovery_TargetTypedSwitchExpression_NullableValueType()
        {
            var source = """
                struct S
                {
                    void M(int i)
                    {
                        S? s = default;
                        s ??= i switch
                        {
                            1 => new(a),
                            _ => default,
                        };
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (8,22): error CS0103: The name 'a' does not exist in the current context
                //             1 => new(a),
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(8, 22));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var switchExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<SwitchExpressionSyntax>().Single();
            var switchExpressionTypeInfo = model.GetTypeInfo(switchExpression);
            Assert.Null(switchExpressionTypeInfo.Type);
            Assert.Equal("S", switchExpressionTypeInfo.ConvertedType.ToTestDisplayString());
 
            var objectCreationExpression = switchExpression.DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.Equal("S", objectCreationExpressionTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("S", objectCreationExpressionTypeInfo.ConvertedType.ToTestDisplayString());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.Null(objectCreationExpressionSymbolInfo.Symbol);
            Assert.Equal(CandidateReason.OverloadResolutionFailure, objectCreationExpressionSymbolInfo.CandidateReason);
            AssertEx.SetEqual(["S..ctor()"], objectCreationExpressionSymbolInfo.CandidateSymbols.ToTestDisplayStrings());
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            AssertEx.SetEqual(["S..ctor()"], objectCreationExpressionMemberGroup.ToTestDisplayStrings());
        }
 
        [Fact]
        public void ErrorRecovery_TargetTypedSwitchExpression_NotNullableValueType()
        {
            var source = """
                struct S
                {
                    void M(int i)
                    {
                        S s = default;
                        s ??= i switch
                        {
                            1 => new(a),
                            _ => default,
                        };
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (8,22): error CS0103: The name 'a' does not exist in the current context
                //             1 => new(a),
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(8, 22));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var switchExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<SwitchExpressionSyntax>().Single();
            var switchExpressionTypeInfo = model.GetTypeInfo(switchExpression);
            Assert.True(switchExpressionTypeInfo.Type.IsErrorType());
            Assert.True(switchExpressionTypeInfo.ConvertedType.IsErrorType());
 
            var objectCreationExpression = switchExpression.DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.True(objectCreationExpressionTypeInfo.Type.IsErrorType());
            Assert.True(objectCreationExpressionTypeInfo.ConvertedType.IsErrorType());
            var objectCreationExpressionSymbolInfo = model.GetSymbolInfo(objectCreationExpression);
            Assert.True(objectCreationExpressionSymbolInfo.IsEmpty);
            var objectCreationExpressionMemberGroup = model.GetMemberGroup(objectCreationExpression);
            Assert.Empty(objectCreationExpressionMemberGroup);
        }
 
        [Fact]
        public void ErrorRecovery_TargetTypedInterpolatedString_ReferenceType()
        {
            var source = """
                class C
                {
                    void M(int i)
                    {
                        CustomInterpolatedStringHandler h = default;
                        h ??= $"The value is {a}";
                    }
                }
                """;
 
            var comp = CreateCompilation([source, GetInterpolatedStringCustomHandlerType("CustomInterpolatedStringHandler", "class", useBoolReturns: false)]);
            comp.VerifyDiagnostics(
                // (6,31): error CS0103: The name 'a' does not exist in the current context
                //         h ??= $"The value is {a}";
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 31));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var interpolatedString = tree.GetCompilationUnitRoot().DescendantNodes().OfType<InterpolatedStringExpressionSyntax>().Single();
            var interpolatedStringTypeInfo = model.GetTypeInfo(interpolatedString);
            Assert.Equal("System.String", interpolatedStringTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("CustomInterpolatedStringHandler", interpolatedStringTypeInfo.ConvertedType.ToTestDisplayString());
        }
 
        [Fact]
        public void ErrorRecovery_TargetTypedInterpolatedString_NullableValueType()
        {
            var source = """
                class C
                {
                    void M(int i)
                    {
                        CustomInterpolatedStringHandler? h = default;
                        h ??= $"The value is {a}";
                    }
                }
                """;
 
            var comp = CreateCompilation([source, GetInterpolatedStringCustomHandlerType("CustomInterpolatedStringHandler", "struct", useBoolReturns: false)]);
            comp.VerifyDiagnostics(
                // (6,31): error CS0103: The name 'a' does not exist in the current context
                //         h ??= $"The value is {a}";
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 31));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var interpolatedString = tree.GetCompilationUnitRoot().DescendantNodes().OfType<InterpolatedStringExpressionSyntax>().Single();
            var interpolatedStringTypeInfo = model.GetTypeInfo(interpolatedString);
            Assert.Equal("System.String", interpolatedStringTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("CustomInterpolatedStringHandler", interpolatedStringTypeInfo.ConvertedType.ToTestDisplayString());
        }
 
        [Fact]
        public void ErrorRecovery_TargetTypedInterpolatedString_NotNullableValueType()
        {
            var source = """
                class C
                {
                    void M(int i)
                    {
                        CustomInterpolatedStringHandler h = default;
                        h ??= $"The value is {a}";
                    }
                }
                """;
 
            var comp = CreateCompilation([source, GetInterpolatedStringCustomHandlerType("CustomInterpolatedStringHandler", "struct", useBoolReturns: false)]);
            comp.VerifyDiagnostics(
                // (6,31): error CS0103: The name 'a' does not exist in the current context
                //         h ??= $"The value is {a}";
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 31));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var interpolatedString = tree.GetCompilationUnitRoot().DescendantNodes().OfType<InterpolatedStringExpressionSyntax>().Single();
            var interpolatedStringTypeInfo = model.GetTypeInfo(interpolatedString);
            Assert.Equal("System.String", interpolatedStringTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("System.String", interpolatedStringTypeInfo.ConvertedType.ToTestDisplayString());
        }
 
        [Fact]
        public void ErrorRecovery_ReferenceType_ImplicitConversion()
        {
            var source = """
                class C
                {
                    void M()
                    {
                        object o = default;
                        o ??= new C(a);
                    }
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (6,19): error CS1729: 'C' does not contain a constructor that takes 1 arguments
                //         o ??= new C(a);
                Diagnostic(ErrorCode.ERR_BadCtorArgCount, "C").WithArguments("C", "1").WithLocation(6, 19),
                // (6,21): error CS0103: The name 'a' does not exist in the current context
                //         o ??= new C(a);
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 21));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var objectCreationExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<ObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.Equal("C", objectCreationExpressionTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("System.Object", objectCreationExpressionTypeInfo.ConvertedType.ToTestDisplayString());
 
            var conversion = model.GetConversion(objectCreationExpression);
            Assert.Equal(ConversionKind.ImplicitReference, conversion.Kind);
        }
 
        [Fact]
        public void ErrorRecovery_NullableValueType_ImplicitConversion()
        {
            var source = """
                struct S
                {
                    void M()
                    {
                        S? s = default;
                        s ??= new ConvertibleToS(a);
                    }
                }
 
                class ConvertibleToS
                {
                    public static implicit operator S(ConvertibleToS c) => default;
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (6,19): error CS1729: 'ConvertibleToS' does not contain a constructor that takes 1 arguments
                //         s ??= new ConvertibleToS(a);
                Diagnostic(ErrorCode.ERR_BadCtorArgCount, "ConvertibleToS").WithArguments("ConvertibleToS", "1").WithLocation(6, 19),
                // (6,34): error CS0103: The name 'a' does not exist in the current context
                //         s ??= new ConvertibleToS(a);
                Diagnostic(ErrorCode.ERR_NameNotInContext, "a").WithArguments("a").WithLocation(6, 34));
 
            var tree = comp.SyntaxTrees.First();
            var model = comp.GetSemanticModel(tree);
 
            var objectCreationExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<ObjectCreationExpressionSyntax>().Single();
            var objectCreationExpressionTypeInfo = model.GetTypeInfo(objectCreationExpression);
            Assert.Equal("ConvertibleToS", objectCreationExpressionTypeInfo.Type.ToTestDisplayString());
            Assert.Equal("S", objectCreationExpressionTypeInfo.ConvertedType.ToTestDisplayString());
 
            var conversion = model.GetConversion(objectCreationExpression);
            Assert.Equal(ConversionKind.ImplicitUserDefined, conversion.Kind);
            Assert.Equal("S ConvertibleToS.op_Implicit(ConvertibleToS c)", conversion.MethodSymbol.ToTestDisplayString());
        }
    }
}