Skip to content

Commit

Permalink
Adjust ContainingSymbol for substituted local function
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv committed Nov 4, 2024
1 parent 6217b5c commit 938f2ed
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 10 deletions.
4 changes: 1 addition & 3 deletions src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7132,9 +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
Debug.Assert(current is ErrorMethodSymbol);
return -1;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,9 @@ public sealed override Symbol ContainingSymbol
{
get
{
return _containingType;
return MethodKind is MethodKind.LocalFunction
? OriginalDefinition.ContainingSymbol
: _containingType;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
101 changes: 101 additions & 0 deletions src/Compilers/CSharp/Test/Symbol/Symbols/LocalFunctionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<InvocationExpressionSyntax>(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 t) { }
}
}
""";
var comp = CreateCompilation([source]);
comp.VerifyDiagnostics();

var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var invocation = GetSyntax<InvocationExpressionSyntax>(tree, "local(new C())");
var symbol = model.GetSymbolInfo(invocation).Symbol;
Assert.Equal("void local<C>(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<T1>
{
public void M<T2>()
{
outer<int>();
void outer<T3>()
{
local(42);
void local<T4>(T4 t) { }
}
}
}
""";
var comp = CreateCompilation([source]);
comp.VerifyDiagnostics();

var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var invocation = GetSyntax<InvocationExpressionSyntax>(tree, "local(42)");
var symbol = model.GetSymbolInfo(invocation).Symbol;
Assert.Equal("void local<System.Int32>(System.Int32 t)", symbol.ToTestDisplayString());
Assert.Equal("void outer<T3>()", symbol.ContainingSymbol.ToTestDisplayString());
Assert.Equal("void C<T1>.M<T2>()", symbol.ContainingSymbol.ContainingSymbol.ToTestDisplayString());
}

[Fact]
public void ContainingSymbol_ConstructedMethod()
{
var source = """
C<int>.M<string>();
public class C<T1>
{
public static void M<T2>() { }
}
""";
var comp = CreateCompilation([source]);
comp.VerifyDiagnostics();

var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var invocation = GetSyntax<InvocationExpressionSyntax>(tree, "C<int>.M<string>()");
var symbol = model.GetSymbolInfo(invocation).Symbol;
Assert.Equal("void C<System.Int32>.M<System.String>()", symbol.ToTestDisplayString());
Assert.Equal("C<System.Int32>", symbol.ContainingSymbol.ToTestDisplayString());
}
}
}

0 comments on commit 938f2ed

Please sign in to comment.