Skip to content

Commit b04c5c3

Browse files
authored
[semi-auto-props]: Rename ContainsFieldKeyword, revise cases that should handle FieldKeywordBackingField (#61310)
1 parent d04d096 commit b04c5c3

File tree

12 files changed

+415
-21
lines changed

12 files changed

+415
-21
lines changed

src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ private void CompileNamedType(NamedTypeSymbol containingType)
520520
var member = members[memberOrdinal];
521521

522522
//When a filter is supplied, limit the compilation of members passing the filter.
523-
if (member is not SourcePropertyAccessorSymbol { ContainsFieldKeyword: true } accessor ||
523+
if (member is not SourcePropertyAccessorSymbol { ContainsFieldIdentifier: true } accessor ||
524524
!PassesFilter(_filterOpt, member))
525525
{
526526
continue;
@@ -579,7 +579,7 @@ private void CompileNamedType(NamedTypeSymbol containingType)
579579
Debug.Assert(!method.IsPartialDefinition());
580580
}
581581

582-
if (member is SourcePropertyAccessorSymbol { ContainsFieldKeyword: true })
582+
if (member is SourcePropertyAccessorSymbol { ContainsFieldIdentifier: true })
583583
{
584584
// We already compiled these accessors in the loop above.
585585
continue;

src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,12 @@ private static void GetDocumentsForMethodsAndNestedTypes(PooledHashSet<Cci.Debug
389389

390390
case SymbolKind.Property:
391391
AddSymbolLocation(result, member);
392+
FieldSymbol fieldKeywordBackingField = (member as SourcePropertySymbolBase)?.FieldKeywordBackingField;
393+
if (fieldKeywordBackingField is not null)
394+
{
395+
AddSymbolLocation(result, fieldKeywordBackingField);
396+
}
397+
392398
break;
393399
case SymbolKind.Field:
394400
if (member is TupleErrorFieldSymbol)

src/Compilers/CSharp/Portable/FlowAnalysis/EmptyStructTypeCache.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ private FieldSymbol GetActualField(Symbol member, NamedTypeSymbol type)
215215
return (!eventSymbol.HasAssociatedField || ShouldIgnoreStructField(eventSymbol, eventSymbol.Type)) ? null : eventSymbol.AssociatedField.AsMember(type);
216216
case SymbolKind.Property:
217217
// PROTOTYPE(semi-auto-props): Review other event associated field callers and see if we have to do anything special for properties.
218+
// Everything is reviewed except FlowAnalysis.
218219

219220
// Backing field for semi auto props are not included in GetMembers.
220221
if (member is SourcePropertySymbol property)

src/Compilers/CSharp/Portable/Symbols/BaseTypeAnalysis.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ private static (bool definitelyManaged, bool hasGenerics) DependsOnDefinitelyMan
180180
var hasGenerics = false;
181181
if (partialClosure.Add(type))
182182
{
183-
foreach (var member in type.GetInstanceFieldsAndEvents())
183+
foreach (var member in type.GetInstanceFieldsAndEventsAndProperties())
184184
{
185185
// Only instance fields (including field-like events) affect the outcome.
186186
FieldSymbol field;
@@ -190,10 +190,14 @@ private static (bool definitelyManaged, bool hasGenerics) DependsOnDefinitelyMan
190190
field = (FieldSymbol)member;
191191
Debug.Assert((object)(field.AssociatedSymbol as EventSymbol) == null,
192192
"Didn't expect to find a field-like event backing field in the member list.");
193+
Debug.Assert(field is not SynthesizedBackingFieldSymbol { IsCreatedForFieldKeyword: true });
193194
break;
194195
case SymbolKind.Event:
195196
field = ((EventSymbol)member).AssociatedField;
196197
break;
198+
case SymbolKind.Property:
199+
field = (member as SourcePropertySymbolBase)?.FieldKeywordBackingField;
200+
break;
197201
default:
198202
throw ExceptionUtilities.UnexpectedValue(member.Kind);
199203
}

src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@ internal static bool IsFieldOrFieldLikeEvent(this Symbol member, out FieldSymbol
641641
case SymbolKind.Event:
642642
field = ((EventSymbol)member).AssociatedField;
643643
return (object)field != null;
644+
// PROTOTYPE(semi-auto-props): Revise if we need to do something for FieldKeywordBackingfield.
644645
default:
645646
field = null;
646647
return false;

src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -550,19 +550,20 @@ internal virtual ImmutableArray<Symbol> GetSimpleNonTypeMembers(string name)
550550
/// For source symbols may be called while calculating
551551
/// <see cref="NamespaceOrTypeSymbol.GetMembersUnordered"/>.
552552
/// </remarks>
553-
internal virtual IEnumerable<Symbol> GetInstanceFieldsAndEvents()
553+
internal virtual IEnumerable<Symbol> GetInstanceFieldsAndEventsAndProperties()
554554
{
555-
return GetMembersUnordered().Where(IsInstanceFieldOrEvent);
555+
return GetMembersUnordered().Where(IsInstanceFieldOrEventOrProperty);
556556
}
557557

558-
protected static Func<Symbol, bool> IsInstanceFieldOrEvent = symbol =>
558+
protected static Func<Symbol, bool> IsInstanceFieldOrEventOrProperty = symbol =>
559559
{
560560
if (!symbol.IsStatic)
561561
{
562562
switch (symbol.Kind)
563563
{
564564
case SymbolKind.Field:
565565
case SymbolKind.Event:
566+
case SymbolKind.Property:
566567
return true;
567568
}
568569
}

src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1597,10 +1597,10 @@ private Dictionary<string, ImmutableArray<Symbol>> GetMembersByNameSlow()
15971597
return _lazyMembersDictionary;
15981598
}
15991599

1600-
internal override IEnumerable<Symbol> GetInstanceFieldsAndEvents()
1600+
internal override IEnumerable<Symbol> GetInstanceFieldsAndEventsAndProperties()
16011601
{
16021602
var membersAndInitializers = this.GetMembersAndInitializers();
1603-
return membersAndInitializers.NonTypeMembers.Where(IsInstanceFieldOrEvent);
1603+
return membersAndInitializers.NonTypeMembers.Where(IsInstanceFieldOrEventOrProperty);
16041604
}
16051605

16061606
protected void AfterMembersChecks(BindingDiagnosticBag diagnostics)

src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ internal class SourcePropertyAccessorSymbol : SourceMemberMethodSymbol
2424
private string _lazyName;
2525
private readonly bool _bodyShouldBeSynthesized;
2626
private readonly bool _isExpressionBodied;
27-
private readonly bool _containsFieldKeyword;
27+
private readonly bool _containsFieldIdentifier;
2828
private readonly bool _usesInit;
2929

3030
public static SourcePropertyAccessorSymbol CreateAccessorSymbol(
@@ -113,7 +113,7 @@ public static SourcePropertyAccessorSymbol CreateAccessorSymbol(
113113
// PROTOTYPE(semi-auto-props): Figure out what is going to be more efficient, to go after tokens and then
114114
// checking their parent, or to go after nodes (IdentifierNameSyntax) first and then checking the underlying token.
115115
// 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?
116-
private static bool NodeContainsFieldKeyword(CSharpSyntaxNode? node)
116+
private static bool NodeContainsFieldIdentifier(CSharpSyntaxNode? node)
117117
{
118118
if (node is null)
119119
{
@@ -151,7 +151,7 @@ private SourcePropertyAccessorSymbol(
151151
_property = property;
152152
_bodyShouldBeSynthesized = false;
153153
_isExpressionBodied = true;
154-
_containsFieldKeyword = property.IsIndexer ? false : NodeContainsFieldKeyword(syntax);
154+
_containsFieldIdentifier = !property.IsIndexer && NodeContainsFieldIdentifier(syntax);
155155

156156
// The modifiers for the accessor are the same as the modifiers for the property,
157157
// minus the indexer and readonly bit
@@ -197,7 +197,7 @@ protected SourcePropertyAccessorSymbol(
197197
{
198198
_property = property;
199199
_bodyShouldBeSynthesized = bodyShouldBeSynthesized;
200-
_containsFieldKeyword = property.IsIndexer ? false : NodeContainsFieldKeyword(getAccessorSyntax(syntax));
200+
_containsFieldIdentifier = !property.IsIndexer && NodeContainsFieldIdentifier(getAccessorSyntax(syntax));
201201
Debug.Assert(!_property.IsExpressionBodied, "Cannot have accessors in expression bodied lightweight properties");
202202
_isExpressionBodied = !hasBlockBody && hasExpressionBody;
203203
_usesInit = usesInit;
@@ -459,8 +459,7 @@ internal Accessibility LocalAccessibility
459459
/// This is only calculated from syntax, so we don't know if it
460460
/// will bind to something or will create a backing field.
461461
/// </remarks>
462-
// PROTOTYPE(semi-auto-props): Rename to ContainsFieldKeywordSyntactically or similar.
463-
internal bool ContainsFieldKeyword => _containsFieldKeyword;
462+
internal bool ContainsFieldIdentifier => _containsFieldIdentifier;
464463

465464
/// <summary>
466465
/// Indicates whether this accessor has no body in source and a body will be synthesized by the compiler.

src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ private void EnsureBackingFieldIsSynthesized()
447447

448448
if (this is SourcePropertySymbol propertySymbol)
449449
{
450-
if (propertySymbol.GetMethod is SourcePropertyAccessorSymbol { ContainsFieldKeyword: true } getMethod)
450+
if (propertySymbol.GetMethod is SourcePropertyAccessorSymbol { ContainsFieldIdentifier: true } getMethod)
451451
{
452452
noteAccessorBinding();
453453
var binder = getMethod.TryGetBodyBinder();
@@ -456,7 +456,7 @@ private void EnsureBackingFieldIsSynthesized()
456456

457457
// If we still don't have a backing field after binding the getter, try binding the setter.
458458
if (_lazyBackingFieldSymbol == _lazyBackingFieldSymbolSentinel &&
459-
propertySymbol.SetMethod is SourcePropertyAccessorSymbol { ContainsFieldKeyword: true } setMethod)
459+
propertySymbol.SetMethod is SourcePropertyAccessorSymbol { ContainsFieldIdentifier: true } setMethod)
460460
{
461461
noteAccessorBinding();
462462
setMethod.TryGetBodyBinder()?.BindMethodBody(setMethod.SyntaxNode, BindingDiagnosticBag.Discarded);
@@ -813,7 +813,7 @@ private bool AllowFieldAttributeTarget
813813
{
814814
get
815815
{
816-
// PROTOTYPE(semi-auto-props): Fix implementation for semi auto properties.
816+
// PROTOTYPE(semi-auto-props): Fix implementation for semi auto properties. Consider also the BackingField?.GetAttributes() call in GetAttributesBag. Should it handle FieldKeywordBackingField? Add a test.
817817
return _getMethod?.BodyShouldBeSynthesized == true ||
818818
_setMethod?.BodyShouldBeSynthesized == true;
819819
}
@@ -1626,6 +1626,7 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok
16261626
{
16271627
var diagnostics = BindingDiagnosticBag.GetInstance();
16281628
var conversions = new TypeConversions(this.ContainingAssembly.CorLibrary);
1629+
16291630
foreach (var parameter in this.Parameters)
16301631
{
16311632
parameter.ForceComplete(locationOpt, cancellationToken);

src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,7 @@ SmallDictionary<Symbol, Symbol> computeDefinitionToMemberMap()
10811081

10821082
case SymbolKind.Event:
10831083
var underlyingEvent = (EventSymbol)member;
1084+
// PROTOTYPE(semi-auto-props): Do we need to do something for properties with FieldKeywordBackingField?
10841085
var underlyingAssociatedField = underlyingEvent.AssociatedField;
10851086
// The field is not part of the members list
10861087
if (underlyingAssociatedField is object)

src/Compilers/CSharp/Test/Emit/PDB/PDBWinMdExpTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,34 @@ public int this[int a]
242242
AssertXml.Equal(expected, actual);
243243
}
244244

245+
[ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)]
246+
public void TestWinMdExpData_SemiAutoProperty()
247+
{
248+
var text = @"
249+
public class C
250+
{
251+
public int P { get => field; }
252+
}
253+
";
254+
255+
string expected = @"<?xml version=""1.0"" encoding=""utf-16""?>
256+
<token-map>
257+
<token-location token=""0x02xxxxxx"" file=""source.cs"" start-line=""2"" start-column=""14"" end-line=""2"" end-column=""15""/>
258+
<token-location token=""0x06xxxxxx"" file=""source.cs"" start-line=""2"" start-column=""14"" end-line=""2"" end-column=""15""/>
259+
<token-location token=""0x04xxxxxx"" file=""source.cs"" start-line=""4"" start-column=""16"" end-line=""4"" end-column=""17""/>
260+
<token-location token=""0x17xxxxxx"" file=""source.cs"" start-line=""4"" start-column=""16"" end-line=""4"" end-column=""17""/>
261+
<token-location token=""0x06xxxxxx"" file=""source.cs"" start-line=""4"" start-column=""20"" end-line=""4"" end-column=""23""/>
262+
</token-map>";
263+
264+
var compilation = CreateCompilationWithMscorlib45(
265+
text,
266+
options: TestOptions.ReleaseWinMD,
267+
sourceFileName: "source.cs").VerifyDiagnostics();
268+
269+
string actual = PdbTestUtilities.GetTokenToLocationMap(compilation, true);
270+
AssertXml.Equal(expected, actual);
271+
}
272+
245273
[ConditionalFact(typeof(WindowsOnly), Reason = ConditionalSkipReason.NativePdbRequiresDesktop)]
246274
public void TestWinMdExpData_AnonymousTypes()
247275
{

0 commit comments

Comments
 (0)