-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Add warning when property has a backing field but a manually implemented accessor does not use it #75325
Add warning when property has a backing field but a manually implemented accessor does not use it #75325
Changes from 5 commits
3eb86da
cd64f8d
5a8104a
7c0f6c1
b6903fd
36345ac
6b95474
79ff4ec
2c8e4d0
41ccabc
b81794a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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, | ||
RikkiGibson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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. | ||
|
@@ -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, | ||
|
@@ -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) | ||
|
@@ -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; | ||
|
@@ -305,6 +312,47 @@ 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider inlining this local function. |
||
|
||
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 && [email protected](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) | ||
{ | ||
var accessorName = accessorToBlame switch | ||
{ | ||
{ MethodKind: MethodKind.PropertyGet, IsInitOnly: false } => SyntaxFacts.GetText(SyntaxKind.GetKeyword), | ||
{ MethodKind: MethodKind.PropertySet, IsInitOnly: false } => SyntaxFacts.GetText(SyntaxKind.SetKeyword), | ||
{ MethodKind: MethodKind.PropertySet, IsInitOnly: true } => SyntaxFacts.GetText(SyntaxKind.InitKeyword), | ||
_ => throw ExceptionUtilities.UnexpectedValue(accessorToBlame) | ||
}; | ||
|
||
diagnostics.Add(ErrorCode.WRN_AccessorDoesNotUseBackingField, accessorToBlame.GetFirstLocation(), accessorName, @this); | ||
} | ||
} | ||
} | ||
#nullable disable | ||
|
||
public sealed override RefKind RefKind | ||
{ | ||
get | ||
|
@@ -648,10 +696,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; | ||
|
@@ -811,6 +859,7 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, | |
this.CheckModifiers(isExplicitInterfaceImplementation, Location, IsIndexer, diagnostics); | ||
|
||
CheckInitializerIfNeeded(diagnostics); | ||
CheckFieldKeywordUsage(diagnostics); | ||
|
||
if (RefKind != RefKind.None && IsRequired) | ||
{ | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly,
the backing 'field'
reads very awkwardly. Maybe justshould use 'field'
?