From cf886937c2ac2a291fc38e5fba91e35f457cf17f Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 10 Mar 2022 18:16:19 -0800 Subject: [PATCH 1/7] Error when required members are not set Implements reading the required members list of a type, and enforces that required members are all set by an initializer on the constructor. Required members must be initialized with values, not with nested object initializers. Test plan https://github.com/dotnet/roslyn/issues/57046. Specification https://github.com/dotnet/csharplang/blob/main/proposals/required-members.md --- .../Portable/Binder/Binder_Expressions.cs | 78 +- .../CSharp/Portable/CSharpResources.resx | 12 + .../CSharp/Portable/Errors/ErrorCode.cs | 4 + .../Symbols/Metadata/PE/PEMethodSymbol.cs | 5 + .../Portable/Symbols/NamedTypeSymbol.cs | 122 ++ .../Source/SourceMemberContainerSymbol.cs | 14 + .../Portable/Symbols/SymbolExtensions.cs | 4 + .../Portable/xlf/CSharpResources.cs.xlf | 22 +- .../Portable/xlf/CSharpResources.de.xlf | 22 +- .../Portable/xlf/CSharpResources.es.xlf | 22 +- .../Portable/xlf/CSharpResources.fr.xlf | 22 +- .../Portable/xlf/CSharpResources.it.xlf | 22 +- .../Portable/xlf/CSharpResources.ja.xlf | 22 +- .../Portable/xlf/CSharpResources.ko.xlf | 22 +- .../Portable/xlf/CSharpResources.pl.xlf | 22 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 22 +- .../Portable/xlf/CSharpResources.ru.xlf | 22 +- .../Portable/xlf/CSharpResources.tr.xlf | 22 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 22 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 22 +- .../Symbol/Symbols/RequiredMembersTests.cs | 1412 ++++++++++++++++- .../Test/Utilities/CSharp/CSharpTestBase.cs | 19 +- 22 files changed, 1924 insertions(+), 32 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 81feaabf66212..bc0191b8dd97e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -4967,6 +4967,78 @@ private static void ReportDuplicateObjectMemberInitializers(BoundExpression boun } } + private static void CheckRequiredMembersInObjectInitializer( + BoundObjectCreationExpression creation, + BindingDiagnosticBag diagnostics) + { + if (!creation.Constructor.ShouldCheckRequiredMembers()) + { + return; + } + + if (creation.Constructor.ContainingType.HasRequiredMembersError) + { + // An error will be reported on the constructor if from source, or a use-site diagnostic will be reported on the use if from metadata. + return; + } + + var requiredMembers = creation.Constructor.ContainingType.AllRequiredMembers.ToBuilder(); + + if (requiredMembers.Count == 0) + { + return; + } + + if (creation.InitializerExpressionOpt == null) + { + reportMembers(); + return; + } + + foreach (var initializer in creation.InitializerExpressionOpt.Initializers) + { + if (initializer is not BoundAssignmentOperator { Left: BoundObjectInitializerMember member, Right: { } initializerExpression }) + { + continue; + } + + if (!requiredMembers.TryGetValue(member.MemberSymbol.Name, out var requiredMember)) + { + continue; + } + + if (!member.MemberSymbol.Equals(requiredMember, TypeCompareKind.ConsiderEverything)) + { + continue; + } + + requiredMembers.Remove(member.MemberSymbol.Name); + + if (initializerExpression is BoundObjectInitializerExpressionBase) + { + diagnostics.Add(ErrorCode.ERR_RequiredMembersMustBeAssignment, initializerExpression.Syntax.Location, requiredMember); + } + } + + reportMembers(); + + void reportMembers() + { + Location location = creation.Syntax switch + { + ObjectCreationExpressionSyntax { Type: { } type } => type.Location, + BaseObjectCreationExpressionSyntax { NewKeyword: { } newKeyword } => newKeyword.GetLocation(), + _ => creation.Syntax.Location + }; + + foreach (var (_, member) in requiredMembers) + { + // Required member '{0}' must be set in the object initializer. + diagnostics.Add(ErrorCode.ERR_RequiredMemberMustBeSet, location, member); + } + } + } + private BoundCollectionInitializerExpression BindCollectionInitializerExpression( InitializerExpressionSyntax initializerSyntax, TypeSymbol initializerType, @@ -5411,7 +5483,7 @@ protected BoundExpression BindClassCreationExpression( } boundInitializerOpt = makeBoundInitializerOpt(); - result = new BoundObjectCreationExpression( + var creation = new BoundObjectCreationExpression( node, method, candidateConstructors, @@ -5427,7 +5499,9 @@ protected BoundExpression BindClassCreationExpression( type, hasError); - return result; + CheckRequiredMembersInObjectInitializer(creation, diagnostics); + + return creation; } LookupResultKind resultKind; diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 338d3d7425350..5225f0c81ba09 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7007,6 +7007,18 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Required member '{0}' must be settable. + + Required member '{0}' must be set in the object initializer. + + + Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer. + + + The required members list for '{0}' is malformed and cannot be interpreted. + + + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + Line contains different whitespace than the closing line of the raw string literal: '{0}' versus '{1}' diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 947a0a7162abc..475e6d1e0eed2 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2058,5 +2058,9 @@ internal enum ErrorCode ERR_RequiredMemberCannotBeLessVisibleThanContainingType = 9503, ERR_ExplicitRequiredMember = 9504, ERR_RequiredMemberMustBeSettable = 9505, + ERR_RequiredMemberMustBeSet = 9506, + ERR_RequiredMembersMustBeAssignment = 9507, + ERR_RequiredMembersInvalid = 9508, + ERR_RequiredMembersBaseTypeInvalid = 9509, } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs index c6d6e5ebc134f..13dffcbcdea8c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs @@ -1346,6 +1346,11 @@ internal override UseSiteInfo GetUseSiteInfo() } } + if (diagnosticInfo == null && this.ShouldCheckRequiredMembers() && ContainingType.HasRequiredMembersError) + { + diagnosticInfo = new CSDiagnosticInfo(ErrorCode.ERR_RequiredMembersInvalid, ContainingType); + } + return InitializeUseSiteDiagnostic(result.AdjustDiagnosticInfo(diagnosticInfo)); } diff --git a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs index 7cb9d7c0f7086..5c525758f8be4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs @@ -11,6 +11,8 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Symbols; using Roslyn.Utilities; @@ -24,6 +26,14 @@ internal abstract partial class NamedTypeSymbol : TypeSymbol, INamedTypeSymbolIn { private bool _hasNoBaseCycles; + /// + /// This field cannot be used to determine whether has been initialized. + /// Ensure that is checked for defaultness _before_ reading this field, which potentially + /// means using an `Interlocked.MemoryBarrier()` to ensure that reading this location is not reordered before the read to . + /// + private bool _lazyHasRequiredMembersErrorDoNotAccessDirectly = false; + private ImmutableSegmentedDictionary _lazyRequiredMembers; + // Only the compiler can create NamedTypeSymbols. internal NamedTypeSymbol(TupleExtraData tupleData = null) { @@ -499,6 +509,118 @@ internal abstract bool MangleName /// internal abstract bool HasDeclaredRequiredMembers { get; } +#nullable enable + /// + /// Whether the type encountered an error while trying to build its complete list of required members. + /// + internal bool HasRequiredMembersError + { + get + { + CalculateRequiredMembersIfRequired(); + Debug.Assert(!_lazyRequiredMembers.IsDefault); + return _lazyHasRequiredMembersErrorDoNotAccessDirectly; + } + } + + /// + /// The full list of all required members for this type, including from base classes. If is true, + /// this returns empty. + /// + /// + /// Do not call this API if all you need are the required members declared on this type. Use instead, filtering for + /// required members, instead of calling this API. + /// + internal ImmutableSegmentedDictionary AllRequiredMembers + { + get + { + CalculateRequiredMembersIfRequired(); + Debug.Assert(!_lazyRequiredMembers.IsDefault); + return _lazyRequiredMembers; + } + } + + private void CalculateRequiredMembersIfRequired() + { + if (_lazyRequiredMembers.IsDefault) + { + ImmutableSegmentedDictionary.Builder? builder = null; + bool success = TryCalculateRequiredMembers(ref builder); + var requiredMembers = success + ? builder?.ToImmutable() ?? ImmutableSegmentedDictionary.Empty + : ImmutableSegmentedDictionary.Empty; + + _lazyHasRequiredMembersErrorDoNotAccessDirectly = !success; + // InterlockedInitialize uses `Interlocked.CompareExchange` under the hood, which will introduce a full + // memory barrier, ensuring that the _lazyHasRequiredMembersErrorDoNotAccessDirectly write is not reordered + // after the initialization of _lazyRequiredMembers. + RoslynImmutableInterlocked.InterlockedInitialize(ref _lazyRequiredMembers, requiredMembers); + } + else + { + // Ensure that, after this method, _lazyRequiredMembersErrorDoNotAccessDirectly is able to be accessed safely. + Interlocked.MemoryBarrier(); + } + } + + /// + /// Attempts to calculate the required members for this type. Returns false if there were errors. + /// + private bool TryCalculateRequiredMembers(ref ImmutableSegmentedDictionary.Builder? requiredMembersBuilder) + { + var lazyRequiredMembers = _lazyRequiredMembers; + if (!lazyRequiredMembers.IsDefault) + { + requiredMembersBuilder = lazyRequiredMembers.ToBuilder(); + // Ensure that this read is not reordered before the read to `IsDefault`. + Interlocked.MemoryBarrier(); + return !_lazyHasRequiredMembersErrorDoNotAccessDirectly; + } + + if (BaseTypeNoUseSiteDiagnostics?.TryCalculateRequiredMembers(ref requiredMembersBuilder) == false) + { + return false; + } + + // We need to make sure that members from a base type weren't hidden by members from the current type. + if (!HasDeclaredRequiredMembers && requiredMembersBuilder == null) + { + return true; + } + + return addCurrentTypeMembers(ref requiredMembersBuilder); + + bool addCurrentTypeMembers(ref ImmutableSegmentedDictionary.Builder? requiredMembersBuilder) + { + requiredMembersBuilder ??= ImmutableSegmentedDictionary.CreateBuilder(); + + foreach (var member in GetMembersUnordered()) + { + if (requiredMembersBuilder.ContainsKey(member.Name)) + { + // This is only permitted if the member is an override of a required member from a base type, and is required itself. + if (!member.IsRequired() + || member.GetOverriddenMember() is not { } overriddenMember + || !overriddenMember.Equals(requiredMembersBuilder[member.Name], TypeCompareKind.ConsiderEverything)) + { + return false; + } + } + + if (!member.IsRequired()) + { + continue; + } + + requiredMembersBuilder[member.Name] = member; + } + + return true; + } + } +#nullable disable + /// /// Get all the members of this symbol. /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 94a6a797825de..c3c81cdbb0de2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -2400,6 +2400,20 @@ private void CheckForRequiredMemberAttribute(BindingDiagnosticBag diagnostics) // Ensure that an error is reported if the required constructor isn't present. _ = Binder.GetWellKnownTypeMember(DeclaringCompilation, WellKnownMember.System_Runtime_CompilerServices_RequiredMemberAttribute__ctor, diagnostics, Locations[0]); } + + if (BaseTypeNoUseSiteDiagnostics is (not SourceMemberContainerTypeSymbol) and { HasRequiredMembersError: true }) + { + foreach (var member in GetMembersUnordered()) + { + if (member is not MethodSymbol method || !method.ShouldCheckRequiredMembers()) + { + continue; + } + + // The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + diagnostics.Add(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, method.Locations[0], BaseTypeNoUseSiteDiagnostics); + } + } } private bool TypeOverridesObjectMethod(string name) diff --git a/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs index 2fa9cce9e30d4..b7a2bc2e045da 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs @@ -841,5 +841,9 @@ internal static bool HasAsyncMethodBuilderAttribute(this Symbol symbol, [NotNull } internal static bool IsRequired(this Symbol symbol) => symbol is FieldSymbol { IsRequired: true } or PropertySymbol { IsRequired: true }; + + internal static bool ShouldCheckRequiredMembers(this MethodSymbol constructor) + // PROTOTYPE(req): Check for the SetsRequiredMembersAttribute and return false for that case + => constructor.MethodKind == MethodKind.Constructor; } } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index eebde2822ddca..f876bc3fd56d3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1162,11 +1162,31 @@ Required member '{0}' cannot be less visible or have a setter less visible than the containing type '{1}'. + + Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer. + + Required member '{0}' must be settable. Required member '{0}' must be settable. + + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + + + + The required members list for '{0}' is malformed and cannot be interpreted. + 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. + + Types and aliases cannot be named 'required'. Types and aliases cannot be named 'required'. @@ -11514,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 527da043682de..d4c1af70c7b78 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1162,11 +1162,31 @@ Required member '{0}' cannot be less visible or have a setter less visible than the containing type '{1}'. + + Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer. + + Required member '{0}' must be settable. Required member '{0}' must be settable. + + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + + + + The required members list for '{0}' is malformed and cannot be interpreted. + 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. + + Types and aliases cannot be named 'required'. Types and aliases cannot be named 'required'. @@ -11514,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 2bbbc667d76e0..d079780e28d7a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1162,11 +1162,31 @@ Required member '{0}' cannot be less visible or have a setter less visible than the containing type '{1}'. + + Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer. + + Required member '{0}' must be settable. Required member '{0}' must be settable. + + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + + + + The required members list for '{0}' is malformed and cannot be interpreted. + 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. + + Types and aliases cannot be named 'required'. Types and aliases cannot be named 'required'. @@ -11514,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 54fbfe8e29f33..43aae3d20e616 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1162,11 +1162,31 @@ Required member '{0}' cannot be less visible or have a setter less visible than the containing type '{1}'. + + Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer. + + Required member '{0}' must be settable. Required member '{0}' must be settable. + + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + + + + The required members list for '{0}' is malformed and cannot be interpreted. + 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. + + Types and aliases cannot be named 'required'. Types and aliases cannot be named 'required'. @@ -11514,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 ee5d501d19ed5..fcd06aadb6eed 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1162,11 +1162,31 @@ Required member '{0}' cannot be less visible or have a setter less visible than the containing type '{1}'. + + Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer. + + Required member '{0}' must be settable. Required member '{0}' must be settable. + + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + + + + The required members list for '{0}' is malformed and cannot be interpreted. + 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. + + Types and aliases cannot be named 'required'. Types and aliases cannot be named 'required'. @@ -11514,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 3814916427d0a..cd5ae6a1428fd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1162,11 +1162,31 @@ Required member '{0}' cannot be less visible or have a setter less visible than the containing type '{1}'. + + Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer. + + Required member '{0}' must be settable. Required member '{0}' must be settable. + + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + + + + The required members list for '{0}' is malformed and cannot be interpreted. + 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. + + Types and aliases cannot be named 'required'. Types and aliases cannot be named 'required'. @@ -11514,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 c1ba1a9120394..2806f16450a1e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1162,11 +1162,31 @@ Required member '{0}' cannot be less visible or have a setter less visible than the containing type '{1}'. + + Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer. + + Required member '{0}' must be settable. Required member '{0}' must be settable. + + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + + + + The required members list for '{0}' is malformed and cannot be interpreted. + 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. + + Types and aliases cannot be named 'required'. Types and aliases cannot be named 'required'. @@ -11513,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 b27a9c13fb351..32e5f0bfb1dde 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1162,11 +1162,31 @@ Required member '{0}' cannot be less visible or have a setter less visible than the containing type '{1}'. + + Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer. + + Required member '{0}' must be settable. Required member '{0}' must be settable. + + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + + + + The required members list for '{0}' is malformed and cannot be interpreted. + 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. + + Types and aliases cannot be named 'required'. Types and aliases cannot be named 'required'. @@ -11514,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 703ad6ce92f74..3acf3259284e7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1162,11 +1162,31 @@ Required member '{0}' cannot be less visible or have a setter less visible than the containing type '{1}'. + + Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer. + + Required member '{0}' must be settable. Required member '{0}' must be settable. + + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + + + + The required members list for '{0}' is malformed and cannot be interpreted. + 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. + + Types and aliases cannot be named 'required'. Types and aliases cannot be named 'required'. @@ -11514,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 4b878b2f25257..af1fba364585a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1162,11 +1162,31 @@ Required member '{0}' cannot be less visible or have a setter less visible than the containing type '{1}'. + + Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer. + + Required member '{0}' must be settable. Required member '{0}' must be settable. + + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + + + + The required members list for '{0}' is malformed and cannot be interpreted. + 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. + + Types and aliases cannot be named 'required'. Types and aliases cannot be named 'required'. @@ -11514,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 67e4cbd0b3ba3..daecc4070a2fb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1162,11 +1162,31 @@ Required member '{0}' cannot be less visible or have a setter less visible than the containing type '{1}'. + + Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer. + + Required member '{0}' must be settable. Required member '{0}' must be settable. + + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + + + + The required members list for '{0}' is malformed and cannot be interpreted. + 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. + + Types and aliases cannot be named 'required'. Types and aliases cannot be named 'required'. @@ -11514,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 151c37cac76d5..8d22ee52e3c3b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1162,11 +1162,31 @@ Required member '{0}' cannot be less visible or have a setter less visible than the containing type '{1}'. + + Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer. + + Required member '{0}' must be settable. Required member '{0}' must be settable. + + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + + + + The required members list for '{0}' is malformed and cannot be interpreted. + 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. + + Types and aliases cannot be named 'required'. Types and aliases cannot be named 'required'. @@ -11519,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 c5cadfd841e55..c8b83c4dd8b24 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1162,11 +1162,31 @@ Required member '{0}' cannot be less visible or have a setter less visible than the containing type '{1}'. + + Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer. + + Required member '{0}' must be settable. Required member '{0}' must be settable. + + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + + + + The required members list for '{0}' is malformed and cannot be interpreted. + 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. + + Types and aliases cannot be named 'required'. Types and aliases cannot be named 'required'. @@ -11514,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 51ebef1477f6d..cbf586bb858b9 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs @@ -3,8 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; +using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -15,21 +17,19 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols; [CompilerTrait(CompilerFeature.RequiredMembers)] public class RequiredMembersTests : CSharpTestBase { - private const string RequiredMemberAttribute = @" -namespace System.Runtime.CompilerServices -{ - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)] - public class RequiredMemberAttribute : Attribute - { - public RequiredMemberAttribute() - { - } - } -} -"; + private const string RequiredMemberAttributeVB = @" +Namespace System.Runtime.CompilerServices + + Public Class RequiredMemberAttribute + Inherits Attribute + End Class +End Namespace"; - private static CSharpCompilation CreateCompilationWithRequiredMembers(CSharpTestSource source, CSharpParseOptions? parseOptions = null, CSharpCompilationOptions? options = null, string? assemblyName = null) - => CreateCompilation(new[] { source, RequiredMemberAttribute }, options: options, parseOptions: parseOptions, assemblyName: assemblyName); + private static CSharpCompilation CreateCompilationWithRequiredMembers(CSharpTestSource source, IEnumerable? references = null, CSharpParseOptions? parseOptions = null, CSharpCompilationOptions? options = null, string? assemblyName = null, TargetFramework targetFramework = TargetFramework.Standard) + => CreateCompilation(new[] { source, RequiredMemberAttribute }, references, options: options, parseOptions: parseOptions, assemblyName: assemblyName, targetFramework: targetFramework); + + private Compilation CreateVisualBasicCompilationWithRequiredMembers(string source) + => CreateVisualBasicCompilation(new[] { source, RequiredMemberAttributeVB }); private static Action ValidateRequiredMembersInModule(string[] memberPaths, string expectedAttributeLayout) { @@ -1222,4 +1222,1388 @@ struct S3 comp.VerifyDiagnostics(); } + + [Theory] + [CombinatorialData] + public void EnforcedRequiredMembers_NoInheritance_NoneSet(bool useMetadataReference, [CombinatorialValues("", " C")] string constructor) + { + var c = @" +public class C +{ + public required int Prop { get; set; } + public required int Field; +} +"; + + var creationWithoutAssignment = $@"C c = new{constructor}();"; + var comp = CreateCompilationWithRequiredMembers(new[] { c, creationWithoutAssignment }); + + var expectedDiagnostics = constructor == " C" ? new[] + { + // (1,11): error CS9506: Required member 'C.Prop' must be set in the object initializer. + // C c = new C(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.Prop").WithLocation(1, 11), + // (1,11): error CS9506: Required member 'C.Field' must be set in the object initializer. + // C c = new C(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.Field").WithLocation(1, 11) + } + : new[] { + // (1,7): error CS9506: Required member 'C.Prop' must be set in the object initializer. + // C c = new(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "new").WithArguments("C.Prop").WithLocation(1, 7), + // (1,7): error CS9506: Required member 'C.Field' must be set in the object initializer. + // C c = new(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "new").WithArguments("C.Field").WithLocation(1, 7) + }; + + comp.VerifyDiagnostics(expectedDiagnostics); + + var cComp = CreateCompilationWithRequiredMembers(c); + comp = CreateCompilation(creationWithoutAssignment, references: new[] { useMetadataReference ? cComp.ToMetadataReference() : cComp.EmitToImageReference() }); + + comp.VerifyDiagnostics(expectedDiagnostics); + } + + [Theory] + [CombinatorialData] + public void EnforcedRequiredMembers_NoInheritance_PartialSet(bool useMetadataReference, [CombinatorialValues("", " C")] string constructor) + { + var c = @" +public class C +{ + public required int Prop1 { get; set; } + public required int Prop2 { get; set; } + public required int Field1; + public required int Field2; +} +"; + + var creationWithoutAssignment = $@"C c = new{constructor}() {{ Prop2 = 1, Field2 = 1 }};"; + var comp = CreateCompilationWithRequiredMembers(new[] { c, creationWithoutAssignment }); + + var expectedDiagnostics = constructor == " C" ? new[] + { + // (1,11): error CS9506: Required member 'C.Prop1' must be set in the object initializer. + // C c = new C() { Prop2 = 1, Field2 = 1 }; + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.Prop1").WithLocation(1, 11), + // (1,11): error CS9506: Required member 'C.Field1' must be set in the object initializer. + // C c = new C() { Prop2 = 1, Field2 = 1 }; + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.Field1").WithLocation(1, 11) + } + : new[] { + // (1,7): error CS9506: Required member 'C.Prop1' must be set in the object initializer. + // C c = new() { Prop2 = 1, Field2 = 1 }; + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "new").WithArguments("C.Prop1").WithLocation(1, 7), + // (1,7): error CS9506: Required member 'C.Field1' must be set in the object initializer. + // C c = new() { Prop2 = 1, Field2 = 1 }; + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "new").WithArguments("C.Field1").WithLocation(1, 7) + }; + + comp.VerifyDiagnostics(expectedDiagnostics); + + var cComp = CreateCompilationWithRequiredMembers(c); + comp = CreateCompilation(creationWithoutAssignment, references: new[] { useMetadataReference ? cComp.ToMetadataReference() : cComp.EmitToImageReference() }); + + comp.VerifyDiagnostics(expectedDiagnostics); + } + + [Theory] + [CombinatorialData] + public void EnforcedRequiredMembers_NoInheritance_AllSet(bool useMetadataReference, [CombinatorialValues("", " C")] string constructor) + { + var c = @" +public class C +{ + public required int Prop1 { get; set; } + public required int Field1; +} +"; + + var creationWithoutAssignment = @" +C c = new" + constructor + @"() { Prop1 = 1, Field1 = 1 }; +System.Console.WriteLine($""{c.Prop1}, {c.Field1}""); +"; + var comp = CreateCompilationWithRequiredMembers(new[] { c, creationWithoutAssignment }); + CompileAndVerify(comp, expectedOutput: "1, 1"); + + var cComp = CreateCompilationWithRequiredMembers(c); + comp = CreateCompilation(creationWithoutAssignment, references: new[] { useMetadataReference ? cComp.ToMetadataReference() : cComp.EmitToImageReference() }); + CompileAndVerify(comp, expectedOutput: "1, 1"); + } + + [Fact] + public void EnforcedRequiredMembers_NoInheritance_Unsettable() + { + var c = @" +var c = new C(); +c = new C() { Prop1 = 1, Field1 = 1 }; + +public class C +{ + public required int Prop1 { get; } + public required readonly int Field1; +} +"; + var comp = CreateCompilationWithRequiredMembers(c); + comp.VerifyDiagnostics( + // (2,13): error CS9506: Required member 'C.Prop1' must be set in the object initializer. + // var c = new C(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.Prop1").WithLocation(2, 13), + // (2,13): error CS9506: Required member 'C.Field1' must be set in the object initializer. + // var c = new C(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.Field1").WithLocation(2, 13), + // (3,15): error CS0200: Property or indexer 'C.Prop1' cannot be assigned to -- it is read only + // c = new C() { Prop1 = 1, Field1 = 1 }; + Diagnostic(ErrorCode.ERR_AssgReadonlyProp, "Prop1").WithArguments("C.Prop1").WithLocation(3, 15), + // (3,26): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // c = new C() { Prop1 = 1, Field1 = 1 }; + Diagnostic(ErrorCode.ERR_AssgReadonly, "Field1").WithLocation(3, 26), + // (7,25): error CS9505: Required member 'C.Prop1' must be settable. + // public required int Prop1 { get; } + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSettable, "Prop1").WithArguments("C.Prop1").WithLocation(7, 25), + // (8,34): error CS9505: Required member 'C.Field1' must be settable. + // public required readonly int Field1; + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSettable, "Field1").WithArguments("C.Field1").WithLocation(8, 34) + ); + } + + [Fact] + public void EnforcedRequiredMembers_NoInheritance_DisallowedNestedObjectInitializer() + { + var c = @" +var c = new C() { D1 = { NestedProp = 1 }, D2 = { 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); + 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), + // (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) + ); + } + + [Fact] + public void EnforcedRequiredMembers_NoInheritance_DisallowedNestedCollectionInitializer() + { + var c = @" +using System.Collections.Generic; +var c = new C() { L1 = { 1, 2, 3 }, L2 = { 4, 5, 6 } }; + +public class C +{ + public required List L1 { get; set; } + public required List L2; +} +"; + var comp = CreateCompilationWithRequiredMembers(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), + // (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) + ); + } + + [Theory] + [CombinatorialData] + public void EnforcedRequiredMembers_Inheritance_NoneSet(bool useMetadataReference) + { + var @base = @" +public class Base +{ + public required int Prop1 { get; set; } + public required int Field1; +} +"; + + var code = @" +_ = new Derived(); + +public class Derived : Base +{ + public required int Prop2 { get; set; } + public required int Field2; +} +"; + + var comp = CreateCompilationWithRequiredMembers(new[] { @base, code }); + + var expectedDiagnostics = new[] { + // (2,9): error CS9506: Required member 'Base.Prop1' must be set in the object initializer. + // _ = new Derived(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Base.Prop1").WithLocation(2, 9), + // (2,9): error CS9506: Required member 'Base.Field1' must be set in the object initializer. + // _ = new Derived(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Base.Field1").WithLocation(2, 9), + // (2,9): error CS9506: Required member 'Derived.Prop2' must be set in the object initializer. + // _ = new Derived(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Prop2").WithLocation(2, 9), + // (2,9): error CS9506: Required member 'Derived.Field2' must be set in the object initializer. + // _ = new Derived(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Field2").WithLocation(2, 9) + }; + + comp.VerifyDiagnostics(expectedDiagnostics); + + var baseComp = CreateCompilationWithRequiredMembers(@base); + baseComp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(code, new[] { useMetadataReference ? baseComp.ToMetadataReference() : baseComp.EmitToImageReference() }); + comp.VerifyDiagnostics(expectedDiagnostics); + } + + [Theory] + [CombinatorialData] + public void EnforcedRequiredMembers_Inheritance_PartialSet(bool useMetadataReference) + { + var @base = @" +public class Base +{ + public required int Prop1 { get; set; } + public required int Field1; + public required int Prop2 { get; set; } + public required int Field2; +} +"; + + var code = @" +_ = new Derived() { Prop1 = 1, Field1 = 1, Prop3 = 3, Field3 = 3 }; + +public class Derived : Base +{ + public required int Prop3 { get; set; } + public required int Field3; + public required int Prop4 { get; set; } + public required int Field4; +} +"; + + var comp = CreateCompilationWithRequiredMembers(new[] { @base, code }); + + var expectedDiagnostics = new[] { + // (2,9): error CS9506: Required member 'Base.Prop2' must be set in the object initializer. + // _ = new Derived() { Prop1 = 1, Field1 = 1, Prop3 = 3, Field3 = 3 }; + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Base.Prop2").WithLocation(2, 9), + // (2,9): error CS9506: Required member 'Base.Field2' must be set in the object initializer. + // _ = new Derived() { Prop1 = 1, Field1 = 1, Prop3 = 3, Field3 = 3 }; + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Base.Field2").WithLocation(2, 9), + // (2,9): error CS9506: Required member 'Derived.Prop4' must be set in the object initializer. + // _ = new Derived() { Prop1 = 1, Field1 = 1, Prop3 = 3, Field3 = 3 }; + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Prop4").WithLocation(2, 9), + // (2,9): error CS9506: Required member 'Derived.Field4' must be set in the object initializer. + // _ = new Derived() { Prop1 = 1, Field1 = 1, Prop3 = 3, Field3 = 3 }; + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Field4").WithLocation(2, 9) + }; + + comp.VerifyDiagnostics(expectedDiagnostics); + + var baseComp = CreateCompilationWithRequiredMembers(@base); + baseComp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(code, new[] { useMetadataReference ? baseComp.ToMetadataReference() : baseComp.EmitToImageReference() }); + comp.VerifyDiagnostics(expectedDiagnostics); + } + + [Theory] + [CombinatorialData] + public void EnforcedRequiredMembers_Inheritance_AllSet(bool useMetadataReference) + { + var @base = @" +public class Base +{ + public required int Prop1 { get; set; } + public required int Field1; +} +"; + + var code = @" +_ = new Derived() { Prop1 = 1, Field1 = 1, Prop2 = 2, Field2 = 2 }; + +public class Derived : Base +{ + public required int Prop2 { get; set; } + public required int Field2; +} +"; + + var comp = CreateCompilationWithRequiredMembers(new[] { @base, code }); + CompileAndVerify(comp).VerifyDiagnostics(); + + var baseComp = CreateCompilationWithRequiredMembers(@base); + baseComp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(code, new[] { useMetadataReference ? baseComp.ToMetadataReference() : baseComp.EmitToImageReference() }); + CompileAndVerify(comp).VerifyDiagnostics(); + } + + [Fact] + public void EnforcedRequiredMembers_ThroughRetargeting_NoneSet() + { + var retargetedCode = @"public class C {}"; + + var originalC = CreateCompilation(new AssemblyIdentity("Ret", new Version(1, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences); + + var @base = @" +public class Base +{ + public required C Prop1 { get; set; } + public required C Field1; +} +"; + + var baseComp = CreateCompilationWithRequiredMembers(@base, new[] { originalC.ToMetadataReference() }, targetFramework: TargetFramework.Standard); + + var retargetedC = CreateCompilation(new AssemblyIdentity("Ret", new Version(2, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences); + + var code = @" +_ = new Derived(); + +public class Derived : Base +{ + public required C Prop2 { get; set; } + public required C Field2; +} +"; + + var comp = CreateCompilation(code, new[] { baseComp.ToMetadataReference(), retargetedC.ToMetadataReference() }, targetFramework: TargetFramework.Standard); + comp.VerifyDiagnostics( + // (2,9): error CS9506: Required member 'Base.Field1' must be set in the object initializer. + // _ = new Derived(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Base.Field1").WithLocation(2, 9), + // (2,9): error CS9506: Required member 'Base.Prop1' must be set in the object initializer. + // _ = new Derived(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Base.Prop1").WithLocation(2, 9), + // (2,9): error CS9506: Required member 'Derived.Prop2' must be set in the object initializer. + // _ = new Derived(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Prop2").WithLocation(2, 9), + // (2,9): error CS9506: Required member 'Derived.Field2' must be set in the object initializer. + // _ = new Derived(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Field2").WithLocation(2, 9) + ); + + var baseSymbol = comp.GetTypeByMetadataName("Base"); + Assert.IsType(baseSymbol); + } + + [Fact] + public void EnforcedRequiredMembers_ThroughRetargeting_AllSet() + { + var retargetedCode = @"public class C {}"; + + var originalC = CreateCompilation(new AssemblyIdentity("Ret", new Version(1, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences); + + var @base = @" +public class Base +{ + public required C Prop1 { get; set; } + public required C Field1; +} +"; + + var baseComp = CreateCompilationWithRequiredMembers(@base, new[] { originalC.ToMetadataReference() }, targetFramework: TargetFramework.Standard); + + var retargetedC = CreateCompilation(new AssemblyIdentity("Ret", new Version(2, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences); + + var code = @" +_ = new Derived() { Prop1 = new(), Field1 = new(), Prop2 = new(), Field2 = new() }; + +public class Derived : Base +{ + public required C Prop2 { get; set; } + public required C Field2; +} +"; + + var comp = CreateCompilation(code, new[] { baseComp.ToMetadataReference(), retargetedC.ToMetadataReference() }, targetFramework: TargetFramework.Standard); + comp.VerifyDiagnostics(); + + var baseSymbol = comp.GetTypeByMetadataName("Base"); + Assert.IsType(baseSymbol); + } + + [Theory] + [CombinatorialData] + public void EnforcedRequiredMembers_Override_NoneSet(bool useMetadataReference) + { + var @base = @" +public class Base +{ + public virtual required int Prop1 { get; set; } +} +"; + + var code = @" +_ = new Derived(); + +public class Derived : Base +{ + public override required int Prop1 { get; set; } +} +"; + + var comp = CreateCompilationWithRequiredMembers(new[] { @base, code }); + + var expectedDiagnostics = new[] { + // (2,9): error CS9506: Required member 'Derived.Prop1' must be set in the object initializer. + // _ = new Derived(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Prop1").WithLocation(2, 9) + }; + + comp.VerifyDiagnostics(expectedDiagnostics); + + var baseComp = CreateCompilationWithRequiredMembers(@base); + baseComp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(code, new[] { useMetadataReference ? baseComp.ToMetadataReference() : baseComp.EmitToImageReference() }); + comp.VerifyDiagnostics(expectedDiagnostics); + } + + [Theory] + [CombinatorialData] + public void EnforcedRequiredMembers_Override_PartialSet(bool useMetadataReference) + { + var @base = @" +public class Base +{ + public virtual required int Prop1 { get; set; } + public virtual required int Prop2 { get; set; } +} +"; + + var code = @" +_ = new Derived() { Prop2 = 1 }; + +public class Derived : Base +{ + public override required int Prop1 { get; set; } + public override required int Prop2 { get; set; } +} +"; + + var comp = CreateCompilationWithRequiredMembers(new[] { @base, code }); + + var expectedDiagnostics = new[] { + // (2,9): error CS9506: Required member 'Derived.Prop1' must be set in the object initializer. + // _ = new Derived() { Prop2 = 1 }; + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Prop1").WithLocation(2, 9) + }; + + comp.VerifyDiagnostics(expectedDiagnostics); + + var baseComp = CreateCompilationWithRequiredMembers(@base); + baseComp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(code, new[] { useMetadataReference ? baseComp.ToMetadataReference() : baseComp.EmitToImageReference() }); + comp.VerifyDiagnostics(expectedDiagnostics); + } + + [Theory] + [CombinatorialData] + public void EnforcedRequiredMembers_Override_AllSet(bool useMetadataReference) + { + var @base = @" +public class Base +{ + public virtual required int Prop1 { get; set; } + public virtual required int Prop2 { get; set; } +} +"; + + var code = @" +_ = new Derived() { Prop1 = 1, Prop2 = 1 }; + +public class Derived : Base +{ + public override required int Prop1 { get; set; } + public override required int Prop2 { get; set; } +} +"; + + var comp = CreateCompilationWithRequiredMembers(new[] { @base, code }); + CompileAndVerify(comp).VerifyDiagnostics(); + + var baseComp = CreateCompilationWithRequiredMembers(@base); + baseComp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(code, new[] { useMetadataReference ? baseComp.ToMetadataReference() : baseComp.EmitToImageReference() }); + CompileAndVerify(comp).VerifyDiagnostics(); + } + + [Fact] + public void EnforcedRequiredMembers_OverrideRetargeted_AllSet() + { + var retargetedCode = @"public class C {}"; + var originalC = CreateCompilation(new AssemblyIdentity("Ret", new Version(1, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences); + + var @base = @" +public class Base +{ + public required virtual C Prop1 { get; set; } +} +"; + + var baseComp = CreateCompilationWithRequiredMembers(@base, new[] { originalC.ToMetadataReference() }, targetFramework: TargetFramework.Standard); + var retargetedC = CreateCompilation(new AssemblyIdentity("Ret", new Version(2, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences); + + var code = @" +_ = new Derived() { Prop1 = new() }; + +public class Derived : Base +{ + public override required C Prop1 { get; set; } +} +"; + + var comp = CreateCompilation(code, new[] { baseComp.ToMetadataReference(), retargetedC.ToMetadataReference() }); + comp.VerifyDiagnostics(); + + Assert.IsType(comp.GetTypeByMetadataName("Base")); + } + + [Fact] + public void EnforcedRequiredMembers_OverrideRetargeted_NoneSet() + { + var retargetedCode = @"public class C {}"; + var originalC = CreateCompilation(new AssemblyIdentity("Ret", new Version(1, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences); + + var @base = @" +public class Base +{ + public required virtual C Prop1 { get; set; } +} +"; + + var baseComp = CreateCompilationWithRequiredMembers(@base, new[] { originalC.ToMetadataReference() }, targetFramework: TargetFramework.Standard); + var retargetedC = CreateCompilation(new AssemblyIdentity("Ret", new Version(2, 0, 0, 0), isRetargetable: true), retargetedCode, TargetFrameworkUtil.StandardReferences); + + var code = @" +_ = new Derived(); + +public class Derived : Base +{ + public override required C Prop1 { get; set; } +} +"; + + var comp = CreateCompilation(code, new[] { baseComp.ToMetadataReference(), retargetedC.ToMetadataReference() }); + comp.VerifyDiagnostics( + // (2,9): error CS9506: Required member 'Derived.Prop1' must be set in the object initializer. + // _ = new Derived(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Prop1").WithLocation(2, 9) + ); + + Assert.IsType(comp.GetTypeByMetadataName("Base")); + } + + [Fact] + public void EnforcedRequiredMembers_ShadowedInSource_01() + { + var vb = @" +Imports System.Runtime.CompilerServices + +Public Class Base + + Public Property P As Integer +End Class + + +Public Class Derived + Inherits Base + + Public Shadows Property P As Integer +End Class +"; + + var vbComp = CreateVisualBasicCompilationWithRequiredMembers(vb); + CompileAndVerify(vbComp).VerifyDiagnostics(); + + var c = @" +_ = new Derived2(); +_ = new Derived3(); + +class Derived2 : Derived +{ + public Derived2() {} +} +class Derived3 : Derived { }"; + + var comp = CreateCompilation(c, new[] { vbComp.EmitToImageReference() }); + comp.VerifyDiagnostics( + // (7,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // public Derived2() {} + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived2").WithArguments("Derived").WithLocation(7, 12), + // (7,12): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // public Derived2() {} + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "Derived2").WithArguments("Derived").WithLocation(7, 12), + // (9,7): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // class Derived3 : Derived { } + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived3").WithArguments("Derived").WithLocation(9, 7), + // (9,7): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // class Derived3 : Derived { } + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "Derived3").WithArguments("Derived").WithLocation(9, 7) + ); + } + + [Fact] + public void EnforcedRequiredMembers_ShadowedInSource_02() + { + var vb = @" +Imports System.Runtime.CompilerServices + +Public Class Base + + Public Property P As Integer +End Class + + +Public Class Derived + Inherits Base + Public Shadows Property P As Integer +End Class +"; + + var vbComp = CreateVisualBasicCompilationWithRequiredMembers(vb); + CompileAndVerify(vbComp).VerifyDiagnostics(); + + var c = @" +_ = new Derived2(); +_ = new Derived3(); + +class Derived2 : Derived +{ + public Derived2() {} +} +class Derived3 : Derived { }"; + + var comp = CreateCompilation(c, new[] { vbComp.EmitToImageReference() }); + comp.VerifyDiagnostics( + // (7,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // public Derived2() {} + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived2").WithArguments("Derived").WithLocation(7, 12), + // (7,12): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // public Derived2() {} + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "Derived2").WithArguments("Derived").WithLocation(7, 12), + // (9,7): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // class Derived3 : Derived { } + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived3").WithArguments("Derived").WithLocation(9, 7), + // (9,7): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // class Derived3 : Derived { } + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "Derived3").WithArguments("Derived").WithLocation(9, 7) + ); + } + + [Fact] + public void EnforcedRequiredMembers_ShadowedInSource_03() + { + var vb = @" +Imports System.Runtime.CompilerServices + +Public Class Base + + Public Property P As Integer +End Class + +Public Class Derived + Inherits Base + Public Shadows Property P As Integer +End Class +"; + + var vbComp = CreateVisualBasicCompilationWithRequiredMembers(vb); + CompileAndVerify(vbComp).VerifyDiagnostics(); + + var c = @" +_ = new Derived2(); +_ = new Derived3(); + +class Derived2 : Derived +{ + public Derived2() {} +} +class Derived3 : Derived { }"; + + var comp = CreateCompilation(c, new[] { vbComp.EmitToImageReference() }); + comp.VerifyDiagnostics( + // (7,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // public Derived2() {} + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived2").WithArguments("Derived").WithLocation(7, 12), + // (7,12): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // public Derived2() {} + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "Derived2").WithArguments("Derived").WithLocation(7, 12), + // (9,7): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // class Derived3 : Derived { } + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived3").WithArguments("Derived").WithLocation(9, 7), + // (9,7): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // class Derived3 : Derived { } + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "Derived3").WithArguments("Derived").WithLocation(9, 7) + ); + } + + [Fact] + public void EnforcedRequiredMembers_ShadowedInSource_04() + { + var original = @" +public record Base +{ + public required int P { get; init; } +} +"; + + var originalComp = CreateCompilationWithRequiredMembers(new[] { original, IsExternalInitTypeDefinition }, assemblyName: "original"); + CompileAndVerify(originalComp).VerifyDiagnostics(); + + // This IL is the equivalent of: + // public record Derived : Base + // { + // public new int P { get; init; } + // } + + var ilSource = @" +.assembly extern original {} + +.class public auto ansi beforefieldinit Derived + extends [original]Base + implements class [mscorlib]System.IEquatable`1 +{ + .custom instance void [original]RequiredMemberAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field private initonly int32 '

