diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 09141e3ff0c49..0d3cb75c452ac 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -4986,13 +4986,15 @@ private static void CheckRequiredMembersInObjectInitializer( return; } - var requiredMembers = constructor.ContainingType.AllRequiredMembers.ToBuilder(); + var requiredMembers = constructor.ContainingType.AllRequiredMembers; if (requiredMembers.Count == 0) { return; } + var requiredMembersBuilder = requiredMembers.ToBuilder(); + if (initializers.IsDefaultOrEmpty) { reportMembers(); @@ -5001,24 +5003,28 @@ private static void CheckRequiredMembersInObjectInitializer( foreach (var initializer in initializers) { - var (memberSymbol, initializerExpression) = initializer is BoundAssignmentOperator assignmentOperator - ? (assignmentOperator.Left switch - { - // Regular initializers - BoundObjectInitializerMember member => member.MemberSymbol, - // Attribute initializers - BoundPropertyAccess propertyAccess => propertyAccess.PropertySymbol, - BoundFieldAccess fieldAccess => fieldAccess.FieldSymbol, - _ => null - }, assignmentOperator.Right) - : default; + if (initializer is not BoundAssignmentOperator assignmentOperator) + { + continue; + } + + var memberSymbol = assignmentOperator.Left switch + { + // Regular initializers + BoundObjectInitializerMember member => member.MemberSymbol, + // Attribute initializers + BoundPropertyAccess propertyAccess => propertyAccess.PropertySymbol, + BoundFieldAccess fieldAccess => fieldAccess.FieldSymbol, + // Error cases + _ => null + }; if (memberSymbol is null) { continue; } - if (!requiredMembers.TryGetValue(memberSymbol.Name, out var requiredMember)) + if (!requiredMembersBuilder.TryGetValue(memberSymbol.Name, out var requiredMember)) { continue; } @@ -5028,12 +5034,12 @@ private static void CheckRequiredMembersInObjectInitializer( continue; } - requiredMembers.Remove(memberSymbol.Name); + requiredMembersBuilder.Remove(memberSymbol.Name); - if (initializerExpression is BoundObjectInitializerExpressionBase) + if (assignmentOperator.Right is BoundObjectInitializerExpressionBase initializerExpression) { // Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. - diagnostics.Add(ErrorCode.ERR_RequiredMembersMustBeAssignment, initializerExpression.Syntax.Location, requiredMember); + diagnostics.Add(ErrorCode.ERR_RequiredMembersMustBeAssignedValue, initializerExpression.Syntax.Location, requiredMember); } } @@ -5049,7 +5055,7 @@ void reportMembers() _ => creationSyntax.Location }; - foreach (var (_, member) in requiredMembers) + foreach (var (_, member) in requiredMembersBuilder) { // Required member '{0}' must be set in the object initializer or attribute constructor. diagnostics.Add(ErrorCode.ERR_RequiredMemberMustBeSet, location, member); @@ -5847,6 +5853,8 @@ internal bool TryPerformConstructorOverloadResolution( diagnostics.AddDependencies(useSiteInfo); foreach (var diagnostic in useSiteInfo.Diagnostics) { + // We don't want to report this error here because we'll report ERR_RequiredMembersBaseTypeInvalid. That error is suppressable by the + // user using the `SetsRequiredMembers` attribute on the constructor, so reporting this error would prevent that from working. if ((ErrorCode)diagnostic.Code == ErrorCode.ERR_RequiredMembersInvalid) { continue; diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 37e085f41196f..4d375d06462c3 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7010,7 +7010,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Required member '{0}' must be set in the object initializer or attribute constructor. - + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 475e6d1e0eed2..50af936c9ead5 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2059,7 +2059,7 @@ internal enum ErrorCode ERR_ExplicitRequiredMember = 9504, ERR_RequiredMemberMustBeSettable = 9505, ERR_RequiredMemberMustBeSet = 9506, - ERR_RequiredMembersMustBeAssignment = 9507, + ERR_RequiredMembersMustBeAssignedValue = 9507, ERR_RequiredMembersInvalid = 9508, ERR_RequiredMembersBaseTypeInvalid = 9509, } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index aa0068593c057..bf9303a671c24 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1182,7 +1182,7 @@ The required members list for '{0}' is malformed and cannot be interpreted. - + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. @@ -11534,4 +11534,4 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index ec14f18174b29..93b09d671f453 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1182,7 +1182,7 @@ The required members list for '{0}' is malformed and cannot be interpreted. - + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. @@ -11534,4 +11534,4 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 8c45c9dc73c0d..3bf5f53e1bb40 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1182,7 +1182,7 @@ The required members list for '{0}' is malformed and cannot be interpreted. - + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. @@ -11534,4 +11534,4 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 1a610c39f7d04..d4049f28726de 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1182,7 +1182,7 @@ The required members list for '{0}' is malformed and cannot be interpreted. - + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. @@ -11534,4 +11534,4 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 54d0207742b82..92e3a9d267a5e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1182,7 +1182,7 @@ The required members list for '{0}' is malformed and cannot be interpreted. - + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. @@ -11534,4 +11534,4 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 7c0848e4fcec9..ed8316d331273 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1182,7 +1182,7 @@ The required members list for '{0}' is malformed and cannot be interpreted. - + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. @@ -11534,4 +11534,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index cda2f01c75728..05bc6643835bb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1182,7 +1182,7 @@ The required members list for '{0}' is malformed and cannot be interpreted. - + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. @@ -11533,4 +11533,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index e3b4e9b813fe7..515161c208c83 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1182,7 +1182,7 @@ The required members list for '{0}' is malformed and cannot be interpreted. - + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. @@ -11534,4 +11534,4 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 1331b89b14e07..bb82df68d50f4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1182,7 +1182,7 @@ The required members list for '{0}' is malformed and cannot be interpreted. - + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. @@ -11534,4 +11534,4 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index e949f3e3b67e8..c2e372a2ddd51 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1182,7 +1182,7 @@ The required members list for '{0}' is malformed and cannot be interpreted. - + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. @@ -11534,4 +11534,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 643f8416444d2..c742616406eb1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1182,7 +1182,7 @@ The required members list for '{0}' is malformed and cannot be interpreted. - + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. @@ -11534,4 +11534,4 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index c6383d341d063..edd66da4415c7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1182,7 +1182,7 @@ The required members list for '{0}' is malformed and cannot be interpreted. - + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. @@ -11539,4 +11539,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 922c6b22e4800..d1a306577c56b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1182,7 +1182,7 @@ The required members list for '{0}' is malformed and cannot be interpreted. - + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. @@ -11534,4 +11534,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - + \ No newline at end of file diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs index aa9a583b62e61..9a9780068ef6d 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs @@ -1388,13 +1388,33 @@ public class D comp.VerifyDiagnostics( // (2,24): error CS9507: Required member 'C.D1' must be assigned a value, it cannot use a nested member or collection initializer. // var c = new C() { D1 = { NestedProp = 1 }, D2 = { NestedProp = 2 } }; - Diagnostic(ErrorCode.ERR_RequiredMembersMustBeAssignment, "{ NestedProp = 1 }").WithArguments("C.D1").WithLocation(2, 24), + Diagnostic(ErrorCode.ERR_RequiredMembersMustBeAssignedValue, "{ NestedProp = 1 }").WithArguments("C.D1").WithLocation(2, 24), // (2,49): error CS9507: Required member 'C.D2' must be assigned a value, it cannot use a nested member or collection initializer. // var c = new C() { D1 = { NestedProp = 1 }, D2 = { NestedProp = 2 } }; - Diagnostic(ErrorCode.ERR_RequiredMembersMustBeAssignment, "{ NestedProp = 2 }").WithArguments("C.D2").WithLocation(2, 49) + Diagnostic(ErrorCode.ERR_RequiredMembersMustBeAssignedValue, "{ NestedProp = 2 }").WithArguments("C.D2").WithLocation(2, 49) ); } + [Fact] + public void EnforcedRequiredMembers_NoInheritance_NestedObjectCreationAllowed() + { + var c = @" +var c = new C() { D1 = new() { NestedProp = 1 }, D2 = new() { NestedProp = 2 } }; + +public class C +{ + public required D D1 { get; set; } + public required D D2; +} +public class D +{ + public int NestedProp { get; set; } +} +"; + var comp = CreateCompilationWithRequiredMembers(c); + CompileAndVerify(comp).VerifyDiagnostics(); + } + [Fact] public void EnforcedRequiredMembers_NoInheritance_DisallowedNestedCollectionInitializer() { @@ -1412,13 +1432,30 @@ public class C comp.VerifyDiagnostics( // (3,24): error CS9507: Required member 'C.L1' must be assigned a value, it cannot use a nested member or collection initializer. // var c = new C() { L1 = { 1, 2, 3 }, L2 = { 4, 5, 6 } }; - Diagnostic(ErrorCode.ERR_RequiredMembersMustBeAssignment, "{ 1, 2, 3 }").WithArguments("C.L1").WithLocation(3, 24), + Diagnostic(ErrorCode.ERR_RequiredMembersMustBeAssignedValue, "{ 1, 2, 3 }").WithArguments("C.L1").WithLocation(3, 24), // (3,42): error CS9507: Required member 'C.L2' must be assigned a value, it cannot use a nested member or collection initializer. // var c = new C() { L1 = { 1, 2, 3 }, L2 = { 4, 5, 6 } }; - Diagnostic(ErrorCode.ERR_RequiredMembersMustBeAssignment, "{ 4, 5, 6 }").WithArguments("C.L2").WithLocation(3, 42) + Diagnostic(ErrorCode.ERR_RequiredMembersMustBeAssignedValue, "{ 4, 5, 6 }").WithArguments("C.L2").WithLocation(3, 42) ); } + [Fact] + public void EnforcedRequiredMembers_NoInheritance_NestedObjectCreationWithCollectionInitializerAllowed() + { + var c = @" +using System.Collections.Generic; +var c = new C() { L1 = new() { 1, 2, 3 }, L2 = new() { 4, 5, 6 } }; + +public class C +{ + public required List L1 { get; set; } + public required List L2; +} +"; + var comp = CreateCompilationWithRequiredMembers(c); + CompileAndVerify(comp).VerifyDiagnostics(); + } + [Theory] [CombinatorialData] public void EnforcedRequiredMembers_Inheritance_NoneSet(bool useMetadataReference)