Skip to content

Commit b4799dd

Browse files
authored
Merge pull request #61571 from dotnet/features/required-members
Merge Required Members to main
2 parents 54586cd + ce12420 commit b4799dd

File tree

220 files changed

+12120
-921
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

220 files changed

+12120
-921
lines changed

docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,3 +338,16 @@ Console.WriteLine($"{{{12:X}}}");
338338
The workaround is to remove the extra braces in the format string.
339339

340340
You can learn more about this change in the associated [roslyn issue](https://github.com/dotnet/roslyn/issues/57750).
341+
342+
## Types cannot be named `required`
343+
344+
***Introduced in Visual Studio 2022 version 17.3.*** Starting in C# 11, types cannot be named `required`. The compiler will report an error on all such type names. To work around this, the type name and all usages must be escaped with an `@`:
345+
346+
```csharp
347+
class required {} // Error CS9029
348+
class @required {} // No error
349+
```
350+
351+
This was done as `required` is now a member modifier for properties and fields.
352+
353+
You can learn more about this change in the associated [csharplang issue](https://github.com/dotnet/csharplang/issues/3630).

docs/contributing/Compiler Test Plan.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ This document provides guidance for thinking about language interactions and tes
3939
- Access modifiers (public, protected, internal, protected internal, private protected, private), static, ref
4040
- type declarations (class, record class/struct with or without positional members, struct, interface, type parameter)
4141
- methods
42-
- fields
43-
- properties (including get/set/init accessors)
42+
- fields (required and not)
43+
- properties (including get/set/init accessors, required and not)
4444
- events (including add/remove accessors)
4545
- Parameter modifiers (ref, out, in, params)
4646
- Attributes (including generic attributes and security attributes)
@@ -110,6 +110,7 @@ This document provides guidance for thinking about language interactions and tes
110110
- pre-processing directives
111111
- COM interop
112112
- modopt and modreq
113+
- CompilerFeatureRequiredAttribute
113114
- ref assemblies
114115
- extern alias
115116
- UnmanagedCallersOnly

src/Analyzers/CSharp/Tests/OrderModifiers/OrderModifiersTests.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,5 +431,39 @@ static internal class C2
431431
static internal class Nested { }
432432
}");
433433
}
434+
435+
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsOrderModifiers)]
436+
public async Task RequiredAfterAllOnProp()
437+
{
438+
await TestInRegularAndScriptAsync("""
439+
class C
440+
{
441+
[|required|] public virtual unsafe int Prop { get; init; }
442+
}
443+
""",
444+
"""
445+
class C
446+
{
447+
public virtual unsafe required int Prop { get; init; }
448+
}
449+
""");
450+
}
451+
452+
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsOrderModifiers)]
453+
public async Task RequiredAfterAllButVolatileOnField()
454+
{
455+
await TestInRegularAndScriptAsync("""
456+
class C
457+
{
458+
[|required|] public unsafe volatile int Field;
459+
}
460+
""",
461+
"""
462+
class C
463+
{
464+
public unsafe required volatile int Field;
465+
}
466+
""");
467+
}
434468
}
435469
}

src/CodeStyle/Core/Analyzers/PublicAPI.Unshipped.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsOverride.get -> b
4141
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsPartial.get -> bool
4242
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsReadOnly.get -> bool
4343
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsRef.get -> bool
44+
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsRequired.get -> bool
4445
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsSealed.get -> bool
4546
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsStatic.get -> bool
4647
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsUnsafe.get -> bool
@@ -56,6 +57,7 @@ Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsNew(bool isNe
5657
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsOverride(bool isOverride) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
5758
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsReadOnly(bool isReadOnly) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
5859
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsRef(bool isRef) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
60+
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsRequired(bool isRequired) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
5961
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsSealed(bool isSealed) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
6062
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsStatic(bool isStatic) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
6163
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsUnsafe(bool isUnsafe) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
@@ -84,6 +86,7 @@ static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Override.get
8486
static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Partial.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
8587
static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.ReadOnly.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
8688
static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Ref.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
89+
static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Required.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
8790
static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Sealed.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
8891
static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Static.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
8992
static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.TryParse(string value, out Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers modifiers) -> bool

src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,8 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a
194194
diagnostics,
195195
out var memberResolutionResult,
196196
out var candidateConstructors,
197-
allowProtectedConstructorsOfBaseType: true);
197+
allowProtectedConstructorsOfBaseType: true,
198+
suppressUnsupportedRequiredMembersError: false);
198199
attributeConstructor = memberResolutionResult.Member;
199200
expanded = memberResolutionResult.Resolution == MemberResolutionKind.ApplicableInExpandedForm;
200201
argsToParamsOpt = memberResolutionResult.Result.ArgsToParamsOpt;
@@ -238,6 +239,11 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a
238239
ImmutableArray<BoundAssignmentOperator> boundNamedArguments = analyzedArguments.NamedArguments?.ToImmutableAndFree() ?? ImmutableArray<BoundAssignmentOperator>.Empty;
239240
Debug.Assert(boundNamedArguments.All(arg => !arg.Right.NeedsToBeConverted()));
240241