k__BackingField' + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( + 01 00 00 00 00 00 00 00 + ) + + // Methods + .method family hidebysig specialname virtual + instance class [mscorlib]System.Type get_EqualityContract () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + ldnull + throw + } // end of method Derived::get_EqualityContract + + .method public hidebysig specialname + instance int32 get_P () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + ldnull + throw + } // end of method Derived::get_P + + .method public hidebysig specialname + instance void modreq([mscorlib]System.Runtime.CompilerServices.IsExternalInit) set_P ( + int32 'value' + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + ldnull + throw + } // end of method Derived::set_P + + .method public hidebysig virtual + instance string ToString () cil managed + { + ldnull + throw + } // end of method Derived::ToString + + .method family hidebysig virtual + instance bool PrintMembers ( + class [mscorlib]System.Text.StringBuilder builder + ) cil managed + { + ldnull + throw + } // end of method Derived::PrintMembers + + .method public hidebysig specialname static + bool op_Inequality ( + class Derived left, + class Derived right + ) cil managed + { + ldnull + throw + } // end of method Derived::op_Inequality + + .method public hidebysig specialname static + bool op_Equality ( + class Derived left, + class Derived right + ) cil managed + { + ldnull + throw + } // end of method Derived::op_Equality + + .method public hidebysig virtual + instance int32 GetHashCode () cil managed + { + ldnull + throw + } // end of method Derived::GetHashCode + + .method public hidebysig virtual + instance bool Equals ( + object obj + ) cil managed + { + ldnull + throw + } // end of method Derived::Equals + + .method public final hidebysig virtual + instance bool Equals ( + class [original]Base other + ) cil managed + { + ldnull + throw + } // end of method Derived::Equals + + .method public hidebysig newslot virtual + instance bool Equals ( + class Derived other + ) cil managed + { + ldnull + throw + } // end of method Derived::Equals + + .method public hidebysig newslot virtual + instance class Derived '$' () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.PreserveBaseOverridesAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance class [original]Base [original]Base::'$'() + ldnull + throw + } // end of method Derived::'$' + + .method family hidebysig specialname rtspecialname + instance void .ctor ( + class Derived original + ) cil managed + { + ldnull + throw + } // end of method Derived::.ctor + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldnull + throw + } // end of method Derived::.ctor + + // Properties + .property instance class [mscorlib]System.Type EqualityContract() + { + .get instance class [mscorlib]System.Type Derived::get_EqualityContract() + } + .property instance int32 P() + { + .get instance int32 Derived::get_P() + .set instance void modreq([mscorlib]System.Runtime.CompilerServices.IsExternalInit) Derived::set_P(int32) + } + +} // end of class Derived +"; + + var il = CompileIL(ilSource); + + var c = @" +_ = new DerivedDerived1(); +_ = new DerivedDerived2(); +_ = new DerivedDerived3(); + +record DerivedDerived1 : Derived +{ + public DerivedDerived1() + { + } +} +record DerivedDerived2 : Derived +{ +} +record DerivedDerived3() : Derived; +"; + + var comp = CreateCompilation(c, new[] { il, originalComp.EmitToImageReference() }); + // PROTOTYPE(req): do we want to take the effort to remove some of these duplicate errors? + comp.VerifyDiagnostics( + // (6,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // record DerivedDerived1 : Derived + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(6, 8), + // (6,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // record DerivedDerived1 : Derived + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(6, 8), + // (8,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // public DerivedDerived1() + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(8, 12), + // (8,12): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // public DerivedDerived1() + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(8, 12), + // (12,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // record DerivedDerived2 : Derived + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), + // (12,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // record DerivedDerived2 : Derived + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), + // (12,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // record DerivedDerived2 : Derived + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), + // (12,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // record DerivedDerived2 : Derived + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), + // (15,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // record DerivedDerived3() : Derived; + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8), + // (15,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // record DerivedDerived3() : Derived; + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8), + // (15,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // record DerivedDerived3() : Derived; + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8), + // (15,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // record DerivedDerived3() : Derived; + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8) + ); + } + + [Fact] + public void EnforcedRequiredMembers_ShadowedInSource_06() + { + var original = @" +public record Base +{ + public required int P { get; init; } +} +"; + + var originalComp = CreateCompilationWithRequiredMembers(new[] { original, IsExternalInitTypeDefinition }, assemblyName: "original"); + CompileAndVerify(originalComp).VerifyDiagnostics(); + + // This IL is the equivalent of: + // public record Derived : Base + // { + // public new required int P { get; init; } + // } + + var ilSource = @" +.assembly extern original {} + +.class public auto ansi beforefieldinit Derived + extends [original]Base + implements class [mscorlib]System.IEquatable`1 +{ + .custom instance void [original]RequiredMemberAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field private initonly int32 '

