Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error when required members are not set #60101

Merged
merged 8 commits into from
Mar 17, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,11 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a
ImmutableArray<BoundAssignmentOperator> boundNamedArguments = analyzedArguments.NamedArguments?.ToImmutableAndFree() ?? ImmutableArray<BoundAssignmentOperator>.Empty;
Debug.Assert(boundNamedArguments.All(arg => !arg.Right.NeedsToBeConverted()));

if (attributeConstructor is not null)
{
CheckRequiredMembersInObjectInitializer(attributeConstructor, ImmutableArray<BoundExpression>.CastUp(boundNamedArguments), node, diagnostics);
}

analyzedArguments.ConstructorArguments.Free();

return new BoundAttribute(node, attributeConstructor, boundConstructorArguments, boundConstructorArgumentNamesOpt, argsToParamsOpt, expanded,
Expand Down Expand Up @@ -572,7 +577,8 @@ protected MethodSymbol BindAttributeConstructor(
diagnostics,
out memberResolutionResult,
out candidateConstructors,
allowProtectedConstructorsOfBaseType: true))
allowProtectedConstructorsOfBaseType: true,
suppressUnsupportedRequiredMembersError: false))
{
resultKind = resultKind.WorseResultKind(
memberResolutionResult.IsValid && !IsConstructorAccessible(memberResolutionResult.Member, ref useSiteInfo) ?
Expand Down
73 changes: 55 additions & 18 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4064,7 +4064,8 @@ private BoundExpression BindConstructorInitializerCore(
diagnostics,
out memberResolutionResult,
out candidateConstructors,
allowProtectedConstructorsOfBaseType: true);
allowProtectedConstructorsOfBaseType: true,
suppressUnsupportedRequiredMembersError: true);
MethodSymbol resultMember = memberResolutionResult.Member;

validateRecordCopyConstructor(constructor, baseType, resultMember, errorLocation, diagnostics);
Expand Down Expand Up @@ -4967,55 +4968,71 @@ private static void ReportDuplicateObjectMemberInitializers(BoundExpression boun
}
}

#nullable enable
private static void CheckRequiredMembersInObjectInitializer(
BoundObjectCreationExpression creation,
MethodSymbol constructor,
ImmutableArray<BoundExpression> initializers,
SyntaxNode creationSyntax,
BindingDiagnosticBag diagnostics)
{
if (!creation.Constructor.ShouldCheckRequiredMembers())
if (!constructor.ShouldCheckRequiredMembers())
{
return;
}

if (creation.Constructor.ContainingType.HasRequiredMembersError)
if (constructor.ContainingType.HasRequiredMembersError)
{
// An error will be reported on the constructor if from source, or a use-site diagnostic will be reported on the use if from metadata.
return;
}

var requiredMembers = creation.Constructor.ContainingType.AllRequiredMembers.ToBuilder();
var requiredMembers = constructor.ContainingType.AllRequiredMembers.ToBuilder();

if (requiredMembers.Count == 0)
333fred marked this conversation as resolved.
Show resolved Hide resolved
{
return;
}

if (creation.InitializerExpressionOpt == null)
if (initializers.IsDefaultOrEmpty)
{
reportMembers();
return;
}

foreach (var initializer in creation.InitializerExpressionOpt.Initializers)
foreach (var initializer in initializers)
{
if (initializer is not BoundAssignmentOperator { Left: BoundObjectInitializerMember member, Right: { } initializerExpression })
var (memberSymbol, initializerExpression) = initializer is BoundAssignmentOperator assignmentOperator
? (assignmentOperator.Left switch
{
// Regular initializers
BoundObjectInitializerMember member => member.MemberSymbol,
// Attribute initializers
BoundPropertyAccess propertyAccess => propertyAccess.PropertySymbol,
BoundFieldAccess fieldAccess => fieldAccess.FieldSymbol,
_ => null
333fred marked this conversation as resolved.
Show resolved Hide resolved
}, assignmentOperator.Right)
: default;
333fred marked this conversation as resolved.
Show resolved Hide resolved

if (memberSymbol is null)
{
continue;
}

if (!requiredMembers.TryGetValue(member.MemberSymbol.Name, out var requiredMember))
if (!requiredMembers.TryGetValue(memberSymbol.Name, out var requiredMember))
{
continue;
}

if (!member.MemberSymbol.Equals(requiredMember, TypeCompareKind.ConsiderEverything))
if (!memberSymbol.Equals(requiredMember, TypeCompareKind.ConsiderEverything))
{
continue;
}

requiredMembers.Remove(member.MemberSymbol.Name);
requiredMembers.Remove(memberSymbol.Name);

if (initializerExpression is BoundObjectInitializerExpressionBase)
{
// Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer.
diagnostics.Add(ErrorCode.ERR_RequiredMembersMustBeAssignment, initializerExpression.Syntax.Location, requiredMember);
333fred marked this conversation as resolved.
Show resolved Hide resolved
}
}
Expand All @@ -5024,20 +5041,22 @@ private static void CheckRequiredMembersInObjectInitializer(

void reportMembers()
{
Location location = creation.Syntax switch
Location location = creationSyntax switch
{
ObjectCreationExpressionSyntax { Type: { } type } => type.Location,
BaseObjectCreationExpressionSyntax { NewKeyword: { } newKeyword } => newKeyword.GetLocation(),
_ => creation.Syntax.Location
AttributeSyntax { Name: { } name } => name.Location,
_ => creationSyntax.Location
333fred marked this conversation as resolved.
Show resolved Hide resolved
};

foreach (var (_, member) in requiredMembers)
{
// Required member '{0}' must be set in the object initializer.
// Required member '{0}' must be set in the object initializer or attribute constructor.
diagnostics.Add(ErrorCode.ERR_RequiredMemberMustBeSet, location, member);
}
}
}
#nullable disable

private BoundCollectionInitializerExpression BindCollectionInitializerExpression(
InitializerExpressionSyntax initializerSyntax,
Expand Down Expand Up @@ -5437,7 +5456,8 @@ protected BoundExpression BindClassCreationExpression(
diagnostics,
out MemberResolutionResult<MethodSymbol> memberResolutionResult,
out ImmutableArray<MethodSymbol> candidateConstructors,
allowProtectedConstructorsOfBaseType: false) &&
allowProtectedConstructorsOfBaseType: false,
suppressUnsupportedRequiredMembersError: false) &&
!type.IsAbstract)
{
var method = memberResolutionResult.Member;
Expand Down Expand Up @@ -5499,7 +5519,7 @@ protected BoundExpression BindClassCreationExpression(
type,
hasError);

CheckRequiredMembersInObjectInitializer(creation, diagnostics);
CheckRequiredMembersInObjectInitializer(creation.Constructor, creation.InitializerExpressionOpt?.Initializers ?? default, creation.Syntax, diagnostics);

return creation;
}
Expand Down Expand Up @@ -5773,7 +5793,8 @@ internal bool TryPerformConstructorOverloadResolution(
BindingDiagnosticBag diagnostics,
out MemberResolutionResult<MethodSymbol> memberResolutionResult,
out ImmutableArray<MethodSymbol> candidateConstructors,
bool allowProtectedConstructorsOfBaseType) // Last to make named arguments more convenient.
bool allowProtectedConstructorsOfBaseType,
bool suppressUnsupportedRequiredMembersError) // Last to make named arguments more convenient.
{
// Get accessible constructors for performing overload resolution.
ImmutableArray<MethodSymbol> allInstanceConstructors;
Expand Down Expand Up @@ -5821,7 +5842,23 @@ internal bool TryPerformConstructorOverloadResolution(
}
}