242+
if (attributeConstructor is not null)
243+
{
244+
CheckRequiredMembersInObjectInitializer(attributeConstructor, ImmutableArray<BoundExpression>.CastUp(boundNamedArguments), node, diagnostics);
245+
}
246+
241247
analyzedArguments.ConstructorArguments.Free();
242248

243249
return new BoundAttribute(

src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs

Lines changed: 139 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4073,7 +4073,8 @@ private BoundExpression BindConstructorInitializerCore(
40734073
diagnostics,
40744074
out memberResolutionResult,
40754075
out candidateConstructors,
4076-
allowProtectedConstructorsOfBaseType: true);
4076+
allowProtectedConstructorsOfBaseType: true,
4077+
suppressUnsupportedRequiredMembersError: true);
40774078
MethodSymbol resultMember = memberResolutionResult.Member;
40784079

40794080
validateRecordCopyConstructor(constructor, baseType, resultMember, errorLocation, diagnostics);
@@ -4125,6 +4126,13 @@ private BoundExpression BindConstructorInitializerCore(
41254126
diagnostics);
41264127
}
41274128

4129+
if (resultMember.HasSetsRequiredMembers && !constructor.HasSetsRequiredMembers)
4130+
{
4131+
hasErrors = true;
4132+
// This constructor must add 'SetsRequiredMembers' because it chains to a constructor that has that attribute.
4133+
diagnostics.Add(ErrorCode.ERR_ChainingToSetsRequiredMembersRequiresSetsRequiredMembers, errorLocation);
4134+
}
4135+
41284136
return new BoundCall(
41294137
nonNullSyntax,
41304138
receiver,
@@ -4977,6 +4985,102 @@ private static void ReportDuplicateObjectMemberInitializers(BoundExpression boun
49774985
}
49784986
}
49794987