k__BackingField' + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( + 01 00 00 00 00 00 00 00 + ) + + // Methods + .method family hidebysig specialname virtual + instance class [mscorlib]System.Type get_EqualityContract () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + ldnull + throw + } // end of method Derived::get_EqualityContract + + .method public hidebysig specialname + instance int32 get_P () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + ldnull + throw + } // end of method Derived::get_P + + .method public hidebysig specialname + instance void modreq([mscorlib]System.Runtime.CompilerServices.IsExternalInit) set_P ( + int32 'value' + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + ldnull + throw + } // end of method Derived::set_P + + .method public hidebysig virtual + instance string ToString () cil managed + { + ldnull + throw + } // end of method Derived::ToString + + .method family hidebysig virtual + instance bool PrintMembers ( + class [mscorlib]System.Text.StringBuilder builder + ) cil managed + { + ldnull + throw + } // end of method Derived::PrintMembers + + .method public hidebysig specialname static + bool op_Inequality ( + class Derived left, + class Derived right + ) cil managed + { + ldnull + throw + } // end of method Derived::op_Inequality + + .method public hidebysig specialname static + bool op_Equality ( + class Derived left, + class Derived right + ) cil managed + { + ldnull + throw + } // end of method Derived::op_Equality + + .method public hidebysig virtual + instance int32 GetHashCode () cil managed + { + ldnull + throw + } // end of method Derived::GetHashCode + + .method public hidebysig virtual + instance bool Equals ( + object obj + ) cil managed + { + ldnull + throw + } // end of method Derived::Equals + + .method public final hidebysig virtual + instance bool Equals ( + class [original]Base other + ) cil managed + { + ldnull + throw + } // end of method Derived::Equals + + .method public hidebysig newslot virtual + instance bool Equals ( + class Derived other + ) cil managed + { + ldnull + throw + } // end of method Derived::Equals + + .method public hidebysig newslot virtual + instance class Derived '$' () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.PreserveBaseOverridesAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance class [original]Base [original]Base::'$'() + ldnull + throw + } // end of method Derived::'$' + + .method family hidebysig specialname rtspecialname + instance void .ctor ( + class Derived original + ) cil managed + { + ldnull + throw + } // end of method Derived::.ctor + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldnull + throw + } // end of method Derived::.ctor + + // Properties + .property instance class [mscorlib]System.Type EqualityContract() + { + .get instance class [mscorlib]System.Type Derived::get_EqualityContract() + } + .property instance int32 P() + { + .custom instance void [original]RequiredMemberAttribute::.ctor() = ( + 01 00 00 00 + ) + .get instance int32 Derived::get_P() + .set instance void modreq([mscorlib]System.Runtime.CompilerServices.IsExternalInit) Derived::set_P(int32) + } + +} // end of class Derived +"; + + var il = CompileIL(ilSource); + + var c = @" +_ = new DerivedDerived1(); +_ = new DerivedDerived2(); +_ = new DerivedDerived3(); + +record DerivedDerived1 : Derived +{ + public DerivedDerived1() + { + } +} +record DerivedDerived2 : Derived +{ +} +record DerivedDerived3() : Derived; +"; + + var comp = CreateCompilation(c, new[] { il, originalComp.EmitToImageReference() }); + // PROTOTYPE(req): do we want to take the effort to remove some of these duplicate errors? + comp.VerifyDiagnostics( + // (6,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // record DerivedDerived1 : Derived + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(6, 8), + // (6,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // record DerivedDerived1 : Derived + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(6, 8), + // (8,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // public DerivedDerived1() + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(8, 12), + // (8,12): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // public DerivedDerived1() + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(8, 12), + // (12,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // record DerivedDerived2 : Derived + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), + // (12,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // record DerivedDerived2 : Derived + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), + // (12,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // record DerivedDerived2 : Derived + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), + // (12,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // record DerivedDerived2 : Derived + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), + // (15,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // record DerivedDerived3() : Derived; + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8), + // (15,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // record DerivedDerived3() : Derived; + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8), + // (15,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // record DerivedDerived3() : Derived; + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8), + // (15,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // record DerivedDerived3() : Derived; + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8) + ); + } + + [Fact] + public void EnforcedRequiredMembers_ShadowedFromMetadata_01() + { + var vb = @" +Imports System.Runtime.CompilerServices + +Public Class Base + + Public Property P As Integer +End Class + + +Public Class Derived + Inherits Base + + Public Shadows Property P As Integer +End Class +"; + + var vbComp = CreateVisualBasicCompilationWithRequiredMembers(vb); + CompileAndVerify(vbComp).VerifyDiagnostics(); + + var c = @"_ = new Derived();"; + var comp = CreateCompilation(c, new[] { vbComp.EmitToImageReference() }); + comp.VerifyDiagnostics( + // (1,9): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // _ = new Derived(); + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "Derived").WithArguments("Derived").WithLocation(1, 9) + ); + } + + [Fact] + public void EnforcedRequiredMembers_ShadowedFromMetadata_02() + { + var vb = @" +Imports System.Runtime.CompilerServices + +Public Class Base + + Public Property P As Integer +End Class + + +Public Class Derived + Inherits Base + Public Shadows Property P As Integer +End Class +"; + + var vbComp = CreateVisualBasicCompilationWithRequiredMembers(vb); + CompileAndVerify(vbComp).VerifyDiagnostics(); + + var c = @"_ = new Derived();"; + var comp = CreateCompilation(c, new[] { vbComp.EmitToImageReference() }); + comp.VerifyDiagnostics( + // (1,9): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // _ = new Derived(); + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "Derived").WithArguments("Derived").WithLocation(1, 9) + ); + } + + [Fact] + public void EnforcedRequiredMembers_ShadowedFromMetadata_03() + { + var vb = @" +Imports System.Runtime.CompilerServices + +Public Class Base + + Public Property P As Integer +End Class + +Public Class Derived + Inherits Base + Public Shadows Property P As Integer +End Class +"; + + var vbComp = CreateVisualBasicCompilationWithRequiredMembers(vb); + CompileAndVerify(vbComp).VerifyDiagnostics(); + + var c = @"_ = new Derived();"; + var comp = CreateCompilation(c, new[] { vbComp.EmitToImageReference() }); + comp.VerifyDiagnostics( + // (1,9): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // _ = new Derived(); + Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "Derived").WithArguments("Derived").WithLocation(1, 9) + ); + } + + [Fact] + public void InterpolatedStringHandlerWithRequiredMembers() + { + var code = @" +CustomHandler handler = $""""; + +partial class CustomHandler +{ + public required int Field; +}"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial class", useBoolReturns: false); + + var comp = CreateCompilationWithRequiredMembers(new[] { code, handler }); + comp.VerifyDiagnostics( + // (2,25): error CS9506: Required member 'CustomHandler.Field' must be set in the object initializer. + // CustomHandler handler = $""; + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, @"$""""").WithArguments("CustomHandler.Field").WithLocation(2, 25) + ); + } + + [Fact] + public void CoClassWithRequiredMembers_NoneSet() + { + string code = @" +using System; +using System.Runtime.InteropServices; + +_ = new I(); + +[ComImport, Guid(""00020810-0000-0000-C000-000000000046"")] +[CoClass(typeof(C))] +interface I +{ +} + +class C : I +{ + public required int P { get; set; } +} + +"; + var comp = CreateCompilationWithRequiredMembers(code); + comp.VerifyDiagnostics( + // (5,9): error CS9506: Required member 'C.P' must be set in the object initializer. + // _ = new I(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "I").WithArguments("C.P").WithLocation(5, 9) + ); + } + + [Fact] + public void CoClassWithRequiredMembers_AllSet() + { + string code = @" +using System; +using System.Runtime.InteropServices; + +_ = new I() { P = 1 }; + +[ComImport, Guid(""00020810-0000-0000-C000-000000000046"")] +[CoClass(typeof(C))] +interface I +{ + public int P { get; set; } +} + +class C : I +{ + public required int P { get; set; } +} +"; + var comp = CreateCompilationWithRequiredMembers(code); + comp.VerifyDiagnostics(); + } } diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 7ec96c09388a9..953e494b360f7 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -625,6 +625,19 @@ public UnmanagedCallersOnlyAttribute() { } } }"; + protected const string RequiredMemberAttribute = @" +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)] + public class RequiredMemberAttribute : Attribute + { + public RequiredMemberAttribute() + { + } + } +} +"; + protected static CSharpCompilationOptions WithNullableEnable(CSharpCompilationOptions options = null) { return WithNullable(options, NullableContextOptions.Enable); @@ -1295,12 +1308,12 @@ internal static void VerifyUsesOfNullability(Symbol symbol, ImmutableArray references, CSharpCompilationOptions options = null, CSharpParseOptions parseOptions = null) { - var trees = (source == null) ? null : source.Select(s => Parse(s, options: parseOptions)).ToArray(); + var trees = (source ?? CSharpTestSource.None).GetSyntaxTrees(parseOptions); Func createCompilationLambda = () => CSharpCompilation.Create(identity.Name, options: options ?? TestOptions.ReleaseDll, references: references, syntaxTrees: trees); ValidateCompilation(createCompilationLambda); From 740c4edf4246ef014ea332bee725593da6285562 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Fri, 11 Mar 2022 11:28:27 -0800 Subject: [PATCH 2/7] PR Feedback: * Support checking attribute constructors * Suppress some duplicate diagnostics * Minor other feedback. --- .../Portable/Binder/Binder_Attributes.cs | 8 +- .../Portable/Binder/Binder_Expressions.cs | 73 +++- .../CSharp/Portable/CSharpResources.resx | 4 +- .../Source/SourceMemberContainerSymbol.cs | 2 +- .../Portable/Symbols/SymbolExtensions.cs | 4 +- .../Portable/xlf/CSharpResources.cs.xlf | 10 +- .../Portable/xlf/CSharpResources.de.xlf | 10 +- .../Portable/xlf/CSharpResources.es.xlf | 10 +- .../Portable/xlf/CSharpResources.fr.xlf | 10 +- .../Portable/xlf/CSharpResources.it.xlf | 10 +- .../Portable/xlf/CSharpResources.ja.xlf | 10 +- .../Portable/xlf/CSharpResources.ko.xlf | 10 +- .../Portable/xlf/CSharpResources.pl.xlf | 10 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 10 +- .../Portable/xlf/CSharpResources.ru.xlf | 10 +- .../Portable/xlf/CSharpResources.tr.xlf | 10 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 10 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 10 +- .../Symbol/Symbols/RequiredMembersTests.cs | 360 +++++++++++++----- 19 files changed, 396 insertions(+), 185 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs index 951343dbe27cb..02d81b2cc9a3a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs @@ -215,6 +215,11 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a ImmutableArray boundNamedArguments = analyzedArguments.NamedArguments?.ToImmutableAndFree() ?? ImmutableArray.Empty; Debug.Assert(boundNamedArguments.All(arg => !arg.Right.NeedsToBeConverted())); + if (attributeConstructor is not null) + { + CheckRequiredMembersInObjectInitializer(attributeConstructor, ImmutableArray.CastUp(boundNamedArguments), node, diagnostics); + } + analyzedArguments.ConstructorArguments.Free(); return new BoundAttribute(node, attributeConstructor, boundConstructorArguments, boundConstructorArgumentNamesOpt, argsToParamsOpt, expanded, @@ -572,7 +577,8 @@ protected MethodSymbol BindAttributeConstructor( diagnostics, out memberResolutionResult, out candidateConstructors, - allowProtectedConstructorsOfBaseType: true)) + allowProtectedConstructorsOfBaseType: true, + suppressUnsupportedRequiredMembersError: false)) { resultKind = resultKind.WorseResultKind( memberResolutionResult.IsValid && !IsConstructorAccessible(memberResolutionResult.Member, ref useSiteInfo) ? diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index bc0191b8dd97e..09141e3ff0c49 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -4064,7 +4064,8 @@ private BoundExpression BindConstructorInitializerCore( diagnostics, out memberResolutionResult, out candidateConstructors, - allowProtectedConstructorsOfBaseType: true); + allowProtectedConstructorsOfBaseType: true, + suppressUnsupportedRequiredMembersError: true); MethodSymbol resultMember = memberResolutionResult.Member; validateRecordCopyConstructor(constructor, baseType, resultMember, errorLocation, diagnostics); @@ -4967,55 +4968,71 @@ private static void ReportDuplicateObjectMemberInitializers(BoundExpression boun } } +#nullable enable private static void CheckRequiredMembersInObjectInitializer( - BoundObjectCreationExpression creation, + MethodSymbol constructor, + ImmutableArray initializers, + SyntaxNode creationSyntax, BindingDiagnosticBag diagnostics) { - if (!creation.Constructor.ShouldCheckRequiredMembers()) + if (!constructor.ShouldCheckRequiredMembers()) { return; } - if (creation.Constructor.ContainingType.HasRequiredMembersError) + if (constructor.ContainingType.HasRequiredMembersError) { // An error will be reported on the constructor if from source, or a use-site diagnostic will be reported on the use if from metadata. return; } - var requiredMembers = creation.Constructor.ContainingType.AllRequiredMembers.ToBuilder(); + var requiredMembers = constructor.ContainingType.AllRequiredMembers.ToBuilder(); if (requiredMembers.Count == 0) { return; } - if (creation.InitializerExpressionOpt == null) + if (initializers.IsDefaultOrEmpty) { reportMembers(); return; } - foreach (var initializer in creation.InitializerExpressionOpt.Initializers) + foreach (var initializer in initializers) { - if (initializer is not BoundAssignmentOperator { Left: BoundObjectInitializerMember member, Right: { } initializerExpression }) + 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 (memberSymbol is null) { continue; } - if (!requiredMembers.TryGetValue(member.MemberSymbol.Name, out var requiredMember)) + if (!requiredMembers.TryGetValue(memberSymbol.Name, out var requiredMember)) { continue; } - if (!member.MemberSymbol.Equals(requiredMember, TypeCompareKind.ConsiderEverything)) + if (!memberSymbol.Equals(requiredMember, TypeCompareKind.ConsiderEverything)) { continue; } - requiredMembers.Remove(member.MemberSymbol.Name); + requiredMembers.Remove(memberSymbol.Name); if (initializerExpression is BoundObjectInitializerExpressionBase) { + // 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); } } @@ -5024,20 +5041,22 @@ private static void CheckRequiredMembersInObjectInitializer( void reportMembers() { - Location location = creation.Syntax switch + Location location = creationSyntax switch { ObjectCreationExpressionSyntax { Type: { } type } => type.Location, BaseObjectCreationExpressionSyntax { NewKeyword: { } newKeyword } => newKeyword.GetLocation(), - _ => creation.Syntax.Location + AttributeSyntax { Name: { } name } => name.Location, + _ => creationSyntax.Location }; foreach (var (_, member) in requiredMembers) { - // Required member '{0}' must be set in the object initializer. + // Required member '{0}' must be set in the object initializer or attribute constructor. diagnostics.Add(ErrorCode.ERR_RequiredMemberMustBeSet, location, member); } } } +#nullable disable private BoundCollectionInitializerExpression BindCollectionInitializerExpression( InitializerExpressionSyntax initializerSyntax, @@ -5437,7 +5456,8 @@ protected BoundExpression BindClassCreationExpression( diagnostics, out MemberResolutionResult memberResolutionResult, out ImmutableArray candidateConstructors, - allowProtectedConstructorsOfBaseType: false) && + allowProtectedConstructorsOfBaseType: false, + suppressUnsupportedRequiredMembersError: false) && !type.IsAbstract) { var method = memberResolutionResult.Member; @@ -5499,7 +5519,7 @@ protected BoundExpression BindClassCreationExpression( type, hasError); - CheckRequiredMembersInObjectInitializer(creation, diagnostics); + CheckRequiredMembersInObjectInitializer(creation.Constructor, creation.InitializerExpressionOpt?.Initializers ?? default, creation.Syntax, diagnostics); return creation; } @@ -5773,7 +5793,8 @@ internal bool TryPerformConstructorOverloadResolution( BindingDiagnosticBag diagnostics, out MemberResolutionResult memberResolutionResult, out ImmutableArray candidateConstructors, - bool allowProtectedConstructorsOfBaseType) // Last to make named arguments more convenient. + bool allowProtectedConstructorsOfBaseType, + bool suppressUnsupportedRequiredMembersError) // Last to make named arguments more convenient. { // Get accessible constructors for performing overload resolution. ImmutableArray allInstanceConstructors; @@ -5821,7 +5842,23 @@ internal bool TryPerformConstructorOverloadResolution( } } - diagnostics.Add(errorLocation, useSiteInfo); + if (suppressUnsupportedRequiredMembersError && useSiteInfo.AccumulatesDiagnostics && useSiteInfo.Diagnostics is { Count: not 0 }) + { + diagnostics.AddDependencies(useSiteInfo); + foreach (var diagnostic in useSiteInfo.Diagnostics) + { + if ((ErrorCode)diagnostic.Code == ErrorCode.ERR_RequiredMembersInvalid) + { + continue; + } + + diagnostics.ReportUseSiteDiagnostic(diagnostic, errorLocation); + } + } + else + { + diagnostics.Add(errorLocation, useSiteInfo); + } if (succeededIgnoringAccessibility) { diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 5225f0c81ba09..37e085f41196f 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7008,7 +7008,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Required member '{0}' must be settable. - Required member '{0}' must be set in the object initializer. + 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. @@ -7017,7 +7017,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ The required members list for '{0}' is malformed and cannot be interpreted. - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. Line contains different whitespace than the closing line of the raw string literal: '{0}' versus '{1}' diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index c3c81cdbb0de2..a4f847759b390 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -2410,7 +2410,7 @@ private void CheckForRequiredMemberAttribute(BindingDiagnosticBag diagnostics) continue; } - // The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. diagnostics.Add(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, method.Locations[0], BaseTypeNoUseSiteDiagnostics); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs index b7a2bc2e045da..30000f258033d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs @@ -842,8 +842,8 @@ internal static bool HasAsyncMethodBuilderAttribute(this Symbol symbol, [NotNull internal static bool IsRequired(this Symbol symbol) => symbol is FieldSymbol { IsRequired: true } or PropertySymbol { IsRequired: true }; - internal static bool ShouldCheckRequiredMembers(this MethodSymbol constructor) + internal static bool ShouldCheckRequiredMembers(this MethodSymbol method) // PROTOTYPE(req): Check for the SetsRequiredMembersAttribute and return false for that case - => constructor.MethodKind == MethodKind.Constructor; + => method.MethodKind == MethodKind.Constructor; } } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index f876bc3fd56d3..aa0068593c057 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1163,8 +1163,8 @@ - Required member '{0}' must be set in the object initializer. - Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer or attribute constructor. + Required member '{0}' must be set in the object initializer or attribute constructor. @@ -1173,8 +1173,8 @@ - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. @@ -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 d4c1af70c7b78..ec14f18174b29 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1163,8 +1163,8 @@ - Required member '{0}' must be set in the object initializer. - Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer or attribute constructor. + Required member '{0}' must be set in the object initializer or attribute constructor. @@ -1173,8 +1173,8 @@ - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. @@ -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 d079780e28d7a..8c45c9dc73c0d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1163,8 +1163,8 @@ - Required member '{0}' must be set in the object initializer. - Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer or attribute constructor. + Required member '{0}' must be set in the object initializer or attribute constructor. @@ -1173,8 +1173,8 @@ - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. @@ -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 43aae3d20e616..1a610c39f7d04 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1163,8 +1163,8 @@ - Required member '{0}' must be set in the object initializer. - Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer or attribute constructor. + Required member '{0}' must be set in the object initializer or attribute constructor. @@ -1173,8 +1173,8 @@ - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. @@ -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 fcd06aadb6eed..54d0207742b82 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1163,8 +1163,8 @@ - Required member '{0}' must be set in the object initializer. - Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer or attribute constructor. + Required member '{0}' must be set in the object initializer or attribute constructor. @@ -1173,8 +1173,8 @@ - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. @@ -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 cd5ae6a1428fd..7c0848e4fcec9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1163,8 +1163,8 @@ - Required member '{0}' must be set in the object initializer. - Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer or attribute constructor. + Required member '{0}' must be set in the object initializer or attribute constructor. @@ -1173,8 +1173,8 @@ - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. @@ -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 2806f16450a1e..cda2f01c75728 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1163,8 +1163,8 @@ - Required member '{0}' must be set in the object initializer. - Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer or attribute constructor. + Required member '{0}' must be set in the object initializer or attribute constructor. @@ -1173,8 +1173,8 @@ - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. @@ -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 32e5f0bfb1dde..e3b4e9b813fe7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1163,8 +1163,8 @@ - Required member '{0}' must be set in the object initializer. - Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer or attribute constructor. + Required member '{0}' must be set in the object initializer or attribute constructor. @@ -1173,8 +1173,8 @@ - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. @@ -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 3acf3259284e7..1331b89b14e07 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1163,8 +1163,8 @@ - Required member '{0}' must be set in the object initializer. - Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer or attribute constructor. + Required member '{0}' must be set in the object initializer or attribute constructor. @@ -1173,8 +1173,8 @@ - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. @@ -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 af1fba364585a..e949f3e3b67e8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1163,8 +1163,8 @@ - Required member '{0}' must be set in the object initializer. - Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer or attribute constructor. + Required member '{0}' must be set in the object initializer or attribute constructor. @@ -1173,8 +1173,8 @@ - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. @@ -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 daecc4070a2fb..643f8416444d2 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1163,8 +1163,8 @@ - Required member '{0}' must be set in the object initializer. - Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer or attribute constructor. + Required member '{0}' must be set in the object initializer or attribute constructor. @@ -1173,8 +1173,8 @@ - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. @@ -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 8d22ee52e3c3b..c6383d341d063 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1163,8 +1163,8 @@ - Required member '{0}' must be set in the object initializer. - Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer or attribute constructor. + Required member '{0}' must be set in the object initializer or attribute constructor. @@ -1173,8 +1173,8 @@ - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. @@ -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 c8b83c4dd8b24..922c6b22e4800 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1163,8 +1163,8 @@ - Required member '{0}' must be set in the object initializer. - Required member '{0}' must be set in the object initializer. + Required member '{0}' must be set in the object initializer or attribute constructor. + Required member '{0}' must be set in the object initializer or attribute constructor. @@ -1173,8 +1173,8 @@ - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. - The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. + The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. @@ -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 cbf586bb858b9..aa9a583b62e61 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; @@ -1235,23 +1236,23 @@ public class C } "; - var creationWithoutAssignment = $@"C c = new{constructor}();"; - var comp = CreateCompilationWithRequiredMembers(new[] { c, creationWithoutAssignment }); + var creation = $@"C c = new{constructor}();"; + var comp = CreateCompilationWithRequiredMembers(new[] { c, creation }); var expectedDiagnostics = constructor == " C" ? new[] { - // (1,11): error CS9506: Required member 'C.Prop' must be set in the object initializer. + // (1,11): error CS9506: Required member 'C.Prop' must be set in the object initializer or attribute constructor. // C c = new C(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.Prop").WithLocation(1, 11), - // (1,11): error CS9506: Required member 'C.Field' must be set in the object initializer. + // (1,11): error CS9506: Required member 'C.Field' must be set in the object initializer or attribute constructor. // C c = new C(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.Field").WithLocation(1, 11) } : new[] { - // (1,7): error CS9506: Required member 'C.Prop' must be set in the object initializer. + // (1,7): error CS9506: Required member 'C.Prop' must be set in the object initializer or attribute constructor. // C c = new(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "new").WithArguments("C.Prop").WithLocation(1, 7), - // (1,7): error CS9506: Required member 'C.Field' must be set in the object initializer. + // (1,7): error CS9506: Required member 'C.Field' must be set in the object initializer or attribute constructor. // C c = new(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "new").WithArguments("C.Field").WithLocation(1, 7) }; @@ -1259,7 +1260,7 @@ public class C comp.VerifyDiagnostics(expectedDiagnostics); var cComp = CreateCompilationWithRequiredMembers(c); - comp = CreateCompilation(creationWithoutAssignment, references: new[] { useMetadataReference ? cComp.ToMetadataReference() : cComp.EmitToImageReference() }); + comp = CreateCompilation(creation, references: new[] { useMetadataReference ? cComp.ToMetadataReference() : cComp.EmitToImageReference() }); comp.VerifyDiagnostics(expectedDiagnostics); } @@ -1278,23 +1279,23 @@ public class C } "; - var creationWithoutAssignment = $@"C c = new{constructor}() {{ Prop2 = 1, Field2 = 1 }};"; - var comp = CreateCompilationWithRequiredMembers(new[] { c, creationWithoutAssignment }); + var creation = $@"C c = new{constructor}() {{ Prop2 = 1, Field2 = 1 }};"; + var comp = CreateCompilationWithRequiredMembers(new[] { c, creation }); var expectedDiagnostics = constructor == " C" ? new[] { - // (1,11): error CS9506: Required member 'C.Prop1' must be set in the object initializer. + // (1,11): error CS9506: Required member 'C.Prop1' must be set in the object initializer or attribute constructor. // C c = new C() { Prop2 = 1, Field2 = 1 }; Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.Prop1").WithLocation(1, 11), - // (1,11): error CS9506: Required member 'C.Field1' must be set in the object initializer. + // (1,11): error CS9506: Required member 'C.Field1' must be set in the object initializer or attribute constructor. // C c = new C() { Prop2 = 1, Field2 = 1 }; Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.Field1").WithLocation(1, 11) } : new[] { - // (1,7): error CS9506: Required member 'C.Prop1' must be set in the object initializer. + // (1,7): error CS9506: Required member 'C.Prop1' must be set in the object initializer or attribute constructor. // C c = new() { Prop2 = 1, Field2 = 1 }; Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "new").WithArguments("C.Prop1").WithLocation(1, 7), - // (1,7): error CS9506: Required member 'C.Field1' must be set in the object initializer. + // (1,7): error CS9506: Required member 'C.Field1' must be set in the object initializer or attribute constructor. // C c = new() { Prop2 = 1, Field2 = 1 }; Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "new").WithArguments("C.Field1").WithLocation(1, 7) }; @@ -1302,7 +1303,7 @@ public class C comp.VerifyDiagnostics(expectedDiagnostics); var cComp = CreateCompilationWithRequiredMembers(c); - comp = CreateCompilation(creationWithoutAssignment, references: new[] { useMetadataReference ? cComp.ToMetadataReference() : cComp.EmitToImageReference() }); + comp = CreateCompilation(creation, references: new[] { useMetadataReference ? cComp.ToMetadataReference() : cComp.EmitToImageReference() }); comp.VerifyDiagnostics(expectedDiagnostics); } @@ -1319,15 +1320,15 @@ public class C } "; - var creationWithoutAssignment = @" + var creation = @" C c = new" + constructor + @"() { Prop1 = 1, Field1 = 1 }; System.Console.WriteLine($""{c.Prop1}, {c.Field1}""); "; - var comp = CreateCompilationWithRequiredMembers(new[] { c, creationWithoutAssignment }); + var comp = CreateCompilationWithRequiredMembers(new[] { c, creation }); CompileAndVerify(comp, expectedOutput: "1, 1"); var cComp = CreateCompilationWithRequiredMembers(c); - comp = CreateCompilation(creationWithoutAssignment, references: new[] { useMetadataReference ? cComp.ToMetadataReference() : cComp.EmitToImageReference() }); + comp = CreateCompilation(creation, references: new[] { useMetadataReference ? cComp.ToMetadataReference() : cComp.EmitToImageReference() }); CompileAndVerify(comp, expectedOutput: "1, 1"); } @@ -1346,10 +1347,10 @@ public class C "; var comp = CreateCompilationWithRequiredMembers(c); comp.VerifyDiagnostics( - // (2,13): error CS9506: Required member 'C.Prop1' must be set in the object initializer. + // (2,13): error CS9506: Required member 'C.Prop1' must be set in the object initializer or attribute constructor. // var c = new C(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.Prop1").WithLocation(2, 13), - // (2,13): error CS9506: Required member 'C.Field1' must be set in the object initializer. + // (2,13): error CS9506: Required member 'C.Field1' must be set in the object initializer or attribute constructor. // var c = new C(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.Field1").WithLocation(2, 13), // (3,15): error CS0200: Property or indexer 'C.Prop1' cannot be assigned to -- it is read only @@ -1443,16 +1444,16 @@ public class Derived : Base var comp = CreateCompilationWithRequiredMembers(new[] { @base, code }); var expectedDiagnostics = new[] { - // (2,9): error CS9506: Required member 'Base.Prop1' must be set in the object initializer. + // (2,9): error CS9506: Required member 'Base.Prop1' must be set in the object initializer or attribute constructor. // _ = new Derived(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Base.Prop1").WithLocation(2, 9), - // (2,9): error CS9506: Required member 'Base.Field1' must be set in the object initializer. + // (2,9): error CS9506: Required member 'Base.Field1' must be set in the object initializer or attribute constructor. // _ = new Derived(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Base.Field1").WithLocation(2, 9), - // (2,9): error CS9506: Required member 'Derived.Prop2' must be set in the object initializer. + // (2,9): error CS9506: Required member 'Derived.Prop2' must be set in the object initializer or attribute constructor. // _ = new Derived(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Prop2").WithLocation(2, 9), - // (2,9): error CS9506: Required member 'Derived.Field2' must be set in the object initializer. + // (2,9): error CS9506: Required member 'Derived.Field2' must be set in the object initializer or attribute constructor. // _ = new Derived(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Field2").WithLocation(2, 9) }; @@ -1495,16 +1496,16 @@ public class Derived : Base var comp = CreateCompilationWithRequiredMembers(new[] { @base, code }); var expectedDiagnostics = new[] { - // (2,9): error CS9506: Required member 'Base.Prop2' must be set in the object initializer. + // (2,9): error CS9506: Required member 'Base.Prop2' must be set in the object initializer or attribute constructor. // _ = new Derived() { Prop1 = 1, Field1 = 1, Prop3 = 3, Field3 = 3 }; Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Base.Prop2").WithLocation(2, 9), - // (2,9): error CS9506: Required member 'Base.Field2' must be set in the object initializer. + // (2,9): error CS9506: Required member 'Base.Field2' must be set in the object initializer or attribute constructor. // _ = new Derived() { Prop1 = 1, Field1 = 1, Prop3 = 3, Field3 = 3 }; Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Base.Field2").WithLocation(2, 9), - // (2,9): error CS9506: Required member 'Derived.Prop4' must be set in the object initializer. + // (2,9): error CS9506: Required member 'Derived.Prop4' must be set in the object initializer or attribute constructor. // _ = new Derived() { Prop1 = 1, Field1 = 1, Prop3 = 3, Field3 = 3 }; Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Prop4").WithLocation(2, 9), - // (2,9): error CS9506: Required member 'Derived.Field4' must be set in the object initializer. + // (2,9): error CS9506: Required member 'Derived.Field4' must be set in the object initializer or attribute constructor. // _ = new Derived() { Prop1 = 1, Field1 = 1, Prop3 = 3, Field3 = 3 }; Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Field4").WithLocation(2, 9) }; @@ -1581,16 +1582,16 @@ public class Derived : Base var comp = CreateCompilation(code, new[] { baseComp.ToMetadataReference(), retargetedC.ToMetadataReference() }, targetFramework: TargetFramework.Standard); comp.VerifyDiagnostics( - // (2,9): error CS9506: Required member 'Base.Field1' must be set in the object initializer. + // (2,9): error CS9506: Required member 'Base.Field1' must be set in the object initializer or attribute constructor. // _ = new Derived(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Base.Field1").WithLocation(2, 9), - // (2,9): error CS9506: Required member 'Base.Prop1' must be set in the object initializer. + // (2,9): error CS9506: Required member 'Base.Prop1' must be set in the object initializer or attribute constructor. // _ = new Derived(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Base.Prop1").WithLocation(2, 9), - // (2,9): error CS9506: Required member 'Derived.Prop2' must be set in the object initializer. + // (2,9): error CS9506: Required member 'Derived.Prop2' must be set in the object initializer or attribute constructor. // _ = new Derived(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Prop2").WithLocation(2, 9), - // (2,9): error CS9506: Required member 'Derived.Field2' must be set in the object initializer. + // (2,9): error CS9506: Required member 'Derived.Field2' must be set in the object initializer or attribute constructor. // _ = new Derived(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Field2").WithLocation(2, 9) ); @@ -1658,7 +1659,7 @@ public class Derived : Base var comp = CreateCompilationWithRequiredMembers(new[] { @base, code }); var expectedDiagnostics = new[] { - // (2,9): error CS9506: Required member 'Derived.Prop1' must be set in the object initializer. + // (2,9): error CS9506: Required member 'Derived.Prop1' must be set in the object initializer or attribute constructor. // _ = new Derived(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Prop1").WithLocation(2, 9) }; @@ -1697,7 +1698,7 @@ public class Derived : Base var comp = CreateCompilationWithRequiredMembers(new[] { @base, code }); var expectedDiagnostics = new[] { - // (2,9): error CS9506: Required member 'Derived.Prop1' must be set in the object initializer. + // (2,9): error CS9506: Required member 'Derived.Prop1' must be set in the object initializer or attribute constructor. // _ = new Derived() { Prop2 = 1 }; Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Prop1").WithLocation(2, 9) }; @@ -1801,7 +1802,7 @@ public class Derived : Base var comp = CreateCompilation(code, new[] { baseComp.ToMetadataReference(), retargetedC.ToMetadataReference() }); comp.VerifyDiagnostics( - // (2,9): error CS9506: Required member 'Derived.Prop1' must be set in the object initializer. + // (2,9): error CS9506: Required member 'Derived.Prop1' must be set in the object initializer or attribute constructor. // _ = new Derived(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Derived").WithArguments("Derived.Prop1").WithLocation(2, 9) ); @@ -1838,23 +1839,21 @@ End Class class Derived2 : Derived { public Derived2() {} + public Derived2(int x) {} } class Derived3 : Derived { }"; var comp = CreateCompilation(c, new[] { vbComp.EmitToImageReference() }); comp.VerifyDiagnostics( - // (7,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (7,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // public Derived2() {} Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived2").WithArguments("Derived").WithLocation(7, 12), - // (7,12): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. - // public Derived2() {} - Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "Derived2").WithArguments("Derived").WithLocation(7, 12), - // (9,7): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. - // class Derived3 : Derived { } - Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived3").WithArguments("Derived").WithLocation(9, 7), - // (9,7): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. + // (8,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. + // public Derived2(int x) {} + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived2").WithArguments("Derived").WithLocation(8, 12), + // (10,7): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // class Derived3 : Derived { } - Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "Derived3").WithArguments("Derived").WithLocation(9, 7) + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived3").WithArguments("Derived").WithLocation(10, 7) ); } @@ -1891,18 +1890,12 @@ class Derived3 : Derived { }"; var comp = CreateCompilation(c, new[] { vbComp.EmitToImageReference() }); comp.VerifyDiagnostics( - // (7,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (7,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // public Derived2() {} Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived2").WithArguments("Derived").WithLocation(7, 12), - // (7,12): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. - // public Derived2() {} - Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "Derived2").WithArguments("Derived").WithLocation(7, 12), - // (9,7): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (9,7): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // class Derived3 : Derived { } - Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived3").WithArguments("Derived").WithLocation(9, 7), - // (9,7): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. - // class Derived3 : Derived { } - Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "Derived3").WithArguments("Derived").WithLocation(9, 7) + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived3").WithArguments("Derived").WithLocation(9, 7) ); } @@ -1938,18 +1931,12 @@ class Derived3 : Derived { }"; var comp = CreateCompilation(c, new[] { vbComp.EmitToImageReference() }); comp.VerifyDiagnostics( - // (7,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (7,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // public Derived2() {} Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived2").WithArguments("Derived").WithLocation(7, 12), - // (7,12): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. - // public Derived2() {} - Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "Derived2").WithArguments("Derived").WithLocation(7, 12), - // (9,7): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (9,7): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // class Derived3 : Derived { } - Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived3").WithArguments("Derived").WithLocation(9, 7), - // (9,7): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. - // class Derived3 : Derived { } - Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "Derived3").WithArguments("Derived").WithLocation(9, 7) + Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "Derived3").WithArguments("Derived").WithLocation(9, 7) ); } @@ -1964,7 +1951,7 @@ public record Base "; var originalComp = CreateCompilationWithRequiredMembers(new[] { original, IsExternalInitTypeDefinition }, assemblyName: "original"); - CompileAndVerify(originalComp).VerifyDiagnostics(); + CompileAndVerify(originalComp, verify: ExecutionConditionUtil.IsCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(); // This IL is the equivalent of: // public record Derived : Base @@ -2157,41 +2144,32 @@ record DerivedDerived3() : Derived; var comp = CreateCompilation(c, new[] { il, originalComp.EmitToImageReference() }); // PROTOTYPE(req): do we want to take the effort to remove some of these duplicate errors? comp.VerifyDiagnostics( - // (6,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (6,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // record DerivedDerived1 : Derived Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(6, 8), // (6,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. // record DerivedDerived1 : Derived Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(6, 8), - // (8,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (8,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // public DerivedDerived1() Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(8, 12), - // (8,12): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. - // public DerivedDerived1() - Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(8, 12), - // (12,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (12,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // record DerivedDerived2 : Derived Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), - // (12,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (12,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // record DerivedDerived2 : Derived Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), // (12,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. // record DerivedDerived2 : Derived Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), - // (12,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. - // record DerivedDerived2 : Derived - Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), - // (15,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (15,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // record DerivedDerived3() : Derived; Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8), - // (15,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (15,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // record DerivedDerived3() : Derived; Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8), // (15,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. // record DerivedDerived3() : Derived; - Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8), - // (15,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. - // record DerivedDerived3() : Derived; Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8) ); } @@ -2207,7 +2185,7 @@ public record Base "; var originalComp = CreateCompilationWithRequiredMembers(new[] { original, IsExternalInitTypeDefinition }, assemblyName: "original"); - CompileAndVerify(originalComp).VerifyDiagnostics(); + CompileAndVerify(originalComp, verify: ExecutionConditionUtil.IsCoreClr ? Verification.Passes : Verification.Skipped).VerifyDiagnostics(); // This IL is the equivalent of: // public record Derived : Base @@ -2403,41 +2381,32 @@ record DerivedDerived3() : Derived; var comp = CreateCompilation(c, new[] { il, originalComp.EmitToImageReference() }); // PROTOTYPE(req): do we want to take the effort to remove some of these duplicate errors? comp.VerifyDiagnostics( - // (6,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (6,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // record DerivedDerived1 : Derived Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(6, 8), // (6,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. // record DerivedDerived1 : Derived Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(6, 8), - // (8,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (8,12): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // public DerivedDerived1() Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(8, 12), - // (8,12): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. - // public DerivedDerived1() - Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived1").WithArguments("Derived").WithLocation(8, 12), - // (12,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (12,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // record DerivedDerived2 : Derived Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), - // (12,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (12,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // record DerivedDerived2 : Derived Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), // (12,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. // record DerivedDerived2 : Derived Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), - // (12,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. - // record DerivedDerived2 : Derived - Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived2").WithArguments("Derived").WithLocation(12, 8), - // (15,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (15,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // record DerivedDerived3() : Derived; Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8), - // (15,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute. + // (15,8): error CS9509: The required members list for the base type 'Derived' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute. // record DerivedDerived3() : Derived; Diagnostic(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8), // (15,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. // record DerivedDerived3() : Derived; - Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8), - // (15,8): error CS9508: The required members list for 'Derived' is malformed and cannot be interpreted. - // record DerivedDerived3() : Derived; Diagnostic(ErrorCode.ERR_RequiredMembersInvalid, "DerivedDerived3").WithArguments("Derived").WithLocation(15, 8) ); } @@ -2547,7 +2516,7 @@ partial class CustomHandler var comp = CreateCompilationWithRequiredMembers(new[] { code, handler }); comp.VerifyDiagnostics( - // (2,25): error CS9506: Required member 'CustomHandler.Field' must be set in the object initializer. + // (2,25): error CS9506: Required member 'CustomHandler.Field' must be set in the object initializer or attribute constructor. // CustomHandler handler = $""; Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, @"$""""").WithArguments("CustomHandler.Field").WithLocation(2, 25) ); @@ -2576,7 +2545,7 @@ class C : I "; var comp = CreateCompilationWithRequiredMembers(code); comp.VerifyDiagnostics( - // (5,9): error CS9506: Required member 'C.P' must be set in the object initializer. + // (5,9): error CS9506: Required member 'C.P' must be set in the object initializer or attribute constructor. // _ = new I(); Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "I").WithArguments("C.P").WithLocation(5, 9) ); @@ -2604,6 +2573,205 @@ class C : I } "; var comp = CreateCompilationWithRequiredMembers(code); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (5,9): error CS9506: Required member 'C.P' must be set in the object initializer or attribute constructor. + // _ = new I() { P = 1 }; + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "I").WithArguments("C.P").WithLocation(5, 9) + ); + } + + [Fact] + public void RequiredMemberInAttribute_NotSet() + { + var code = @" +using System; + +[Attr] +class C +{ +} + +class AttrAttribute : Attribute +{ + public required int P { get; set; } +} +"; + + var comp = CreateCompilationWithRequiredMembers(code); + comp.VerifyDiagnostics( + // (4,2): error CS9506: Required member 'AttrAttribute.P' must be set in the object initializer or attribute constructor. + // [Attr] + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "Attr").WithArguments("AttrAttribute.P").WithLocation(4, 2) + ); + } + + [Fact] + public void RequiredMemberInAttribute_AllSet() + { + var code = @" +using System; + +[Attr(P = 1, F = 2)] +class C +{ +} + +class AttrAttribute : Attribute +{ + public required int P { get; set; } + public required int F; +} +"; + + var comp = CreateCompilationWithRequiredMembers(code); + var verifier = CompileAndVerify(comp, symbolValidator: verify, sourceSymbolValidator: verify); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol module) + { + var c = module.GlobalNamespace.GetMember("C"); + var attr = c.GetAttributes().Single(); + AssertEx.Equal("AttrAttribute", attr.AttributeClass.ToTestDisplayString()); + Assert.Equal(2, attr.CommonNamedArguments.Length); + Assert.Equal(1, (int)attr.CommonNamedArguments[0].Value.ValueInternal!); + Assert.Equal(2, (int)attr.CommonNamedArguments[1].Value.ValueInternal!); + } + } + + [Fact] + public void RequiredMemberInAttribute_Recursive01() + { + var code = @" +using System; + +[Attr(P = 1, F = 2)] +class AttrAttribute : Attribute +{ + public required int P { get; set; } + public required int F; +} +"; + + var comp = CreateCompilationWithRequiredMembers(code); + var verifier = CompileAndVerify(comp, symbolValidator: verify, sourceSymbolValidator: verify); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol module) + { + var attrAttribute = module.GlobalNamespace.GetMember("AttrAttribute"); + var attr = attrAttribute.GetAttributes().Where(a => a.AttributeClass!.Name == "AttrAttribute").Single(); + AssertEx.Equal("AttrAttribute", attr.AttributeClass.ToTestDisplayString()); + Assert.Equal(2, attr.CommonNamedArguments.Length); + Assert.Equal(1, (int)attr.CommonNamedArguments[0].Value.ValueInternal!); + Assert.Equal(2, (int)attr.CommonNamedArguments[1].Value.ValueInternal!); + } + } + + [Fact] + public void RequiredMemberInAttribute_Recursive02() + { + var code = @" +using System; + +class AttrAttribute : Attribute +{ + [Attr(P = 1, F = 2)] + public required int P { get; set; } + public required int F; +} +"; + + var comp = CreateCompilationWithRequiredMembers(code); + var verifier = CompileAndVerify(comp, symbolValidator: verify, sourceSymbolValidator: verify); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol module) + { + var attrAttribute = module.GlobalNamespace.GetMember("AttrAttribute"); + var p = attrAttribute.GetMember("P"); + var attr = p.GetAttributes().Where(a => a.AttributeClass!.Name == "AttrAttribute").Single(); + AssertEx.Equal("AttrAttribute", attr.AttributeClass.ToTestDisplayString()); + Assert.Equal(2, attr.CommonNamedArguments.Length); + Assert.Equal(1, (int)attr.CommonNamedArguments[0].Value.ValueInternal!); + Assert.Equal(2, (int)attr.CommonNamedArguments[1].Value.ValueInternal!); + } + } + + [Fact] + public void RequiredMemberInAttribute_Recursive03() + { + var code = @" +using System; + +class AttrAttribute : Attribute +{ + public required int P { get; set; } + [Attr(P = 1, F = 2)] + public required int F; +} +"; + + var comp = CreateCompilationWithRequiredMembers(code); + var verifier = CompileAndVerify(comp, symbolValidator: verify, sourceSymbolValidator: verify); + verifier.VerifyDiagnostics(); + + static void verify(ModuleSymbol module) + { + var attrAttribute = module.GlobalNamespace.GetMember("AttrAttribute"); + var f = attrAttribute.GetMember("F"); + var attr = f.GetAttributes().Where(a => a.AttributeClass!.Name == "AttrAttribute").Single(); + AssertEx.Equal("AttrAttribute", attr.AttributeClass.ToTestDisplayString()); + Assert.Equal(2, attr.CommonNamedArguments.Length); + Assert.Equal(1, (int)attr.CommonNamedArguments[0].Value.ValueInternal!); + Assert.Equal(2, (int)attr.CommonNamedArguments[1].Value.ValueInternal!); + } + } + + [Fact] + public void RequiredMemberInAttribute_Recursive04() + { + var code = @" +namespace System.Runtime.CompilerServices; + +[RequiredMember] +public class RequiredMemberAttribute : Attribute +{ + public required int P { get; set; } +} +"; + + var comp = CreateCompilation(code); + comp.VerifyDiagnostics( + // (4,2): error CS9506: Required member 'RequiredMemberAttribute.P' must be set in the object initializer or attribute constructor. + // [RequiredMember] + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "RequiredMember").WithArguments("System.Runtime.CompilerServices.RequiredMemberAttribute.P").WithLocation(4, 2), + // (4,2): error CS9504: Do not use 'System.Runtime.CompilerServices.RequiredMemberAttribute'. Use the 'required' keyword on required fields and properties instead. + // [RequiredMember] + Diagnostic(ErrorCode.ERR_ExplicitRequiredMember, "RequiredMember").WithLocation(4, 2) + ); + } + + [Fact] + public void RequiredMemberInAttribute_Recursive05() + { + var code = @" +namespace System.Runtime.CompilerServices; + +public class RequiredMemberAttribute : Attribute +{ + [RequiredMember] + public required int P { get; set; } +} +"; + + var comp = CreateCompilation(code); + comp.VerifyDiagnostics( + // (6,6): error CS9506: Required member 'RequiredMemberAttribute.P' must be set in the object initializer or attribute constructor. + // [RequiredMember] + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "RequiredMember").WithArguments("System.Runtime.CompilerServices.RequiredMemberAttribute.P").WithLocation(6, 6), + // (6,6): error CS9504: Do not use 'System.Runtime.CompilerServices.RequiredMemberAttribute'. Use the 'required' keyword on required fields and properties instead. + // [RequiredMember] + Diagnostic(ErrorCode.ERR_ExplicitRequiredMember, "RequiredMember").WithLocation(6, 6) + ); } } From 2ca360a115be034194ee82dc1daaaade81613417 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Tue, 15 Mar 2022 12:25:28 -0700 Subject: [PATCH 3/7] Address PR feedback. --- .../Portable/Binder/Binder_Expressions.cs | 42 ++++++++++------- .../CSharp/Portable/CSharpResources.resx | 2 +- .../CSharp/Portable/Errors/ErrorCode.cs | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 4 +- .../Portable/xlf/CSharpResources.de.xlf | 4 +- .../Portable/xlf/CSharpResources.es.xlf | 4 +- .../Portable/xlf/CSharpResources.fr.xlf | 4 +- .../Portable/xlf/CSharpResources.it.xlf | 4 +- .../Portable/xlf/CSharpResources.ja.xlf | 4 +- .../Portable/xlf/CSharpResources.ko.xlf | 4 +- .../Portable/xlf/CSharpResources.pl.xlf | 4 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 4 +- .../Portable/xlf/CSharpResources.ru.xlf | 4 +- .../Portable/xlf/CSharpResources.tr.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 4 +- .../Symbol/Symbols/RequiredMembersTests.cs | 45 +++++++++++++++++-- 17 files changed, 94 insertions(+), 49 deletions(-) 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) From 31157e3121ff0b6178382317f8943a9c061e0fc8 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Wed, 16 Mar 2022 16:21:09 -0700 Subject: [PATCH 4/7] Add ability for ImmutableSegmentedDictionary to have sentinel values. Currently, ImmutableSegmentedDictionary cannot be used as a Sentinel value, because there is no way to make a sentinel that is different from Empty or default. This adds the ability to make a new empty dictionary, as well as a way to compare two ImmutableSegmentedDictionaries for reference equality. --- .../Collections/ImmutableSegmentedDictionary.cs | 4 ++++ .../Collections/ImmutableSegmentedDictionary`2.cs | 13 ++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Dependencies/Collections/ImmutableSegmentedDictionary.cs b/src/Dependencies/Collections/ImmutableSegmentedDictionary.cs index 4440fab2d7726..642d4967df0a9 100644 --- a/src/Dependencies/Collections/ImmutableSegmentedDictionary.cs +++ b/src/Dependencies/Collections/ImmutableSegmentedDictionary.cs @@ -22,6 +22,10 @@ public static ImmutableSegmentedDictionary Create(IE where TKey : notnull => ImmutableSegmentedDictionary.Empty.WithComparer(keyComparer); + public static bool ReferenceEquals(ImmutableSegmentedDictionary left, ImmutableSegmentedDictionary right) + where TKey : notnull + => ImmutableSegmentedDictionary.ReferenceEquals(left, right); + public static ImmutableSegmentedDictionary.Builder CreateBuilder() where TKey : notnull => Create().ToBuilder(); diff --git a/src/Dependencies/Collections/ImmutableSegmentedDictionary`2.cs b/src/Dependencies/Collections/ImmutableSegmentedDictionary`2.cs index 99d8a6ce511d7..a4d3bbe0c2fab 100644 --- a/src/Dependencies/Collections/ImmutableSegmentedDictionary`2.cs +++ b/src/Dependencies/Collections/ImmutableSegmentedDictionary`2.cs @@ -70,7 +70,18 @@ namespace Microsoft.CodeAnalysis.Collections internal readonly partial struct ImmutableSegmentedDictionary : IImmutableDictionary, IDictionary, IReadOnlyDictionary, IDictionary, IEquatable> where TKey : notnull { - public static readonly ImmutableSegmentedDictionary Empty = new(new SegmentedDictionary()); + public static readonly ImmutableSegmentedDictionary Empty = CreateEmpty(); + + ///

