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

Handle field reference from semantic model in non-field-backed property #75904

Merged
merged 3 commits into from
Nov 14, 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 @@ -1465,9 +1465,12 @@ private BoundExpression BindFieldExpression(FieldExpressionSyntax node, BindingD
}
}

// Field will be null when binding a field expression in a speculative
// semantic model when the property does not have a backing field.
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 14, 2024

Choose a reason for hiding this comment

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

in a speculative semantic model when the property does not have a backing field.

Consider if we could assert the fact. At least partially. #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

It wasn't obvious what to assert here since the Binder is not specific to the semantic model (IsSemanticModelBinder == false), even when called from that code path.

if (field is null)
{
throw ExceptionUtilities.UnexpectedValue(ContainingMember());
diagnostics.Add(ErrorCode.ERR_NoSuchMember, node, ContainingMember(), "field");
return BadExpression(node);
}

var implicitReceiver = field.IsStatic ? null : ThisReference(node, field.ContainingType, wasCompilerGenerated: true);
Expand Down
68 changes: 68 additions & 0 deletions src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10464,6 +10464,74 @@ .locals init (System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V
VerifyMergedProperties(actualProperties, actualFields);
}

[WorkItem("https://github.com/dotnet/roslyn/issues/75893")]
[Theory]
[CombinatorialData]
public void SpeculativeSemanticModel_01(bool includeLocal)
{
string source = $$"""
class C
{
object P
{
get
{
{{(includeLocal ? "object field = null;" : "")}}
return null;
}
}
}
""";

var parseOptions = TestOptions.RegularPreview;
var comp = CreateCompilation(source, parseOptions: parseOptions);
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var previousAccessor = tree.GetRoot().DescendantNodes().OfType<AccessorDeclarationSyntax>().Single();

var modifiedTree = SyntaxFactory.ParseSyntaxTree(source.Replace("return null;", "return field;"), parseOptions);
var modifiedAccessor = modifiedTree.GetRoot().DescendantNodes().OfType<AccessorDeclarationSyntax>().Single();
Assert.True(model.TryGetSpeculativeSemanticModelForMethodBody(previousAccessor.Body.SpanStart, modifiedAccessor, out var speculativeModel));
var expr = modifiedAccessor.DescendantNodes().OfType<FieldExpressionSyntax>().Single();
Assert.Equal("return field;", expr.Parent.ToString());
var symbolInfo = speculativeModel.GetSymbolInfo(expr);
Assert.Null(symbolInfo.Symbol);
}

[Theory]
[CombinatorialData]
public void SpeculativeSemanticModel_02(bool includeLocal)
{
string source = $$"""
class C
{
object P
{
get
{
{{(includeLocal ? "object field = null;" : "")}}
return null;
}
set;
}
}
""";

var parseOptions = TestOptions.RegularPreview;
var comp = CreateCompilation(source, parseOptions: parseOptions);
var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var previousAccessor = tree.GetRoot().DescendantNodes().OfType<AccessorDeclarationSyntax>().First();

var modifiedTree = SyntaxFactory.ParseSyntaxTree(source.Replace("return null;", "return field;"), parseOptions);
var modifiedAccessor = modifiedTree.GetRoot().DescendantNodes().OfType<AccessorDeclarationSyntax>().First();
Assert.True(model.TryGetSpeculativeSemanticModelForMethodBody(previousAccessor.Body.SpanStart, modifiedAccessor, out var speculativeModel));
var expr = modifiedAccessor.DescendantNodes().OfType<FieldExpressionSyntax>().Single();
Assert.Equal("return field;", expr.Parent.ToString());
var symbolInfo = speculativeModel.GetSymbolInfo(expr);
Assert.Equal("System.Object C.<P>k__BackingField", symbolInfo.Symbol.ToTestDisplayString());
}

[Theory]
[InlineData("{ get; }")]
[InlineData("{ get; set; }")]
Expand Down