4988+
#nullable enable
4989+
private static void CheckRequiredMembersInObjectInitializer(
4990+
MethodSymbol constructor,
4991+
ImmutableArray<BoundExpression> initializers,
4992+
SyntaxNode creationSyntax,
4993+
BindingDiagnosticBag diagnostics)
4994+
{
4995+
if (!constructor.ShouldCheckRequiredMembers())
4996+
{
4997+
return;
4998+
}
4999+
5000+
if (constructor.ContainingType.HasRequiredMembersError)
5001+
{
5002+
// 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.
5003+
return;
5004+
}
5005+
5006+
var requiredMembers = constructor.ContainingType.AllRequiredMembers;
5007+
5008+
if (requiredMembers.Count == 0)
5009+
{
5010+
return;
5011+
}
5012+
5013+
var requiredMembersBuilder = requiredMembers.ToBuilder();
5014+
5015+
if (initializers.IsDefaultOrEmpty)
5016+
{
5017+
reportMembers();
5018+
return;
5019+
}
5020+
5021+
foreach (var initializer in initializers)
5022+
{
5023+
if (initializer is not BoundAssignmentOperator assignmentOperator)
5024+
{
5025+
continue;
5026+
}
5027+
5028+
var memberSymbol = assignmentOperator.Left switch
5029+
{
5030+
// Regular initializers
5031+
BoundObjectInitializerMember member => member.MemberSymbol,
5032+
// Attribute initializers
5033+
BoundPropertyAccess propertyAccess => propertyAccess.PropertySymbol,
5034+
BoundFieldAccess fieldAccess => fieldAccess.FieldSymbol,
5035+
// Error cases
5036+
_ => null
5037+
};
5038+
5039+
if (memberSymbol is null)
5040+
{
5041+
continue;
5042+
}
5043+
5044+
if (!requiredMembersBuilder.TryGetValue(memberSymbol.Name, out var requiredMember))
5045+
{
5046+
continue;
5047+
}
5048+
5049+
if (!memberSymbol.Equals(requiredMember, TypeCompareKind.ConsiderEverything))
5050+
{
5051+
continue;
5052+
}
5053+
5054+
requiredMembersBuilder.Remove(memberSymbol.Name);
5055+
5056+
if (assignmentOperator.Right is BoundObjectInitializerExpressionBase initializerExpression)
5057+
{
5058+
// Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer.
5059+
diagnostics.Add(ErrorCode.ERR_RequiredMembersMustBeAssignedValue, initializerExpression.Syntax.Location, requiredMember);
5060+
}
5061+
}
5062+
5063+
reportMembers();
5064+
5065+
void reportMembers()
5066+
{
5067+
Location location = creationSyntax switch
5068+
{
5069+
ObjectCreationExpressionSyntax { Type: { } type } => type.Location,
5070+
BaseObjectCreationExpressionSyntax { NewKeyword: { } newKeyword } => newKeyword.GetLocation(),
5071+
AttributeSyntax { Name: { } name } => name.Location,
5072+
_ => creationSyntax.Location
5073+
};
5074+
5075+
foreach (var (_, member) in requiredMembersBuilder)
5076+
{
5077+
// Required member '{0}' must be set in the object initializer or attribute constructor.
5078+
diagnostics.Add(ErrorCode.ERR_RequiredMemberMustBeSet, location, member);
5079+
}
5080+
}
5081+
}
5082+
#nullable disable
5083+
49805084
private BoundCollectionInitializerExpression BindCollectionInitializerExpression(
49815085
InitializerExpressionSyntax initializerSyntax,
49825086
TypeSymbol initializerType,
@@ -5375,7 +5479,8 @@ protected BoundExpression BindClassCreationExpression(
53755479
diagnostics,
53765480
out MemberResolutionResult<MethodSymbol> memberResolutionResult,
53775481
out ImmutableArray<MethodSymbol> candidateConstructors,
5378-
allowProtectedConstructorsOfBaseType: false) &&
5482+
allowProtectedConstructorsOfBaseType: false,
5483+
suppressUnsupportedRequiredMembersError: false) &&
53795484
!type.IsAbstract)
53805485
{
53815486
var method = memberResolutionResult.Member;
@@ -5421,7 +5526,7 @@ protected BoundExpression BindClassCreationExpression(
54215526
}
54225527

54235528
boundInitializerOpt = makeBoundInitializerOpt();
5424-
result = new BoundObjectCreationExpression(
5529+
var creation = new BoundObjectCreationExpression(
54255530
node,
54265531
method,
54275532
candidateConstructors,
@@ -5437,7 +5542,9 @@ protected BoundExpression BindClassCreationExpression(
54375542
type,
54385543
hasError);
54395544

5440-
return result;
5545+
CheckRequiredMembersInObjectInitializer(creation.Constructor, creation.InitializerExpressionOpt?.Initializers ?? default, creation.Syntax, diagnostics);
5546+
5547+
return creation;
54415548
}
54425549

54435550
LookupResultKind resultKind;
@@ -5709,7 +5816,8 @@ internal bool TryPerformConstructorOverloadResolution(
57095816
BindingDiagnosticBag diagnostics,
57105817
out MemberResolutionResult<MethodSymbol> memberResolutionResult,
57115818
out ImmutableArray<MethodSymbol> candidateConstructors,
5712-
bool allowProtectedConstructorsOfBaseType) // Last to make named arguments more convenient.
5819+
bool allowProtectedConstructorsOfBaseType,
5820+
bool suppressUnsupportedRequiredMembersError) // Last to make named arguments more convenient.
57135821
{
57145822
// Get accessible constructors for performing overload resolution.
57155823
ImmutableArray<MethodSymbol> allInstanceConstructors;
@@ -5757,7 +5865,7 @@ internal bool TryPerformConstructorOverloadResolution(
57575865
}
57585866
}
57595867

5760-
diagnostics.Add(errorLocation, useSiteInfo);
5868+
ReportConstructorUseSiteDiagnostics(errorLocation, diagnostics, suppressUnsupportedRequiredMembersError, useSiteInfo);
57615869

57625870
if (succeededIgnoringAccessibility)
57635871
{
@@ -5814,6 +5922,31 @@ internal bool TryPerformConstructorOverloadResolution(
58145922
return succeededConsideringAccessibility;
58155923
}
58165924

5925+
internal static bool ReportConstructorUseSiteDiagnostics(Location errorLocation, BindingDiagnosticBag diagnostics, bool suppressUnsupportedRequiredMembersError, CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
5926+
{
5927+
if (suppressUnsupportedRequiredMembersError && useSiteInfo.AccumulatesDiagnostics && useSiteInfo.Diagnostics is { Count: not 0 })
5928+
{
5929+
diagnostics.AddDependencies(useSiteInfo);
5930+
foreach (var diagnostic in useSiteInfo.Diagnostics)
5931+
{
5932+
// We don't want to report this error here because we'll report ERR_RequiredMembersBaseTypeInvalid. That error is suppressable by the
5933+
// user using the `SetsRequiredMembers` attribute on the constructor, so reporting this error would prevent that from working.
5934+
if ((ErrorCode)diagnostic.Code == ErrorCode.ERR_RequiredMembersInvalid)
5935+
{
5936+
continue;
5937+
}
5938+
5939+
diagnostics.ReportUseSiteDiagnostic(diagnostic, errorLocation);
5940+
}
5941+
5942+
return true;
5943+
}
5944+
else
5945+
{
5946+
return diagnostics.Add(errorLocation, useSiteInfo);
5947+
}
5948+
}
5949+
58175950
private ImmutableArray<MethodSymbol> GetAccessibleConstructorsForOverloadResolution(NamedTypeSymbol type, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
58185951
{
58195952
ImmutableArray<MethodSymbol> allInstanceConstructors;

src/Compilers/CSharp/Portable/Binder/Binder_Initializers.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ internal struct ProcessedFieldInitializers
1818
{
1919
internal ImmutableArray<BoundInitializer> BoundInitializers { get; set; }
2020
internal BoundStatement? LoweredInitializers { get; set; }
21-
internal NullableWalker.VariableState AfterInitializersState;
2221
internal bool HasErrors { get; set; }
2322
internal ImportChain? FirstImportChain { get; set; }
2423
}

0 commit comments

Comments
 (0)