From ba7fe3580bc60e6b25729a07c7886b25bcd36c25 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Mon, 30 Sep 2024 13:33:24 -0700 Subject: [PATCH] Disallow declaration of indexers in absence of proper DefaultMemberAttribute. (#75099) Fixes #75032. --- .../Compiler Breaking Changes - DotNet 9.md | 12 +++ .../Symbols/Source/SourceNamedTypeSymbol.cs | 31 ++++---- .../CSharp/Test/Emit/CodeGen/IndexerTests.cs | 76 +++++++++++++++++++ .../Emit/Emit/InAttributeModifierTests.cs | 9 ++- .../IOperation/IOperationTests_IArgument.cs | 12 +++ .../Semantic/Semantics/InitOnlyMemberTests.cs | 11 ++- 6 files changed, 134 insertions(+), 17 deletions(-) diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md index 34688d39142e5..4653f94f2a873 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md @@ -93,3 +93,15 @@ class C } } ``` + + +## Declaration of indexers in absence of proper declaration of DefaultMemberAttribute is no longer allowed. + +***Introduced in Visual Studio 2022 version 17.13*** + +```cs +public interface I1 +{ + public I1 this[I1 args] { get; } // error CS0656: Missing compiler required member 'System.Reflection.DefaultMemberAttribute..ctor' +} +``` diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index bbdec6de9a01e..9e4cca64413c5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -1796,27 +1796,30 @@ protected override void AfterMembersCompletedChecks(BindingDiagnosticBag diagnos Debug.Assert(ObsoleteKind != ObsoleteAttributeKind.Uninitialized); Debug.Assert(GetMembers().All(m => m.ObsoleteKind != ObsoleteAttributeKind.Uninitialized)); - if (ObsoleteKind != ObsoleteAttributeKind.None - || GetMembers().All(m => m is not MethodSymbol { MethodKind: MethodKind.Constructor, ObsoleteKind: ObsoleteAttributeKind.None } method + if (ObsoleteKind == ObsoleteAttributeKind.None + && !GetMembers().All(m => m is not MethodSymbol { MethodKind: MethodKind.Constructor, ObsoleteKind: ObsoleteAttributeKind.None } method || !method.ShouldCheckRequiredMembers())) { - return; - } - - foreach (var member in GetMembers()) - { - if (!member.IsRequired()) + foreach (var member in GetMembers()) { - continue; - } + if (!member.IsRequired()) + { + continue; + } - if (member.ObsoleteKind != ObsoleteAttributeKind.None) - { - // Required member '{0}' should not be attributed with 'ObsoleteAttribute' unless the containing type is obsolete or all constructors are obsolete. - diagnostics.Add(ErrorCode.WRN_ObsoleteMembersShouldNotBeRequired, member.GetFirstLocation(), member); + if (member.ObsoleteKind != ObsoleteAttributeKind.None) + { + // Required member '{0}' should not be attributed with 'ObsoleteAttribute' unless the containing type is obsolete or all constructors are obsolete. + diagnostics.Add(ErrorCode.WRN_ObsoleteMembersShouldNotBeRequired, member.GetFirstLocation(), member); + } } } + if (Indexers.FirstOrDefault() is PropertySymbol indexerSymbol) + { + Binder.GetWellKnownTypeMember(DeclaringCompilation, WellKnownMember.System_Reflection_DefaultMemberAttribute__ctor, diagnostics, indexerSymbol.TryGetFirstLocation() ?? GetFirstLocation()); + } + if (TypeKind == TypeKind.Struct && !IsRecordStruct && HasInlineArrayAttribute(out _)) { if (Layout.Kind == LayoutKind.Explicit) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexerTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexerTests.cs index 962aad44400e0..6b01995d2246c 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexerTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexerTests.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.CSharp.UnitTests.Emit; +using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Xunit; @@ -1185,5 +1186,80 @@ static void Main() } #endregion Lowering + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/75032")] + public void MissingDefaultMemberAttribute_01() + { + var text1 = @" +public interface I1 +{ + public I1 this[I1 args] { get; } +} +"; + var comp1 = CreateCompilation(text1); + comp1.MakeMemberMissing(WellKnownMember.System_Reflection_DefaultMemberAttribute__ctor); + + comp1.VerifyDiagnostics( + // (4,15): error CS0656: Missing compiler required member 'System.Reflection.DefaultMemberAttribute..ctor' + // public I1 this[I1 args] { get; } + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "this").WithArguments("System.Reflection.DefaultMemberAttribute", ".ctor").WithLocation(4, 15) + ); + + comp1 = CreateCompilation(text1); + comp1.MakeTypeMissing(WellKnownType.System_Reflection_DefaultMemberAttribute); + + comp1.VerifyDiagnostics( + // (4,15): error CS0656: Missing compiler required member 'System.Reflection.DefaultMemberAttribute..ctor' + // public I1 this[I1 args] { get; } + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "this").WithArguments("System.Reflection.DefaultMemberAttribute", ".ctor").WithLocation(4, 15) + ); + + comp1 = CreateCompilation(text1); + comp1.VerifyEmitDiagnostics(); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/75032")] + public void MissingDefaultMemberAttribute_02() + { + var text1 = @" +public interface I1 +{ + public I1 this[I1 args] { get; } +} +"; + var comp1 = CreateCompilation(text1); + + var text2 = @" +class C : I1 +{ + I1 I1.this[I1 args] => throw null; +} +"; + var comp2 = CreateCompilation(text2, references: [comp1.ToMetadataReference()]); + comp2.MakeMemberMissing(WellKnownMember.System_Reflection_DefaultMemberAttribute__ctor); + comp2.VerifyEmitDiagnostics(); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/75032")] + public void MissingDefaultMemberAttribute_03() + { + var text1 = @" +using System.Collections.Generic; + +class Program +{ + static IEnumerable Test() + { + return [123]; + } +} +"; + var comp1 = CreateCompilation(text1); + comp1.MakeMemberMissing(WellKnownMember.System_Reflection_DefaultMemberAttribute__ctor); + CompileAndVerify(comp1).VerifyDiagnostics(); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/Emit/InAttributeModifierTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/InAttributeModifierTests.cs index 903de0150cf12..85dab8e75c6d1 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/InAttributeModifierTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/InAttributeModifierTests.cs @@ -1565,6 +1565,9 @@ class Test }"; CreateEmptyCompilation(code).VerifyDiagnostics( + // (9,27): error CS0656: Missing compiler required member 'System.Reflection.DefaultMemberAttribute..ctor' + // public virtual object this[in object p] => null; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "this").WithArguments("System.Reflection.DefaultMemberAttribute", ".ctor").WithLocation(9, 27), // (9,32): error CS0518: Predefined type 'System.Runtime.InteropServices.InAttribute' is not defined or imported // public virtual object this[in object p] => null; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "in object p").WithArguments("System.Runtime.InteropServices.InAttribute").WithLocation(9, 32)); @@ -1588,7 +1591,11 @@ class Test CreateEmptyCompilation(code).VerifyDiagnostics( // (10,20): error CS0518: Predefined type 'System.Runtime.InteropServices.InAttribute' is not defined or imported // public virtual ref readonly object this[object p] => ref value; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "ref readonly object").WithArguments("System.Runtime.InteropServices.InAttribute").WithLocation(10, 20)); + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "ref readonly object").WithArguments("System.Runtime.InteropServices.InAttribute").WithLocation(10, 20), + // (10,40): error CS0656: Missing compiler required member 'System.Reflection.DefaultMemberAttribute..ctor' + // public virtual ref readonly object this[object p] => ref value; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "this").WithArguments("System.Reflection.DefaultMemberAttribute", ".ctor").WithLocation(10, 40) + ); } [Fact] diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs index 94df0ba6c3672..e459d596ecee0 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs @@ -2588,6 +2588,9 @@ void M1() // (6,37): error CS0518: Predefined type 'System.Int32' is not defined or imported // public int this[int x, int? y = 5] Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "5").WithArguments("System.Int32").WithLocation(6, 37), + // (6,16): error CS0656: Missing compiler required member 'System.Reflection.DefaultMemberAttribute..ctor' + // public int this[int x, int? y = 5] + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "this").WithArguments("System.Reflection.DefaultMemberAttribute", ".ctor").WithLocation(6, 16), // (5,27): error CS0518: Predefined type 'System.Int32' is not defined or imported // private int _number = 0; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "0").WithArguments("System.Int32").WithLocation(5, 27), @@ -2766,6 +2769,9 @@ void M1() // (5,12): error CS0518: Predefined type 'System.Int32' is not defined or imported // public int this[int x, int? y = null] Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "int").WithArguments("System.Int32").WithLocation(5, 12), + // (5,16): error CS0656: Missing compiler required member 'System.Reflection.DefaultMemberAttribute..ctor' + // public int this[int x, int? y = null] + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "this").WithArguments("System.Reflection.DefaultMemberAttribute", ".ctor").WithLocation(5, 16), // (5,21): error CS0518: Predefined type 'System.Int32' is not defined or imported // public int this[int x, int? y = null] Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "int").WithArguments("System.Int32").WithLocation(5, 21), @@ -3829,6 +3835,9 @@ P M1() // (5,37): error CS0518: Predefined type 'System.Int32' is not defined or imported // public int this[int x, int? y = 0] Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "0").WithArguments("System.Int32").WithLocation(5, 37), + // (5,16): error CS0656: Missing compiler required member 'System.Reflection.DefaultMemberAttribute..ctor' + // public int this[int x, int? y = 0] + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "this").WithArguments("System.Reflection.DefaultMemberAttribute", ".ctor").WithLocation(5, 16), // (4,27): error CS0518: Predefined type 'System.Int32' is not defined or imported // private int _number = 0; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "0").WithArguments("System.Int32").WithLocation(4, 27), @@ -3903,6 +3912,9 @@ P M1() // (5,12): error CS0518: Predefined type 'System.Int32' is not defined or imported // public int this[int x, int? y = null] Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "int").WithArguments("System.Int32").WithLocation(5, 12), + // (5,16): error CS0656: Missing compiler required member 'System.Reflection.DefaultMemberAttribute..ctor' + // public int this[int x, int? y = null] + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "this").WithArguments("System.Reflection.DefaultMemberAttribute", ".ctor").WithLocation(5, 16), // (5,21): error CS0518: Predefined type 'System.Int32' is not defined or imported // public int this[int x, int? y = null] Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "int").WithArguments("System.Int32").WithLocation(5, 21), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs index 37542bede467a..042a87f2469b6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InitOnlyMemberTests.cs @@ -4,6 +4,7 @@ #nullable disable +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; @@ -3462,7 +3463,10 @@ void M(C c) "; var reference = CreateMetadataReferenceFromIlSource(il); - var comp = CreateCompilation(source, references: new[] { reference }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, references: new[] { reference }, parseOptions: TestOptions.Regular9, + options: TestOptions.ReleaseDll.WithSpecificDiagnosticOptions( + // warning CS1685: The predefined type 'DefaultMemberAttribute' is defined in multiple assemblies + ImmutableDictionary.Empty.Add("CS1685", ReportDiagnostic.Suppress))); comp.VerifyEmitDiagnostics( // (4,25): error CS0569: 'Derived.this[int]': cannot override 'C.this[int]' because it is not supported by the language // public override int this[int i] { set { throw null; } } @@ -3558,7 +3562,10 @@ public class Derived2 : C "; var reference = CreateMetadataReferenceFromIlSource(il); - var comp = CreateCompilation(source, references: new[] { reference }, parseOptions: TestOptions.Regular9); + var comp = CreateCompilation(source, references: new[] { reference }, parseOptions: TestOptions.Regular9, + options: TestOptions.ReleaseDll.WithSpecificDiagnosticOptions( + // warning CS1685: The predefined type 'DefaultMemberAttribute' is defined in multiple assemblies + ImmutableDictionary.Empty.Add("CS1685", ReportDiagnostic.Suppress))); comp.VerifyEmitDiagnostics( // (4,39): error CS0570: 'C.this[int].set' is not supported by the language // public override int this[int i] { set { throw null; } }