diagnostics.Add(errorLocation, useSiteInfo);
if (suppressUnsupportedRequiredMembersError && useSiteInfo.AccumulatesDiagnostics && useSiteInfo.Diagnostics is { Count: not 0 })
333fred marked this conversation as resolved.
Show resolved Hide resolved
{
diagnostics.AddDependencies(useSiteInfo);
foreach (var diagnostic in useSiteInfo.Diagnostics)
{
if ((ErrorCode)diagnostic.Code == ErrorCode.ERR_RequiredMembersInvalid)
{
continue;
}

diagnostics.ReportUseSiteDiagnostic(diagnostic, errorLocation);
}
}
else
{
diagnostics.Add(errorLocation, useSiteInfo);
}

if (succeededIgnoringAccessibility)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -7008,7 +7008,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<value>Required member '{0}' must be settable.</value>
</data>
<data name="ERR_RequiredMemberMustBeSet" xml:space="preserve">
<value>Required member '{0}' must be set in the object initializer.</value>
<value>Required member '{0}' must be set in the object initializer or attribute constructor.</value>
</data>
<data name="ERR_RequiredMembersMustBeAssignment" xml:space="preserve">
333fred marked this conversation as resolved.
Show resolved Hide resolved
<value>Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer.</value>
Expand All @@ -7017,7 +7017,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<value>The required members list for '{0}' is malformed and cannot be interpreted.</value>
</data>
<data name="ERR_RequiredMembersBaseTypeInvalid" xml:space="preserve">
<value>The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute.</value>
<value>The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute.</value>
</data>
<data name="ERR_LineContainsDifferentWhitespace" xml:space="preserve">
<value>Line contains different whitespace than the closing line of the raw string literal: '{0}' versus '{1}'</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2410,7 +2410,7 @@ private void CheckForRequiredMemberAttribute(BindingDiagnosticBag diagnostics)
continue;
}

// The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the `SetsRequiredMembers` attribute.
// The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute.
diagnostics.Add(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, method.Locations[0], BaseTypeNoUseSiteDiagnostics);
333fred marked this conversation as resolved.
Show resolved Hide resolved
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -842,8 +842,8 @@ internal static bool HasAsyncMethodBuilderAttribute(this Symbol symbol, [NotNull

internal static bool IsRequired(this Symbol symbol) => symbol is FieldSymbol { IsRequired: true } or PropertySymbol { IsRequired: true };

internal static bool ShouldCheckRequiredMembers(this MethodSymbol constructor)
internal static bool ShouldCheckRequiredMembers(this MethodSymbol method)
// PROTOTYPE(req): Check for the SetsRequiredMembersAttribute and return false for that case
=> constructor.MethodKind == MethodKind.Constructor;
=> method.MethodKind == MethodKind.Constructor;
}
}
10 changes: 5 additions & 5 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading