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

Fix symbol display for backing fields #76000

Merged
merged 5 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,15 @@ public override void VisitField(IFieldSymbol symbol)
AddPunctuation(SyntaxKind.DotToken);
}

if (symbol.ContainingType.TypeKind == TypeKind.Enum)
if (!Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)
&& symbol is Symbols.PublicModel.FieldSymbol
&& symbol.AssociatedSymbol is IPropertySymbol associatedProperty)
{
AddPropertyNameAndParameters(associatedProperty);
AddPunctuation(SyntaxKind.DotToken);
AddKeyword(SyntaxKind.FieldKeyword);
RikkiGibson marked this conversation as resolved.
Show resolved Hide resolved
}
else if (symbol.ContainingType.TypeKind == TypeKind.Enum)
{
Builder.Add(CreatePart(SymbolDisplayPartKind.EnumMemberName, symbol, symbol.Name));
}
Expand Down Expand Up @@ -321,7 +329,7 @@ public override void VisitMethod(IMethodSymbol symbol)
// If we're using the metadata format, then include the return type.
// Otherwise we eschew it since it is redundant in a conversion
// signature.
if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames))
if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames))
{
goto default;
}
Expand All @@ -332,7 +340,7 @@ public override void VisitMethod(IMethodSymbol symbol)
// If we're using the metadata format, then include the return type.
// Otherwise we eschew it since it is redundant in a conversion
// signature.
if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) ||
if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) ||
tryGetUserDefinedOperatorTokenKind(symbol.MetadataName) == SyntaxKind.None)
{
goto default;
Expand Down Expand Up @@ -462,7 +470,7 @@ public override void VisitMethod(IMethodSymbol symbol)
// Note: we are using the metadata name also in the case that
// symbol.containingType is null (which should never be the case here) or is an
// anonymous type (which 'does not have a name').
var name = Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) || symbol.ContainingType == null || symbol.ContainingType.IsAnonymousType
var name = Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) || symbol.ContainingType == null || symbol.ContainingType.IsAnonymousType
? symbol.Name
: symbol.ContainingType.Name;

Expand All @@ -476,7 +484,7 @@ public override void VisitMethod(IMethodSymbol symbol)
var partKind = GetPartKindForConstructorOrDestructor(symbol);

// Note: we are using the metadata name also in the case that symbol.containingType is null, which should never be the case here.
if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) || symbol.ContainingType == null)
if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) || symbol.ContainingType == null)
{
Builder.Add(CreatePart(partKind, symbol, symbol.Name));
}
Expand All @@ -491,7 +499,7 @@ public override void VisitMethod(IMethodSymbol symbol)
{
AddExplicitInterfaceIfNeeded(symbol.ExplicitInterfaceImplementations);

if (!Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames) &&
if (!Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) &&
symbol.GetSymbol()?.OriginalDefinition is SourceUserDefinedOperatorSymbolBase sourceUserDefinedOperatorSymbolBase)
{
var operatorName = symbol.MetadataName;
Expand Down Expand Up @@ -520,7 +528,7 @@ public override void VisitMethod(IMethodSymbol symbol)
case MethodKind.UserDefinedOperator:
case MethodKind.BuiltinOperator:
{
if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames))
if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames))
{
Builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, symbol.MetadataName));
}
Expand All @@ -541,7 +549,7 @@ public override void VisitMethod(IMethodSymbol symbol)
}
case MethodKind.Conversion:
{
if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames))
if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames))
{
Builder.Add(CreatePart(SymbolDisplayPartKind.MethodName, symbol, symbol.MetadataName));
}
Expand Down
178 changes: 175 additions & 3 deletions src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1448,7 +1448,7 @@ class C {

var format = new SymbolDisplayFormat(
memberOptions: SymbolDisplayMemberOptions.IncludeType,
compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames);
compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames);

TestSymbolDescription(
text,
Expand Down Expand Up @@ -2049,8 +2049,10 @@ class C
SymbolDisplayPartKind.Punctuation);
}

[Fact]
public void TestPropertyGetAccessor()
[Theory]
[InlineData(SymbolDisplayCompilerInternalOptions.None)]
[InlineData(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)]
internal void TestPropertyGetAccessor(SymbolDisplayCompilerInternalOptions internalOptions)
{
var text = @"
class C {
Expand All @@ -2062,6 +2064,7 @@ class C {
GetMembers("get_P").Single();

var format = new SymbolDisplayFormat(
internalOptions,
memberOptions:
SymbolDisplayMemberOptions.IncludeAccessibility |
SymbolDisplayMemberOptions.IncludeContainingType |
Expand Down Expand Up @@ -2123,6 +2126,175 @@ class C {
SymbolDisplayPartKind.Keyword);
}

[Fact]
public void TestPropertyBackingField()
{
var text = @"
#nullable enable
class C {
string P { get; set; } }
";

Func<NamespaceSymbol, Symbol> findSymbol = global =>
global.GetTypeMembers("C", 0).Single().
GetMembers("<P>k__BackingField").Single();

var format = new SymbolDisplayFormat(
memberOptions:
SymbolDisplayMemberOptions.IncludeAccessibility |
SymbolDisplayMemberOptions.IncludeContainingType |
SymbolDisplayMemberOptions.IncludeExplicitInterface |
SymbolDisplayMemberOptions.IncludeModifiers |
SymbolDisplayMemberOptions.IncludeParameters |
SymbolDisplayMemberOptions.IncludeType);

TestSymbolDescription(
text,
findSymbol,
format,
"private String C.P.field",
SymbolDisplayPartKind.Keyword,
SymbolDisplayPartKind.Space,
SymbolDisplayPartKind.ClassName,
SymbolDisplayPartKind.Space,
SymbolDisplayPartKind.ClassName,
SymbolDisplayPartKind.Punctuation,
SymbolDisplayPartKind.PropertyName,
SymbolDisplayPartKind.Punctuation,
SymbolDisplayPartKind.Keyword);
}

[Fact]
public void TestPropertyBackingFieldFromCompilationReference()
{
var text = @"
#nullable enable
class C {
string P { get; set; } }
";

var format = new SymbolDisplayFormat(
memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeModifiers | SymbolDisplayMemberOptions.IncludeAccessibility | SymbolDisplayMemberOptions.IncludeType,
parameterOptions: SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeDefaultValue,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

var comp1 = CreateCompilation(text);
var comp2 = CreateCompilation("", references: [comp1.ToMetadataReference()], options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All));

var prop = comp2.GetMember<FieldSymbol>("C.<P>k__BackingField").GetPublicSymbol();
var parts = SymbolDisplay.ToDisplayParts(prop, format);

Verify(
parts,
"private string P.field",
SymbolDisplayPartKind.Keyword,
SymbolDisplayPartKind.Space,
SymbolDisplayPartKind.Keyword,
SymbolDisplayPartKind.Space,
SymbolDisplayPartKind.PropertyName,
SymbolDisplayPartKind.Punctuation,
SymbolDisplayPartKind.Keyword);
}

[Fact]
public void TestPropertyBackingField_UseMetadataMethodNames()
{
var text = @"
#nullable enable
class C {
string P { get; set; } }
";

Func<NamespaceSymbol, Symbol> findSymbol = global =>
global.GetTypeMembers("C", 0).Single().
GetMembers("<P>k__BackingField").Single();

var format = new SymbolDisplayFormat(
compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames,
memberOptions:
SymbolDisplayMemberOptions.IncludeAccessibility |
SymbolDisplayMemberOptions.IncludeContainingType |
SymbolDisplayMemberOptions.IncludeExplicitInterface |
SymbolDisplayMemberOptions.IncludeModifiers |
SymbolDisplayMemberOptions.IncludeParameters |
SymbolDisplayMemberOptions.IncludeType);

TestSymbolDescription(
text,
findSymbol,
format,
"private String C.<P>k__BackingField",
SymbolDisplayPartKind.Keyword,
SymbolDisplayPartKind.Space,
SymbolDisplayPartKind.ClassName,
SymbolDisplayPartKind.Space,
SymbolDisplayPartKind.ClassName,
SymbolDisplayPartKind.Punctuation,
SymbolDisplayPartKind.FieldName);
}

[Theory]
[InlineData(SymbolDisplayCompilerInternalOptions.None)]
[InlineData(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)]
internal void TestPropertyBackingFieldFromMetadata(SymbolDisplayCompilerInternalOptions internalOptions)
{
// Metadata symbols do not associate the backing field with the property, so the metadata name is always displayed.
var text = @"
#nullable enable
class C {
string P { get; set; } }
";

var format = new SymbolDisplayFormat(
compilerInternalOptions: internalOptions,
memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeModifiers | SymbolDisplayMemberOptions.IncludeAccessibility | SymbolDisplayMemberOptions.IncludeType,
parameterOptions: SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeDefaultValue,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

var comp1 = CreateCompilation(text);
var comp2 = CreateCompilation("", references: [comp1.EmitToImageReference()], options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All));

var prop = comp2.GetMember<FieldSymbol>("C.<P>k__BackingField").GetPublicSymbol();
var parts = SymbolDisplay.ToDisplayParts(prop, format);

Verify(
parts,
"private string <P>k__BackingField",
SymbolDisplayPartKind.Keyword,
SymbolDisplayPartKind.Space,
SymbolDisplayPartKind.Keyword,
SymbolDisplayPartKind.Space,
SymbolDisplayPartKind.FieldName);
}

[Fact]
public void TestPropertyBackingFieldVB()
{
var text = @"
Class A
Public Property Prop As String
End Class";

var format = new SymbolDisplayFormat(
memberOptions: SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeModifiers | SymbolDisplayMemberOptions.IncludeAccessibility | SymbolDisplayMemberOptions.IncludeType,
parameterOptions: SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeName | SymbolDisplayParameterOptions.IncludeDefaultValue,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

var comp = CreateVisualBasicCompilation("c", text);
var a = (ITypeSymbol)comp.GlobalNamespace.GetMembers("A").Single();
var goo = a.GetMembers("_Prop").Single();
var parts = SymbolDisplay.ToDisplayParts(goo, format);

Verify(
parts,
"private string _Prop",
SymbolDisplayPartKind.Keyword,
SymbolDisplayPartKind.Space,
SymbolDisplayPartKind.Keyword,
SymbolDisplayPartKind.Space,
SymbolDisplayPartKind.FieldName);
}

[Fact]
public void TestMemberEventAll()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1076,11 +1076,11 @@ record C
AssertEx.Equal(new[] {
"System.Type! C.EqualityContract.get",
"System.Type! C.EqualityContract { get; }",
"System.Int32 C.<X>k__BackingField",
"System.Int32 C.X.field",
"System.Int32 C.X { get; init; }",
"System.Int32 C.X.get",
"void C.X.init",
"System.String! C.<Y>k__BackingField",
"System.String! C.Y.field",
"System.String! C.Y { get; init; }",
"System.String! C.Y.get",
"void C.Y.init",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public ImmutableDictionary<string, MethodData> GetMethodsByName()

private static readonly SymbolDisplayFormat _testDataKeyFormat = new SymbolDisplayFormat(
compilerInternalOptions:
SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames |
SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames |
SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes,
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ internal enum SymbolDisplayCompilerInternalOptions
None = 0,

/// <summary>
/// ".ctor" instead of "Goo"
/// - ".ctor" instead of "Goo"
/// - "&lt;Prop&gt;k__backingField" instead of "Prop.field" (for C# backing fields)
/// </summary>
UseMetadataMethodNames = 1 << 0,
UseMetadataMemberNames = 1 << 0,

/// <summary>
/// "List`1" instead of "List&lt;T&gt;" ("List(of T)" in VB). Overrides GenericsOptions on
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ public class SymbolDisplayFormat
SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier,
compilerInternalOptions:
SymbolDisplayCompilerInternalOptions.IncludeScriptType |
SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames |
SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames |
SymbolDisplayCompilerInternalOptions.FlagMissingMetadataTypes |
SymbolDisplayCompilerInternalOptions.IncludeCustomModifiers |
SymbolDisplayCompilerInternalOptions.IncludeContainingFileForFileTypes);
Expand Down Expand Up @@ -257,7 +257,7 @@ public class SymbolDisplayFormat
miscellaneousOptions:
SymbolDisplayMiscellaneousOptions.UseSpecialTypes |
SymbolDisplayMiscellaneousOptions.ExpandValueTuple,
compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMethodNames);
compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames);

/// <summary>
/// Used to normalize explicit interface implementation member names.
Expand Down
Loading