Skip to content
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
115 changes: 115 additions & 0 deletions src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2164,6 +2164,121 @@ class C {
SymbolDisplayPartKind.Keyword);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77219")]
public void TestPropertyBackingField_Minimal_01()
{
var text = @"
#nullable enable
class C {
string P { get; set; } }
";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

raw string plz

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh. this is a compiler test. up to you.


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no better way to do this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't an API on the IPropertySymbol that gives us the backing field, for example, so I don't think there are particularly better ways to do this in general.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe there's an issue asking for this. The ide has wanted such an associated symbol API since forever :-)

Note, what you can do instead is get all the fields and they get the single one associated with the property of interest.


var format = new SymbolDisplayFormat(
memberOptions:
SymbolDisplayMemberOptions.IncludeExplicitInterface);

TestSymbolDescription(
text,
findSymbol,
format,
"P.field",
SymbolDisplayPartKind.PropertyName,
SymbolDisplayPartKind.Punctuation,
SymbolDisplayPartKind.Keyword);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77219")]
public void TestPropertyBackingField_Minimal_02()
{
var text = @"
#nullable enable

interface I {
string P { get; set; }
}

class C : I {
string I.P { get; set; } }
";

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

var format = new SymbolDisplayFormat(
memberOptions:
SymbolDisplayMemberOptions.IncludeExplicitInterface);

TestSymbolDescription(
text,
findSymbol,
format,
"I.P.field",
SymbolDisplayPartKind.InterfaceName,
SymbolDisplayPartKind.Punctuation,
SymbolDisplayPartKind.PropertyName,
SymbolDisplayPartKind.Punctuation,
SymbolDisplayPartKind.Keyword);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77219")]
public void TestPropertyBackingField_Minimal_03()
{
var text = @"
#nullable enable

interface I {
string P { get; set; }
}

class C : I {
string I.P { get; set; } }
";

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

var format = new SymbolDisplayFormat();

TestSymbolDescription(
text,
findSymbol,
format,
"P.field",
SymbolDisplayPartKind.PropertyName,
SymbolDisplayPartKind.Punctuation,
SymbolDisplayPartKind.Keyword);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77219")]
public void TestOrdinaryField_Minimal()
{
var text = @"
#nullable enable

class C {
string F;
";

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

var format = new SymbolDisplayFormat();

TestSymbolDescription(
text,
findSymbol,
format,
"F",
SymbolDisplayPartKind.FieldName);
}

[Fact]
public void TestPropertyBackingFieldFromCompilationReference()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7998,6 +7998,44 @@ void N()
NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s")));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77219")]
public async Task NullableBackingFieldThatIsMaybeNull()
{
await TestWithOptionsAsync(TestOptions.RegularPreview,
"""
#nullable enable

class X
{
string? P
{
get => $$field;
}
}
""",
MainDescription($"({FeaturesResources.field}) string? X.P.field"),
NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "P.field")));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77219")]
public async Task NullableBackingFieldThatIsNotNull()
{
await TestWithOptionsAsync(TestOptions.RegularPreview,
"""
#nullable enable

class X
{
string P
{
get => $$field;
} = "a";
}
""",
MainDescription($"({FeaturesResources.field}) string X.P.field"),
NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "P.field")));
}

[Fact]
public async Task NullablePropertyThatIsMaybeNull()
{
Expand Down
9 changes: 7 additions & 2 deletions src/Features/Core/Portable/QuickInfo/QuickInfoUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ namespace Microsoft.CodeAnalysis.QuickInfo;

internal static class QuickInfoUtilities
{
/// <summary>
/// Display variable name only.
/// </summary>
private static readonly SymbolDisplayFormat s_nullableDisplayFormat = new SymbolDisplayFormat();

public static Task<QuickInfoItem> CreateQuickInfoItemAsync(SolutionServices services, SemanticModel semanticModel, TextSpan span, ImmutableArray<ISymbol> symbols, SymbolDescriptionOptions options, CancellationToken cancellationToken)
=> CreateQuickInfoItemAsync(services, semanticModel, span, symbols, supportedPlatforms: null, showAwaitReturn: false, flowState: NullableFlowState.None, options, onTheFlyDocsInfo: null, cancellationToken);

Expand Down Expand Up @@ -136,8 +141,8 @@ public static async Task<QuickInfoItem> CreateQuickInfoItemAsync(

var nullableMessage = flowState switch
{
NullableFlowState.MaybeNull => string.Format(FeaturesResources._0_may_be_null_here, symbol.Name),
NullableFlowState.NotNull => string.Format(FeaturesResources._0_is_not_null_here, symbol.Name),
NullableFlowState.MaybeNull => string.Format(FeaturesResources._0_may_be_null_here, symbol.ToDisplayString(s_nullableDisplayFormat)),
NullableFlowState.NotNull => string.Format(FeaturesResources._0_is_not_null_here, symbol.ToDisplayString(s_nullableDisplayFormat)),
_ => null
};

Expand Down