From 144447da16571602bacbb611776477184d1ad52d Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 19 Feb 2025 15:25:35 +0100 Subject: [PATCH] Disallow initializers on partial constructor definitions --- .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../CSharp/Portable/Errors/ErrorFacts.cs | 1 + .../Symbols/Source/SourceConstructorSymbol.cs | 5 + .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + .../PartialEventsAndConstructorsTests.cs | 157 ++++++++++++++++++ 18 files changed, 232 insertions(+) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 2e7036102b359..5781b2099f02e 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -8065,4 +8065,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ '{0}': partial event cannot have initializer + + '{0}': only the implementing declaration of a partial constructor can have an initializer + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index ba06ddf57d8ff..0c08fe512382f 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2366,6 +2366,7 @@ internal enum ErrorCode ERR_PartialMemberDuplicateDefinition = 9402, ERR_PartialMemberDuplicateImplementation = 9403, ERR_PartialEventInitializer = 9404, + ERR_PartialConstructorInitializer = 9405, // Note: you will need to do the following after adding errors: // 1) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 8de9a5f4dd635..03c740c4cd71b 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2484,6 +2484,7 @@ or ErrorCode.ERR_PartialMemberMissingDefinition or ErrorCode.ERR_PartialMemberDuplicateDefinition or ErrorCode.ERR_PartialMemberDuplicateImplementation or ErrorCode.ERR_PartialEventInitializer + or ErrorCode.ERR_PartialConstructorInitializer => false, }; #pragma warning restore CS8524 // The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs index 8f121d832cf81..3b2a41162609b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs @@ -65,6 +65,11 @@ private SourceConstructorSymbol( } } + if (IsPartialDefinition && syntax.Initializer is { } initializer) + { + diagnostics.Add(ErrorCode.ERR_PartialConstructorInitializer, initializer, this); + } + if (methodKind == MethodKind.StaticConstructor) { CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasAnyBody, diagnostics); diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 46c6b9e43bed6..aa9311aceb71d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1667,6 +1667,11 @@ Parametr params musí mít platný typ kolekce. + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + '{0}': partial event cannot have initializer '{0}': partial event cannot have initializer diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 03d28a7504c3a..1715273202398 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1667,6 +1667,11 @@ Der Params-Parameter muss einen gültigen Sammlungstyp aufweisen + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + '{0}': partial event cannot have initializer '{0}': partial event cannot have initializer diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 7e3618608196f..12890aedda082 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1667,6 +1667,11 @@ El parámetro params debe tener un tipo de colección válido + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + '{0}': partial event cannot have initializer '{0}': partial event cannot have initializer diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 2c352717c76ab..ab1347d8671da 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1667,6 +1667,11 @@ Le paramètre params doit avoir un type de collection valide + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + '{0}': partial event cannot have initializer '{0}': partial event cannot have initializer diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 98d30e89cb0fd..6415201f45f27 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1667,6 +1667,11 @@ Il parametro params deve avere un tipo di raccolta valido + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + '{0}': partial event cannot have initializer '{0}': partial event cannot have initializer diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 278bd44719e64..0589c9f01901d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1667,6 +1667,11 @@ params パラメーターには有効なコレクションの種類が必要です + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + '{0}': partial event cannot have initializer '{0}': partial event cannot have initializer diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 85d70126b9884..552c83f178c58 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1667,6 +1667,11 @@ params 매개 변수는 유효한 컬렉션 형식이어야 합니다. + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + '{0}': partial event cannot have initializer '{0}': partial event cannot have initializer diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 79b1804c5f679..8a633d6e96504 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1667,6 +1667,11 @@ Parametr params musi mieć prawidłowy typ kolekcji + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + '{0}': partial event cannot have initializer '{0}': partial event cannot have initializer diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 105523cf56749..62baee0d37087 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1667,6 +1667,11 @@ O parâmetro params deve ter um tipo de coleção válido + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + '{0}': partial event cannot have initializer '{0}': partial event cannot have initializer diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 776d949ecdd2f..024d32d0bbe9b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1667,6 +1667,11 @@ Параметр params должен иметь допустимый тип коллекции. + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + '{0}': partial event cannot have initializer '{0}': partial event cannot have initializer diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index b41a4fe045e72..f079fb71afe31 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1667,6 +1667,11 @@ Params parametresi geçerli bir koleksiyon türüne sahip olmalıdır + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + '{0}': partial event cannot have initializer '{0}': partial event cannot have initializer diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index f2a8b9c375334..7df046d4dbdf3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1667,6 +1667,11 @@ Params 参数必须具有有效的集合类型 + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + '{0}': partial event cannot have initializer '{0}': partial event cannot have initializer diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index c70935151f534..42426b5b719ab 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1667,6 +1667,11 @@ params 參數必須具有有效的集合型別 + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + '{0}': partial event cannot have initializer '{0}': partial event cannot have initializer diff --git a/src/Compilers/CSharp/Test/Emit3/PartialEventsAndConstructorsTests.cs b/src/Compilers/CSharp/Test/Emit3/PartialEventsAndConstructorsTests.cs index f021f65d38025..460ecafe99a9f 100644 --- a/src/Compilers/CSharp/Test/Emit3/PartialEventsAndConstructorsTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/PartialEventsAndConstructorsTests.cs @@ -778,6 +778,163 @@ partial event System.Action I.E { add { } remove { } } Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "E").WithLocation(8, 35)); } + [Fact] + public void ConstructorInitializers_This_Duplicate() + { + var source = """ + partial class C + { + partial C() : this(1) { } + partial C() : this(2); + + C(int x) { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,17): error CS9405: 'C.C()': only the implementing declaration of a partial constructor can have an initializer + // partial C() : this(2); + Diagnostic(ErrorCode.ERR_PartialConstructorInitializer, ": this(2)").WithArguments("C.C()").WithLocation(4, 17)); + } + + [Fact] + public void ConstructorInitializers_This_OnDefinition() + { + var source = """ + partial class C + { + partial C() { } + partial C() : this(1); + + C(int x) { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,17): error CS9405: 'C.C()': only the implementing declaration of a partial constructor can have an initializer + // partial C() : this(1); + Diagnostic(ErrorCode.ERR_PartialConstructorInitializer, ": this(1)").WithArguments("C.C()").WithLocation(4, 17)); + } + + [Fact] + public void ConstructorInitializers_This_OnImplementation() + { + var source = """ + var c = new C(); + + partial class C + { + public partial C() : this(1) { } + public partial C(); + + C(int x) { System.Console.Write(x); } + } + """; + CompileAndVerify(source, expectedOutput: "1").VerifyDiagnostics(); + } + + [Fact] + public void ConstructorInitializers_Base_Duplicate() + { + var source = """ + abstract class B + { + protected B(int x) { } + } + + partial class C : B + { + partial C() : base(1) { } + partial C() : base(2); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (9,17): error CS9405: 'C.C()': only the implementing declaration of a partial constructor can have an initializer + // partial C() : base(2); + Diagnostic(ErrorCode.ERR_PartialConstructorInitializer, ": base(2)").WithArguments("C.C()").WithLocation(9, 17)); + } + + [Fact] + public void ConstructorInitializers_Base_OnDefinition_01() + { + var source = """ + abstract class B + { + protected B(int x) { } + } + + partial class C : B + { + partial C() { } + partial C() : base(1); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (8,13): error CS7036: There is no argument given that corresponds to the required parameter 'x' of 'B.B(int)' + // partial C() { } + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "C").WithArguments("x", "B.B(int)").WithLocation(8, 13), + // (9,17): error CS9405: 'C.C()': only the implementing declaration of a partial constructor can have an initializer + // partial C() : base(1); + Diagnostic(ErrorCode.ERR_PartialConstructorInitializer, ": base(1)").WithArguments("C.C()").WithLocation(9, 17)); + } + + [Fact] + public void ConstructorInitializers_Base_OnDefinition_02() + { + var source = """ + abstract class B + { + protected B(int x) { } + protected B() { } + } + + partial class C : B + { + partial C() { } + partial C() : base(1); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (10,17): error CS9405: 'C.C()': only the implementing declaration of a partial constructor can have an initializer + // partial C() : base(1); + Diagnostic(ErrorCode.ERR_PartialConstructorInitializer, ": base(1)").WithArguments("C.C()").WithLocation(10, 17)); + } + + [Fact] + public void ConstructorInitializers_Base_OnImplementation() + { + var source = """ + var c = new C(); + + abstract class B + { + protected B(int x) { System.Console.Write(x); } + } + + partial class C : B + { + public partial C() : base(1) { } + public partial C(); + } + """; + CompileAndVerify(source, expectedOutput: "1").VerifyDiagnostics(); + } + + [Fact] + public void VariableInitializer() + { + var source = """ + var c = new C(); + + partial class C + { + int x = 5; + + public partial C() { System.Console.Write(x); } + public partial C(); + } + """; + CompileAndVerify(source, expectedOutput: "5").VerifyDiagnostics(); + } + [Fact] public void Extern_01() {