diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs
index cdd918a3aeb46..20f7118a66261 100644
--- a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs
+++ b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs
@@ -168,7 +168,7 @@ private static void MakeSubmissionInitialization(
}
///
- /// Construct a body for an auto-property accessor (updating or returning the backing field).
+ /// Construct a body for an auto-property accessor (updating or returning the backing field). This can return null on error scenarios.
///
internal static BoundBlock ConstructAutoPropertyAccessorBody(SourceMemberMethodSymbol accessor)
{
@@ -184,6 +184,13 @@ internal static BoundBlock ConstructAutoPropertyAccessorBody(SourceMemberMethodS
}
var field = property.BackingField;
+ if (field is null)
+ {
+ // This happens for public int { set; } where we produce ERR_AutoPropertyMustHaveGetAccessor
+ Debug.Assert(property.GetMethod is null);
+ return null;
+ }
+
Debug.Assert(!field.IsCreatedForFieldKeyword);
var fieldAccess = new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable) { WasCompilerGenerated = true };
BoundStatement statement;
diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
index 7836c005a8f0b..9c1fdac50e138 100644
--- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
+++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
@@ -1869,8 +1869,7 @@ syntaxNode is ConstructorDeclarationSyntax constructorSyntax &&
}
else
{
- var property = sourceMethod.AssociatedSymbol as SourcePropertySymbolBase;
- if ((object)property != null && property.IsAutoPropertyWithGetAccessor) // PROTOTYPE: Just IsAutoProperty, or _isAutoPropertyAccessor?
+ if (sourceMethod is SourcePropertyAccessorSymbol { BodyShouldBeSynthesized: true })
{
return MethodBodySynthesizer.ConstructAutoPropertyAccessorBody(sourceMethod);
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs
index 5e277dc4e08c7..8f7cdbbe5e75a 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs
@@ -22,7 +22,7 @@ internal class SourcePropertyAccessorSymbol : SourceMemberMethodSymbol
private ImmutableArray _lazyRefCustomModifiers;
private ImmutableArray _lazyExplicitInterfaceImplementations;
private string _lazyName;
- private readonly bool _isAutoPropertyAccessor;
+ private readonly bool _bodyShouldBeSynthesized;
private readonly bool _isExpressionBodied;
private readonly bool _containsFieldKeyword;
private readonly bool _usesInit;
@@ -32,7 +32,7 @@ public static SourcePropertyAccessorSymbol CreateAccessorSymbol(
SourcePropertySymbol property,
DeclarationModifiers propertyModifiers,
AccessorDeclarationSyntax syntax,
- bool isAutoPropertyAccessor,
+ bool bodyShouldBeSynthesizedForSemicolonOnly,
BindingDiagnosticBag diagnostics)
{
Debug.Assert(syntax.Kind() == SyntaxKind.GetAccessorDeclaration ||
@@ -42,7 +42,7 @@ public static SourcePropertyAccessorSymbol CreateAccessorSymbol(
bool isGetMethod = (syntax.Kind() == SyntaxKind.GetAccessorDeclaration);
var methodKind = isGetMethod ? MethodKind.PropertyGet : MethodKind.PropertySet;
- bool hasBody = syntax.Body is object;
+ bool hasBlockBody = syntax.Body is object;
bool hasExpressionBody = syntax.ExpressionBody is object;
bool isNullableAnalysisEnabled = containingType.DeclaringCompilation.IsNullableAnalysisEnabledIn(syntax);
CheckForBlockAndExpressionBody(syntax.Body, syntax.ExpressionBody, syntax, diagnostics);
@@ -52,13 +52,13 @@ public static SourcePropertyAccessorSymbol CreateAccessorSymbol(
propertyModifiers,
syntax.Keyword.GetLocation(),
syntax,
- hasBody,
+ hasBlockBody,
hasExpressionBody,
isIterator: SyntaxFacts.HasYieldOperations(syntax.Body),
syntax.Modifiers,
methodKind,
syntax.Keyword.IsKind(SyntaxKind.InitKeyword),
- isAutoPropertyAccessor,
+ bodyShouldBeSynthesized: bodyShouldBeSynthesizedForSemicolonOnly && !hasBlockBody && !hasExpressionBody,
isNullableAnalysisEnabled: isNullableAnalysisEnabled,
diagnostics);
}
@@ -99,34 +99,17 @@ public static SourcePropertyAccessorSymbol CreateAccessorSymbol(
propertyModifiers,
location,
syntax,
- hasBody: false,
+ hasBlockBody: false,
hasExpressionBody: false,
isIterator: false,
modifiers: new SyntaxTokenList(),
methodKind,
usesInit,
- isAutoPropertyAccessor: true,
+ bodyShouldBeSynthesized: true,
isNullableAnalysisEnabled: false,
diagnostics);
}
- public static SourcePropertyAccessorSymbol CreateAccessorSymbol(
- NamedTypeSymbol containingType,
- SynthesizedRecordEqualityContractProperty property,
- DeclarationModifiers propertyModifiers,
- Location location,
- CSharpSyntaxNode syntax,
- BindingDiagnosticBag diagnostics)
- {
- return new SynthesizedRecordEqualityContractProperty.GetAccessorSymbol(
- containingType,
- property,
- propertyModifiers,
- location,
- syntax,
- diagnostics);
- }
-
// PROTOTYPE(semi-auto-props): Figure out what is going to be more efficient, to go after tokens and then
// checking their parent, or to go after nodes (IdentifierNameSyntax) first and then checking the underlying token.
// PROTOTYPE(semi-auto-props): Filter out identifiers that syntactically cannot be keywords. For example those that follow a ., a -> or a :: in names. Something else?
@@ -166,7 +149,7 @@ private SourcePropertyAccessorSymbol(
base(containingType, syntax.GetReference(), location, isIterator: false)
{
_property = property;
- _isAutoPropertyAccessor = false;
+ _bodyShouldBeSynthesized = false;
_isExpressionBodied = true;
_containsFieldKeyword = property.IsIndexer ? false : NodeContainsFieldKeyword(syntax);
@@ -188,7 +171,7 @@ private SourcePropertyAccessorSymbol(
diagnostics.Add(info, location);
}
- this.CheckModifiers(location, hasBody: true, isAutoPropertyOrExpressionBodied: true, diagnostics: diagnostics);
+ this.CheckModifiers(location, isBodyMissing: false, diagnostics: diagnostics);
}
#nullable enable
@@ -198,13 +181,13 @@ protected SourcePropertyAccessorSymbol(
DeclarationModifiers propertyModifiers,
Location location,
CSharpSyntaxNode syntax,
- bool hasBody,
+ bool hasBlockBody,
bool hasExpressionBody,
bool isIterator,
SyntaxTokenList modifiers,
MethodKind methodKind,
bool usesInit,
- bool isAutoPropertyAccessor,
+ bool bodyShouldBeSynthesized,
bool isNullableAnalysisEnabled,
BindingDiagnosticBag diagnostics)
: base(containingType,
@@ -213,10 +196,10 @@ protected SourcePropertyAccessorSymbol(
isIterator)
{
_property = property;
- _isAutoPropertyAccessor = isAutoPropertyAccessor;
+ _bodyShouldBeSynthesized = bodyShouldBeSynthesized;
_containsFieldKeyword = property.IsIndexer ? false : NodeContainsFieldKeyword(getAccessorSyntax(syntax));
Debug.Assert(!_property.IsExpressionBodied, "Cannot have accessors in expression bodied lightweight properties");
- _isExpressionBodied = !hasBody && hasExpressionBody;
+ _isExpressionBodied = !hasBlockBody && hasExpressionBody;
_usesInit = usesInit;
if (_usesInit)
{
@@ -224,7 +207,7 @@ protected SourcePropertyAccessorSymbol(
}
bool modifierErrors;
- var declarationModifiers = this.MakeModifiers(modifiers, property.IsExplicitInterfaceImplementation, hasBody || hasExpressionBody, location, diagnostics, out modifierErrors);
+ var declarationModifiers = this.MakeModifiers(modifiers, property.IsExplicitInterfaceImplementation, hasBlockBody || hasExpressionBody, location, diagnostics, out modifierErrors);
// Include some modifiers from the containing property, but not the accessibility modifiers.
declarationModifiers |= GetAccessorModifiers(propertyModifiers) & ~DeclarationModifiers.AccessibilityMask;
@@ -239,9 +222,9 @@ protected SourcePropertyAccessorSymbol(
this.MakeFlags(methodKind, declarationModifiers, returnsVoid: false, isExtensionMethod: false, isNullableAnalysisEnabled: isNullableAnalysisEnabled,
isMetadataVirtualIgnoringModifiers: property.IsExplicitInterfaceImplementation && (declarationModifiers & DeclarationModifiers.Static) == 0);
- CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody: hasBody || hasExpressionBody || isAutoPropertyAccessor, diagnostics);
+ CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody: hasBlockBody || hasExpressionBody || _bodyShouldBeSynthesized, diagnostics);
- if (hasBody || hasExpressionBody)
+ if (hasBlockBody || hasExpressionBody)
{
CheckModifiersForBody(location, diagnostics);
}
@@ -254,7 +237,7 @@ protected SourcePropertyAccessorSymbol(
if (!modifierErrors)
{
- this.CheckModifiers(location, hasBody || hasExpressionBody, isAutoPropertyAccessor, diagnostics);
+ this.CheckModifiers(location, isBodyMissing: !hasBlockBody && !hasExpressionBody && !_bodyShouldBeSynthesized, diagnostics);
}
static CSharpSyntaxNode? getAccessorSyntax(CSharpSyntaxNode node)
@@ -478,6 +461,11 @@ internal Accessibility LocalAccessibility
///
internal bool ContainsFieldKeyword => _containsFieldKeyword;
+ ///
+ /// Indicates whether this accessor has no body in source and a body will be synthesized by the compiler.
+ ///
+ internal bool BodyShouldBeSynthesized => _bodyShouldBeSynthesized;
+
///
/// Indicates whether this accessor is readonly due to reasons scoped to itself and its containing property.
///
@@ -527,7 +515,7 @@ internal sealed override bool IsDeclaredReadOnly
return ContainingType.IsStructType() &&
!_property.IsStatic &&
- _isAutoPropertyAccessor &&
+ _bodyShouldBeSynthesized &&
MethodKind == MethodKind.PropertyGet;
}
}
@@ -564,7 +552,7 @@ private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, bool isExp
return mods;
}
- private void CheckModifiers(Location location, bool hasBody, bool isAutoPropertyOrExpressionBodied, BindingDiagnosticBag diagnostics)
+ private void CheckModifiers(Location location, bool isBodyMissing, BindingDiagnosticBag diagnostics)
{
// Check accessibility against the accessibility declared on the accessor not the property.
var localAccessibility = this.LocalAccessibility;
@@ -579,7 +567,7 @@ private void CheckModifiers(Location location, bool hasBody, bool isAutoProperty
// '{0}' is a new virtual member in sealed type '{1}'
diagnostics.Add(ErrorCode.ERR_NewVirtualInSealed, location, this, ContainingType);
}
- else if (!hasBody && !IsExtern && !IsAbstract && !isAutoPropertyOrExpressionBodied)
+ else if (isBodyMissing && !IsExtern && !IsAbstract)
{
diagnostics.Add(ErrorCode.ERR_ConcreteMissingBody, location, this);
}
@@ -602,7 +590,7 @@ private void CheckModifiers(Location location, bool hasBody, bool isAutoProperty
// 'init' accessors cannot be marked 'readonly'. Mark '{0}' readonly instead.
diagnostics.Add(ErrorCode.ERR_InitCannotBeReadonly, location, _property);
}
- else if (LocalDeclaredReadOnly && _isAutoPropertyAccessor && MethodKind == MethodKind.PropertySet)
+ else if (LocalDeclaredReadOnly && _bodyShouldBeSynthesized && MethodKind == MethodKind.PropertySet)
{
// Auto-implemented accessor '{0}' cannot be marked 'readonly'.
diagnostics.Add(ErrorCode.ERR_AutoSetterCantBeReadOnly, location, this);
@@ -835,7 +823,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r
{
base.AddSynthesizedAttributes(moduleBuilder, ref attributes);
- if (_isAutoPropertyAccessor)
+ if (_bodyShouldBeSynthesized)
{
var compilation = this.DeclaringCompilation;
AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor));
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs
index f71c881683cec..96fed048c58ab 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs
@@ -353,7 +353,7 @@ private static DeclarationModifiers MakeModifiers(
return mods;
}
- protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics)
+ protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics)
{
var syntax = (BasePropertyDeclarationSyntax)CSharpSyntaxNode;
ArrowExpressionClauseSyntax? arrowExpression = GetArrowExpression(syntax);
@@ -366,21 +366,21 @@ protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isA
}
else
{
- return CreateAccessorSymbol(GetGetAccessorDeclaration(syntax), isAutoPropertyAccessor, diagnostics);
+ return CreateAccessorSymbol(GetGetAccessorDeclaration(syntax), bodyShouldBeSynthesizedForSemicolonOnly, diagnostics);
}
}
- protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics)
+ protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics)
{
var syntax = (BasePropertyDeclarationSyntax)CSharpSyntaxNode;
Debug.Assert(!(syntax.AccessorList is null && GetArrowExpression(syntax) != null));
- return CreateAccessorSymbol(GetSetAccessorDeclaration(syntax), isAutoPropertyAccessor, diagnostics);
+ return CreateAccessorSymbol(GetSetAccessorDeclaration(syntax), bodyShouldBeSynthesizedForSemicolonOnly, diagnostics);
}
private SourcePropertyAccessorSymbol CreateAccessorSymbol(
AccessorDeclarationSyntax syntax,
- bool isAutoPropertyAccessor,
+ bool bodyShouldBeSynthesizedForSemicolonOnly,
BindingDiagnosticBag diagnostics)
{
return SourcePropertyAccessorSymbol.CreateAccessorSymbol(
@@ -388,7 +388,7 @@ private SourcePropertyAccessorSymbol CreateAccessorSymbol(
this,
_modifiers,
syntax,
- isAutoPropertyAccessor,
+ bodyShouldBeSynthesizedForSemicolonOnly,
diagnostics);
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs
index e3ab466a3e38d..f5d88cb95da38 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs
@@ -193,11 +193,11 @@ protected SourcePropertySymbolBase(
if (hasGetAccessor)
{
- _getMethod = CreateGetAccessorSymbol(isAutoPropertyAccessor: isAutoProperty, diagnostics);
+ _getMethod = CreateGetAccessorSymbol(bodyShouldBeSynthesizedForSemicolonOnly: isAutoProperty, diagnostics);
}
if (hasSetAccessor)
{
- _setMethod = CreateSetAccessorSymbol(isAutoPropertyAccessor: isAutoProperty, diagnostics);
+ _setMethod = CreateSetAccessorSymbol(bodyShouldBeSynthesizedForSemicolonOnly: isAutoProperty, diagnostics);
}
}
@@ -643,7 +643,7 @@ internal bool IsNew
/// The implementation may depend only on information available from the type.
///
protected abstract SourcePropertyAccessorSymbol CreateGetAccessorSymbol(
- bool isAutoPropertyAccessor,
+ bool bodyShouldBeSynthesizedForSemicolonOnly,
BindingDiagnosticBag diagnostics);
///
@@ -651,7 +651,7 @@ protected abstract SourcePropertyAccessorSymbol CreateGetAccessorSymbol(
/// The implementation may depend only on information available from the type.
///
protected abstract SourcePropertyAccessorSymbol CreateSetAccessorSymbol(
- bool isAutoPropertyAccessor,
+ bool bodyShouldBeSynthesizedForSemicolonOnly,
BindingDiagnosticBag diagnostics);
public sealed override MethodSymbol? GetMethod
diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs
index 9437e08dd6b5a..b862ee1b25dd0 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs
@@ -55,9 +55,9 @@ public override SyntaxList AttributeDeclarationSyntaxList
protected override Location TypeLocation
=> ContainingType.Locations[0];
- protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics)
+ protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics)
{
- return SourcePropertyAccessorSymbol.CreateAccessorSymbol(
+ return new GetAccessorSymbol(
ContainingType,
this,
_modifiers,
@@ -66,7 +66,7 @@ protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isA
diagnostics);
}
- protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics)
+ protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics)
{
throw ExceptionUtilities.Unreachable;
}
@@ -115,7 +115,7 @@ internal static void VerifyOverridesEqualityContractFromBase(PropertySymbol over
}
}
- internal sealed class GetAccessorSymbol : SourcePropertyAccessorSymbol
+ private sealed class GetAccessorSymbol : SourcePropertyAccessorSymbol
{
internal GetAccessorSymbol(
NamedTypeSymbol containingType,
@@ -130,13 +130,13 @@ internal GetAccessorSymbol(
propertyModifiers,
location,
syntax,
- hasBody: true,
+ hasBlockBody: true,
hasExpressionBody: false,
isIterator: false,
modifiers: new SyntaxTokenList(),
MethodKind.PropertyGet,
usesInit: false,
- isAutoPropertyAccessor: false,
+ bodyShouldBeSynthesized: false,
isNullableAnalysisEnabled: false,
diagnostics)
{
diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs
index 496f928ffbcfa..6c8a016d01ac6 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordPropertySymbol.cs
@@ -48,15 +48,15 @@ protected override Location TypeLocation
public override SyntaxList AttributeDeclarationSyntaxList
=> BackingParameter.AttributeDeclarationList;
- protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics)
+ protected override SourcePropertyAccessorSymbol CreateGetAccessorSymbol(bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics)
{
- Debug.Assert(isAutoPropertyAccessor);
+ Debug.Assert(bodyShouldBeSynthesizedForSemicolonOnly);
return CreateAccessorSymbol(isGet: true, CSharpSyntaxNode, diagnostics);
}
- protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool isAutoPropertyAccessor, BindingDiagnosticBag diagnostics)
+ protected override SourcePropertyAccessorSymbol CreateSetAccessorSymbol(bool bodyShouldBeSynthesizedForSemicolonOnly, BindingDiagnosticBag diagnostics)
{
- Debug.Assert(isAutoPropertyAccessor);
+ Debug.Assert(bodyShouldBeSynthesizedForSemicolonOnly);
return CreateAccessorSymbol(isGet: false, CSharpSyntaxNode, diagnostics);
}