+ /// Creates a new empty segmented dictionary, that is not underlying-reference equal to . The intent is for this to + /// be used for sentinel values only. + /// + internal static ImmutableSegmentedDictionary CreateEmpty() => new(new SegmentedDictionary()); + + /// + /// Compares the given segmented dictionaries for reference-equality of their underlying storage. + /// + internal static bool ReferenceEquals(ImmutableSegmentedDictionary left, ImmutableSegmentedDictionary right) => ReferenceEquals(left._dictionary, right._dictionary); private readonly SegmentedDictionary _dictionary; From aa419fbb94619fd64fcd6bb68b7ee354f73c9f7c Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Wed, 16 Mar 2022 16:21:20 -0700 Subject: [PATCH 5/7] PR Feedback. --- .../Portable/Symbols/NamedTypeSymbol.cs | 65 ++++++++++--------- .../Symbol/Symbols/RequiredMembersTests.cs | 39 +++++++++++ 2 files changed, 72 insertions(+), 32 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs index 5c525758f8be4..8c19b4356ea8b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs @@ -26,13 +26,13 @@ internal abstract partial class NamedTypeSymbol : TypeSymbol, INamedTypeSymbolIn { private bool _hasNoBaseCycles; - /// - /// This field cannot be used to determine whether has been initialized. - /// Ensure that is checked for defaultness _before_ reading this field, which potentially - /// means using an `Interlocked.MemoryBarrier()` to ensure that reading this location is not reordered before the read to . - /// - private bool _lazyHasRequiredMembersErrorDoNotAccessDirectly = false; - private ImmutableSegmentedDictionary _lazyRequiredMembers; + private static ImmutableSegmentedDictionary RequiredMembersSentinel = ImmutableSegmentedDictionary.CreateEmpty(); + + /// + /// if uninitialized. if there are errors. if + /// there are no required members. Otherwise, the required members. + /// + private ImmutableSegmentedDictionary _lazyRequiredMembers = RequiredMembersSentinel; // Only the compiler can create NamedTypeSymbols. internal NamedTypeSymbol(TupleExtraData tupleData = null) @@ -518,8 +518,7 @@ internal bool HasRequiredMembersError get { CalculateRequiredMembersIfRequired(); - Debug.Assert(!_lazyRequiredMembers.IsDefault); - return _lazyHasRequiredMembersErrorDoNotAccessDirectly; + return _lazyRequiredMembers.IsDefault; } } @@ -536,32 +535,29 @@ internal ImmutableSegmentedDictionary AllRequiredMembers get { CalculateRequiredMembersIfRequired(); - Debug.Assert(!_lazyRequiredMembers.IsDefault); + if (_lazyRequiredMembers.IsDefault) + { + return ImmutableSegmentedDictionary.Empty; + } + return _lazyRequiredMembers; } } private void CalculateRequiredMembersIfRequired() { - if (_lazyRequiredMembers.IsDefault) - { - ImmutableSegmentedDictionary.Builder? builder = null; - bool success = TryCalculateRequiredMembers(ref builder); - var requiredMembers = success - ? builder?.ToImmutable() ?? ImmutableSegmentedDictionary.Empty - : ImmutableSegmentedDictionary.Empty; - - _lazyHasRequiredMembersErrorDoNotAccessDirectly = !success; - // InterlockedInitialize uses `Interlocked.CompareExchange` under the hood, which will introduce a full - // memory barrier, ensuring that the _lazyHasRequiredMembersErrorDoNotAccessDirectly write is not reordered - // after the initialization of _lazyRequiredMembers. - RoslynImmutableInterlocked.InterlockedInitialize(ref _lazyRequiredMembers, requiredMembers); - } - else + if (!ImmutableSegmentedDictionary.ReferenceEquals(RequiredMembersSentinel, _lazyRequiredMembers)) { - // Ensure that, after this method, _lazyRequiredMembersErrorDoNotAccessDirectly is able to be accessed safely. - Interlocked.MemoryBarrier(); + return; } + ImmutableSegmentedDictionary.Builder? builder = null; + bool success = TryCalculateRequiredMembers(ref builder); + + var requiredMembers = success + ? builder?.ToImmutable() ?? ImmutableSegmentedDictionary.Empty + : default(ImmutableSegmentedDictionary); + + RoslynImmutableInterlocked.InterlockedCompareExchange(ref _lazyRequiredMembers, requiredMembers, RequiredMembersSentinel); } /// @@ -570,12 +566,17 @@ private void CalculateRequiredMembersIfRequired() private bool TryCalculateRequiredMembers(ref ImmutableSegmentedDictionary.Builder? requiredMembersBuilder) { var lazyRequiredMembers = _lazyRequiredMembers; - if (!lazyRequiredMembers.IsDefault) + if (!ImmutableSegmentedDictionary.ReferenceEquals(RequiredMembersSentinel, lazyRequiredMembers)) { - requiredMembersBuilder = lazyRequiredMembers.ToBuilder(); - // Ensure that this read is not reordered before the read to `IsDefault`. - Interlocked.MemoryBarrier(); - return !_lazyHasRequiredMembersErrorDoNotAccessDirectly; + if (lazyRequiredMembers.IsDefault) + { + return false; + } + else + { + requiredMembersBuilder = lazyRequiredMembers.ToBuilder(); + return true; + } } if (BaseTypeNoUseSiteDiagnostics?.TryCalculateRequiredMembers(ref requiredMembersBuilder) == false) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs index 9a9780068ef6d..a5788ad6a0aec 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs @@ -1368,6 +1368,45 @@ public class C ); } + [Fact] + public void EnforcedRequiredMembers_NoInheritance_Unsettable_FromMetadata() + { + var vb = @" +Imports System.Runtime.CompilerServices + + +Public Class C + + Public Readonly Property Prop1 As Integer + + Public Readonly Field1 As Integer +End Class +"; + + var vbComp = CreateVisualBasicCompilationWithRequiredMembers(vb); + vbComp.VerifyEmitDiagnostics(); + + var c = @" +var c = new C(); +c = new C() { Prop1 = 1, Field1 = 1 }; +"; + var comp = CreateCompilation(new[] { c }, references: new[] { vbComp.EmitToImageReference() }); + comp.VerifyDiagnostics( + // (2,13): error CS9506: Required member 'C.Prop1' must be set in the object initializer or attribute constructor. + // var c = new C(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.Prop1").WithLocation(2, 13), + // (2,13): error CS9506: Required member 'C.Field1' must be set in the object initializer or attribute constructor. + // var c = new C(); + Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C").WithArguments("C.Field1").WithLocation(2, 13), + // (3,15): error CS0200: Property or indexer 'C.Prop1' cannot be assigned to -- it is read only + // c = new C() { Prop1 = 1, Field1 = 1 }; + Diagnostic(ErrorCode.ERR_AssgReadonlyProp, "Prop1").WithArguments("C.Prop1").WithLocation(3, 15), + // (3,26): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer) + // c = new C() { Prop1 = 1, Field1 = 1 }; + Diagnostic(ErrorCode.ERR_AssgReadonly, "Field1").WithLocation(3, 26) + ); + } + [Fact] public void EnforcedRequiredMembers_NoInheritance_DisallowedNestedObjectInitializer() { From c6dedb0ece6f7d6138b5fd8e957596b98fbc53da Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 17 Mar 2022 11:11:35 -0700 Subject: [PATCH 6/7] Revert changes to ImmutableSegmentedDictionary and use an error sentinel. --- .../Portable/Symbols/NamedTypeSymbol.cs | 21 +++++++++++-------- .../ImmutableSegmentedDictionary.cs | 4 ---- .../ImmutableSegmentedDictionary`2.cs | 13 +----------- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs index 8c19b4356ea8b..786fc3d303172 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs @@ -26,13 +26,13 @@ internal abstract partial class NamedTypeSymbol : TypeSymbol, INamedTypeSymbolIn { private bool _hasNoBaseCycles; - private static ImmutableSegmentedDictionary RequiredMembersSentinel = ImmutableSegmentedDictionary.CreateEmpty(); + private static readonly ImmutableSegmentedDictionary RequiredMembersErrorSentinel = ImmutableSegmentedDictionary.Empty.Add("", null!); /// - /// if uninitialized. if there are errors. if + /// if uninitialized. if there are errors. if /// there are no required members. Otherwise, the required members. /// - private ImmutableSegmentedDictionary _lazyRequiredMembers = RequiredMembersSentinel; + private ImmutableSegmentedDictionary _lazyRequiredMembers = default; // Only the compiler can create NamedTypeSymbols. internal NamedTypeSymbol(TupleExtraData tupleData = null) @@ -518,7 +518,8 @@ internal bool HasRequiredMembersError get { CalculateRequiredMembersIfRequired(); - return _lazyRequiredMembers.IsDefault; + Debug.Assert(!_lazyRequiredMembers.IsDefault); + return _lazyRequiredMembers == RequiredMembersErrorSentinel; } } @@ -535,7 +536,8 @@ internal ImmutableSegmentedDictionary AllRequiredMembers get { CalculateRequiredMembersIfRequired(); - if (_lazyRequiredMembers.IsDefault) + Debug.Assert(!_lazyRequiredMembers.IsDefault); + if (_lazyRequiredMembers == RequiredMembersErrorSentinel) { return ImmutableSegmentedDictionary.Empty; } @@ -546,18 +548,19 @@ internal ImmutableSegmentedDictionary AllRequiredMembers private void CalculateRequiredMembersIfRequired() { - if (!ImmutableSegmentedDictionary.ReferenceEquals(RequiredMembersSentinel, _lazyRequiredMembers)) + if (!_lazyRequiredMembers.IsDefault) { return; } + ImmutableSegmentedDictionary.Builder? builder = null; bool success = TryCalculateRequiredMembers(ref builder); var requiredMembers = success ? builder?.ToImmutable() ?? ImmutableSegmentedDictionary.Empty - : default(ImmutableSegmentedDictionary); + : RequiredMembersErrorSentinel; - RoslynImmutableInterlocked.InterlockedCompareExchange(ref _lazyRequiredMembers, requiredMembers, RequiredMembersSentinel); + RoslynImmutableInterlocked.InterlockedInitialize(ref _lazyRequiredMembers, requiredMembers); } /// @@ -566,7 +569,7 @@ private void CalculateRequiredMembersIfRequired() private bool TryCalculateRequiredMembers(ref ImmutableSegmentedDictionary.Builder? requiredMembersBuilder) { var lazyRequiredMembers = _lazyRequiredMembers; - if (!ImmutableSegmentedDictionary.ReferenceEquals(RequiredMembersSentinel, lazyRequiredMembers)) + if (_lazyRequiredMembers == RequiredMembersErrorSentinel) { if (lazyRequiredMembers.IsDefault) { diff --git a/src/Dependencies/Collections/ImmutableSegmentedDictionary.cs b/src/Dependencies/Collections/ImmutableSegmentedDictionary.cs index 642d4967df0a9..4440fab2d7726 100644 --- a/src/Dependencies/Collections/ImmutableSegmentedDictionary.cs +++ b/src/Dependencies/Collections/ImmutableSegmentedDictionary.cs @@ -22,10 +22,6 @@ public static ImmutableSegmentedDictionary Create(IE where TKey : notnull => ImmutableSegmentedDictionary.Empty.WithComparer(keyComparer); - public static bool ReferenceEquals(ImmutableSegmentedDictionary left, ImmutableSegmentedDictionary right) - where TKey : notnull - => ImmutableSegmentedDictionary.ReferenceEquals(left, right); - public static ImmutableSegmentedDictionary.Builder CreateBuilder() where TKey : notnull => Create().ToBuilder(); diff --git a/src/Dependencies/Collections/ImmutableSegmentedDictionary`2.cs b/src/Dependencies/Collections/ImmutableSegmentedDictionary`2.cs index a4d3bbe0c2fab..99d8a6ce511d7 100644 --- a/src/Dependencies/Collections/ImmutableSegmentedDictionary`2.cs +++ b/src/Dependencies/Collections/ImmutableSegmentedDictionary`2.cs @@ -70,18 +70,7 @@ namespace Microsoft.CodeAnalysis.Collections internal readonly partial struct ImmutableSegmentedDictionary : IImmutableDictionary, IDictionary, IReadOnlyDictionary, IDictionary, IEquatable> where TKey : notnull { - public static readonly ImmutableSegmentedDictionary Empty = CreateEmpty(); - - /// - /// Creates a new empty segmented dictionary, that is not underlying-reference equal to . The intent is for this to - /// be used for sentinel values only. - /// - internal static ImmutableSegmentedDictionary CreateEmpty() => new(new SegmentedDictionary()); - - /// - /// Compares the given segmented dictionaries for reference-equality of their underlying storage. - /// - internal static bool ReferenceEquals(ImmutableSegmentedDictionary left, ImmutableSegmentedDictionary right) => ReferenceEquals(left._dictionary, right._dictionary); + public static readonly ImmutableSegmentedDictionary Empty = new(new SegmentedDictionary()); private readonly SegmentedDictionary _dictionary; From cad6ead313c55f3edddf613c9d3bbc14fdea27b0 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Thu, 17 Mar 2022 13:09:31 -0700 Subject: [PATCH 7/7] Fix formatting --- src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs index 786fc3d303172..887e9ec4127f4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs @@ -29,7 +29,7 @@ internal abstract partial class NamedTypeSymbol : TypeSymbol, INamedTypeSymbolIn private static readonly ImmutableSegmentedDictionary RequiredMembersErrorSentinel = ImmutableSegmentedDictionary.Empty.Add("", null!); /// - /// if uninitialized. if there are errors. if + /// if uninitialized. if there are errors. if /// there are no required members. Otherwise, the required members. /// private ImmutableSegmentedDictionary _lazyRequiredMembers = default;