From 95ac0db952a70e199a129d2b99be61303221a90d Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:28:53 -0800 Subject: [PATCH 1/3] Handle field reference from semantic model in non-field-backed property --- .../Portable/Binder/Binder_Expressions.cs | 5 +++- .../CSharp/Test/Emit3/FieldKeywordTests.cs | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 9e19db67c51ab..248134bfb8d59 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -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. 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); diff --git a/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs b/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs index fb7367886e267..341a31e90a63e 100644 --- a/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs @@ -10464,6 +10464,32 @@ .locals init (System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V VerifyMergedProperties(actualProperties, actualFields); } + [WorkItem("https://github.com/dotnet/roslyn/issues/75893")] + [Fact] + public void SpeculativeSemanticModel() + { + string source = """ + class C + { + object P { get { 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().Single(); + + var modifiedTree = SyntaxFactory.ParseSyntaxTree(source.Replace("return null;", "return field;"), parseOptions); + var modifiedAccessor = modifiedTree.GetRoot().DescendantNodes().OfType().Single(); + Assert.True(model.TryGetSpeculativeSemanticModelForMethodBody(previousAccessor.Body.SpanStart, modifiedAccessor, out var speculativeModel)); + var expr = modifiedAccessor.DescendantNodes().OfType().Single(); + Assert.Equal("return field;", expr.Parent.ToString()); + var symbolInfo = speculativeModel.GetSymbolInfo(expr); + Assert.Null(symbolInfo.Symbol); + } + [Theory] [InlineData("{ get; }")] [InlineData("{ get; set; }")] From 51da949a506cf84b46680f628269a07c9eaae4a7 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:30:41 -0800 Subject: [PATCH 2/3] Update test --- .../CSharp/Test/Emit3/FieldKeywordTests.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs b/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs index 341a31e90a63e..1c52da36ef55f 100644 --- a/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs @@ -10465,13 +10465,21 @@ .locals init (System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V } [WorkItem("https://github.com/dotnet/roslyn/issues/75893")] - [Fact] - public void SpeculativeSemanticModel() + [Theory] + [CombinatorialData] + public void SpeculativeSemanticModel(bool includeLocal) { - string source = """ + string source = $$""" class C { - object P { get { return null; } } + object P + { + get + { + {{(includeLocal ? "object field = null;" : "")}} + return null; + } + } } """; From e66b853bb11427875ef239988a48b3aac441af8b Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Thu, 14 Nov 2024 08:22:02 -0800 Subject: [PATCH 3/3] Add test with existing backing field --- .../CSharp/Test/Emit3/FieldKeywordTests.cs | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs b/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs index 1c52da36ef55f..d95d92200b540 100644 --- a/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs @@ -10467,7 +10467,7 @@ .locals init (System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V [WorkItem("https://github.com/dotnet/roslyn/issues/75893")] [Theory] [CombinatorialData] - public void SpeculativeSemanticModel(bool includeLocal) + public void SpeculativeSemanticModel_01(bool includeLocal) { string source = $$""" class C @@ -10498,6 +10498,40 @@ object P 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().First(); + + var modifiedTree = SyntaxFactory.ParseSyntaxTree(source.Replace("return null;", "return field;"), parseOptions); + var modifiedAccessor = modifiedTree.GetRoot().DescendantNodes().OfType().First(); + Assert.True(model.TryGetSpeculativeSemanticModelForMethodBody(previousAccessor.Body.SpanStart, modifiedAccessor, out var speculativeModel)); + var expr = modifiedAccessor.DescendantNodes().OfType().Single(); + Assert.Equal("return field;", expr.Parent.ToString()); + var symbolInfo = speculativeModel.GetSymbolInfo(expr); + Assert.Equal("System.Object C.

k__BackingField", symbolInfo.Symbol.ToTestDisplayString()); + } + [Theory] [InlineData("{ get; }")] [InlineData("{ get; set; }")]