Skip to content

Commit

Permalink
special handling for initializers
Browse files Browse the repository at this point in the history
  • Loading branch information
RikkiGibson committed Oct 8, 2024
1 parent 5a8104a commit 7c0f6c1
Show file tree
Hide file tree
Showing 19 changed files with 227 additions and 73 deletions.
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -8003,7 +8003,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<value>Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes.</value>
</data>
<data name="WRN_AccessorDoesNotUseBackingField" xml:space="preserve">
<value>The '{0}' accessor of property '{1}' should use the backing 'field' because the other accessor is using it.</value>
<value>'{0}' should use the backing 'field' because the other accessor is using it.</value>
</data>
<data name="WRN_AccessorDoesNotUseBackingField_Title" xml:space="preserve">
<value>Property accessor should use the backing 'field' because the other accessor is using it.</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ private static SourcePropertySymbol Create(
string? aliasQualifierOpt;
string memberName = ExplicitInterfaceHelpers.GetMemberNameAndInterfaceSymbol(binder, explicitInterfaceSpecifier, name, diagnostics, out explicitInterfaceType, out aliasQualifierOpt);

var symbol = new SourcePropertySymbol(
return new SourcePropertySymbol(
containingType,
syntax,
hasGetAccessor: getSyntax != null || isExpressionBodied,
Expand All @@ -88,30 +88,11 @@ private static SourcePropertySymbol Create(
hasAutoPropertySet: hasAutoPropertySet,
isExpressionBodied: isExpressionBodied,
accessorsHaveImplementation: accessorsHaveImplementation,
usesFieldKeyword: getterUsesFieldKeyword || setterUsesFieldKeyword,
getterUsesFieldKeyword: getterUsesFieldKeyword,
setterUsesFieldKeyword: setterUsesFieldKeyword,
memberName,
location,
diagnostics);

if (binder.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureFieldKeyword))
{
AccessorDeclarationSyntax? accessorToBlame = null;
if (hasSetAccessorImplementation && !setterUsesFieldKeyword && (hasAutoPropertyGet || getterUsesFieldKeyword))
{
accessorToBlame = setSyntax;
}
else if (hasGetAccessorImplementation && !getterUsesFieldKeyword && (hasAutoPropertySet || setterUsesFieldKeyword))
{
accessorToBlame = getSyntax;
}

if (accessorToBlame is not null)
{
diagnostics.Add(ErrorCode.WRN_AccessorDoesNotUseBackingField, accessorToBlame.Keyword, [accessorToBlame.Keyword.ValueText, symbol]);
}
}

return symbol;
}

private SourcePropertySymbol(
Expand All @@ -128,7 +109,8 @@ private SourcePropertySymbol(
bool hasAutoPropertySet,
bool isExpressionBodied,
bool accessorsHaveImplementation,
bool usesFieldKeyword,
bool getterUsesFieldKeyword,
bool setterUsesFieldKeyword,
string memberName,
Location location,
BindingDiagnosticBag diagnostics)
Expand All @@ -147,7 +129,8 @@ private SourcePropertySymbol(
hasAutoPropertySet: hasAutoPropertySet,
isExpressionBodied: isExpressionBodied,
accessorsHaveImplementation: accessorsHaveImplementation,
usesFieldKeyword: usesFieldKeyword,
getterUsesFieldKeyword: getterUsesFieldKeyword,
setterUsesFieldKeyword: setterUsesFieldKeyword,
syntax.Type.SkipScoped(out _).GetRefKindInLocalOrReturn(diagnostics),
memberName,
syntax.AttributeLists,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ private enum Flags : ushort
IsExpressionBodied = 1 << 0,
HasAutoPropertyGet = 1 << 1,
HasAutoPropertySet = 1 << 2,
UsesFieldKeyword = 1 << 3,
IsExplicitInterfaceImplementation = 1 << 4,
HasInitializer = 1 << 5,
AccessorsHaveImplementation = 1 << 6,
HasExplicitAccessModifier = 1 << 7,
RequiresBackingField = 1 << 8,
GetterUsesFieldKeyword = 1 << 4,
SetterUsesFieldKeyword = 1 << 5,
IsExplicitInterfaceImplementation = 1 << 6,
HasInitializer = 1 << 7,
AccessorsHaveImplementation = 1 << 8,
HasExplicitAccessModifier = 1 << 9,
RequiresBackingField = 1 << 10,
}

// TODO (tomat): consider splitting into multiple subclasses/rare data.
Expand Down Expand Up @@ -90,7 +91,8 @@ protected SourcePropertySymbolBase(
bool hasAutoPropertySet,
bool isExpressionBodied,
bool accessorsHaveImplementation,
bool usesFieldKeyword,
bool getterUsesFieldKeyword,
bool setterUsesFieldKeyword,
RefKind refKind,
string memberName,
SyntaxList<AttributeListSyntax> indexerNameAttributeLists,
Expand Down Expand Up @@ -133,9 +135,14 @@ protected SourcePropertySymbolBase(
_propertyFlags |= Flags.HasAutoPropertySet;
}

if (usesFieldKeyword)
if (getterUsesFieldKeyword)
{
_propertyFlags |= Flags.UsesFieldKeyword;
_propertyFlags |= Flags.GetterUsesFieldKeyword;
}

if (setterUsesFieldKeyword)
{
_propertyFlags |= Flags.SetterUsesFieldKeyword;
}

if (hasInitializer)
Expand Down Expand Up @@ -171,7 +178,7 @@ protected SourcePropertySymbolBase(
_name = _lazySourceName = memberName;
}

if (usesFieldKeyword || hasAutoPropertyGet || hasAutoPropertySet || hasInitializer)
if (getterUsesFieldKeyword || setterUsesFieldKeyword || hasAutoPropertyGet || hasAutoPropertySet || hasInitializer)
{
Debug.Assert(!IsIndexer);
_propertyFlags |= Flags.RequiresBackingField;
Expand Down Expand Up @@ -305,6 +312,39 @@ protected void CheckInitializerIfNeeded(BindingDiagnosticBag diagnostics)
}
}

#nullable enable
private void CheckFieldKeywordUsage(BindingDiagnosticBag diagnostics)
{
if (!this.DeclaringCompilation.IsFeatureEnabled(MessageID.IDS_FeatureFieldKeyword))
{
return;
}

check((SourcePropertySymbolBase?)PartialImplementationPart ?? this, diagnostics);

static void check(SourcePropertySymbolBase @this, BindingDiagnosticBag diagnostics)
{
SourcePropertyAccessorSymbol? accessorToBlame = null;
var propertyFlags = @this._propertyFlags;
var getterUsesFieldKeyword = (propertyFlags & Flags.GetterUsesFieldKeyword) != 0;
var setterUsesFieldKeyword = (propertyFlags & Flags.SetterUsesFieldKeyword) != 0;
if (@this._setMethod is { IsAutoPropertyAccessor: false } setMethod && !setterUsesFieldKeyword && !@this.IsSetOnEitherPart(Flags.HasInitializer) && (@this.HasAutoPropertyGet || getterUsesFieldKeyword))
{
accessorToBlame = setMethod;
}
else if (@this._getMethod is { IsAutoPropertyAccessor: false } getMethod && !getterUsesFieldKeyword && (@this.HasAutoPropertySet || setterUsesFieldKeyword))
{
accessorToBlame = getMethod;
}

if (accessorToBlame is not null)
{
diagnostics.Add(ErrorCode.WRN_AccessorDoesNotUseBackingField, accessorToBlame.GetFirstLocation(), accessorToBlame);
}
}
}
#nullable disable

public sealed override RefKind RefKind
{
get
Expand Down Expand Up @@ -648,10 +688,10 @@ public bool HasSkipLocalsInitAttribute
}

internal bool IsAutoPropertyOrUsesFieldKeyword
=> IsSetOnEitherPart(Flags.HasAutoPropertyGet | Flags.HasAutoPropertySet | Flags.UsesFieldKeyword);
=> IsSetOnEitherPart(Flags.HasAutoPropertyGet | Flags.HasAutoPropertySet | Flags.GetterUsesFieldKeyword | Flags.SetterUsesFieldKeyword);

internal bool UsesFieldKeyword
=> IsSetOnEitherPart(Flags.UsesFieldKeyword);
=> IsSetOnEitherPart(Flags.GetterUsesFieldKeyword | Flags.SetterUsesFieldKeyword);

protected bool HasExplicitAccessModifier
=> (_propertyFlags & Flags.HasExplicitAccessModifier) != 0;
Expand Down Expand Up @@ -811,6 +851,7 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions,
this.CheckModifiers(isExplicitInterfaceImplementation, Location, IsIndexer, diagnostics);

CheckInitializerIfNeeded(diagnostics);
CheckFieldKeywordUsage(diagnostics);

if (RefKind != RefKind.None && IsRequired)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public SynthesizedRecordEqualityContractProperty(SourceMemberContainerTypeSymbol
hasAutoPropertySet: false,
isExpressionBodied: false,
accessorsHaveImplementation: true,
usesFieldKeyword: false,
getterUsesFieldKeyword: false,
setterUsesFieldKeyword: false,
RefKind.None,
PropertyName,
indexerNameAttributeLists: new SyntaxList<AttributeListSyntax>(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public SynthesizedRecordPropertySymbol(
hasAutoPropertySet: true,
isExpressionBodied: false,
accessorsHaveImplementation: true,
usesFieldKeyword: false,
getterUsesFieldKeyword: false,
setterUsesFieldKeyword: false,
RefKind.None,
backingParameter.Name,
indexerNameAttributeLists: new SyntaxList<AttributeListSyntax>(),
Expand Down
4 changes: 2 additions & 2 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.

4 changes: 2 additions & 2 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.

4 changes: 2 additions & 2 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.

4 changes: 2 additions & 2 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.

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf

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

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf

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

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf

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

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf

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

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf

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

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf

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

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf

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

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf

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

4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf

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

Loading

0 comments on commit 7c0f6c1

Please sign in to comment.