diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index a928565197eda..2996add94a1fe 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -7107,7 +7107,7 @@ private void ApplyMemberPostConditions(BoundExpression? receiverOpt, MethodSymbo // Otherwise, returns -1 private int GetReceiverSlotForMemberPostConditions(MethodSymbol? method) { - if (method is null) + if (method is null or ErrorMethodSymbol) { return -1; } @@ -7132,10 +7132,7 @@ private int GetReceiverSlotForMemberPostConditions(MethodSymbol? method) return GetOrCreateSlot(thisParameter); } - // The ContainingSymbol on a substituted local function is incorrect (returns the containing type instead of the containing method) - // so we can't find the proper `this` receiver to apply the member post-conditions to. - // Tracked by https://github.com/dotnet/roslyn/issues/75543 - return -1; + throw ExceptionUtilities.Unreachable(); } private void ApplyMemberPostConditions(int receiverSlot, MethodSymbol method) diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs index e31108b59eb3e..acf4a31b8a632 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedMethodSymbol.cs @@ -190,7 +190,9 @@ public sealed override Symbol ContainingSymbol { get { - return _containingType; + return MethodKind is MethodKind.LocalFunction + ? OriginalDefinition.ContainingSymbol + : _containingType; } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index dccdb2cf46086..3a4c805f23882 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -21286,13 +21286,7 @@ public void M() } """, MemberNotNullAttributeDefinition]); - // The ContainingSymbol on a substituted local function is incorrect (returns the containing type instead of the containing method) - // so we can't find the proper `this` receiver to apply the member post-conditions to. - // Tracked by https://github.com/dotnet/roslyn/issues/75543 c.VerifyDiagnostics( - // (13,9): warning CS8602: Dereference of a possibly null reference. - // field2.ToString(); - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(13, 9), // (15,9): warning CS8602: Dereference of a possibly null reference. // field4.ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field4").WithLocation(15, 9)); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs index 204624bfe5c6c..fc44f296c1a3d 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; @@ -423,5 +424,105 @@ static string MyExtension(this object o) } """).VerifyEmitDiagnostics(); } + + [Fact] + public void ContainingSymbol_LocalFunction() + { + var source = """ +public class C +{ + public void M() + { + local(); + void local() { } + } +} +"""; + var comp = CreateCompilation([source]); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "local()"); + var symbol = model.GetSymbolInfo(invocation).Symbol; + Assert.Equal("void local()", symbol.ToTestDisplayString()); + Assert.Equal("void C.M()", symbol.ContainingSymbol.ToTestDisplayString()); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75543")] + public void ContainingSymbol_ConstructedLocalFunction() + { + var source = """ +public class C +{ + public void M() + { + local(new C()); + void local(T t) { } + } +} +"""; + var comp = CreateCompilation([source]); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "local(new C())"); + var symbol = model.GetSymbolInfo(invocation).Symbol; + Assert.Equal("void local(C t)", symbol.ToTestDisplayString()); + Assert.Equal("void C.M()", symbol.ContainingSymbol.ToTestDisplayString()); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75543")] + public void ContainingSymbol_ConstructedLocalFunction_Nested() + { + var source = """ +public class C +{ + public void M() + { + outer(); + + void outer() + { + local(42); + void local(T4 t) { } + } + } +} +"""; + var comp = CreateCompilation([source]); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "local(42)"); + var symbol = model.GetSymbolInfo(invocation).Symbol; + Assert.Equal("void local(System.Int32 t)", symbol.ToTestDisplayString()); + Assert.Equal("void outer()", symbol.ContainingSymbol.ToTestDisplayString()); + Assert.Equal("void C.M()", symbol.ContainingSymbol.ContainingSymbol.ToTestDisplayString()); + } + + [Fact] + public void ContainingSymbol_ConstructedMethod() + { + var source = """ +C.M(); + +public class C +{ + public static void M() { } +} +"""; + var comp = CreateCompilation([source]); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "C.M()"); + var symbol = model.GetSymbolInfo(invocation).Symbol; + Assert.Equal("void C.M()", symbol.ToTestDisplayString()); + Assert.Equal("C", symbol.ContainingSymbol.ToTestDisplayString()); + } } }