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);