/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Semantic/Semantics/NullCoalesceAssignmentTests.cs
1 005 строк
48 KB
DoctorKrolic
Improve compiler error recovery for null coalesce assignment operator (#80907)
25 ноя 2025, 22:21
Не верифицирован
25 ноя 2025, 22:21
fc8030f
Код
Авторство
О чём код?
// 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()); } } }