From 146f4ac8dbbcdbd036b73c553cc238b8d7825662 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 4 Nov 2024 09:56:03 -0800 Subject: [PATCH] Adjust semantic model for method group conversion --- .../Compilation/CSharpSemanticModel.cs | 8 +- .../Test/Symbol/Symbols/ConversionTests.cs | 107 ++++++++++++++++++ 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index c3405df8f8be7..42c71e95c7398 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -4294,11 +4294,15 @@ private OneOrMany GetMethodGroupSemanticSymbols( // we want to get the symbol that overload resolution chose for M, not the whole method group M. var conversion = (BoundConversion)boundNodeForSyntacticParent; + while (conversion.ConversionKind is not ConversionKind.MethodGroup && conversion.Operand is BoundConversion nestedConversion) + { + Debug.Assert(conversion.ConversionKind is ConversionKind.NoConversion || conversion.ExplicitCastInCode); + conversion = nestedConversion; + } + var method = conversion.SymbolOpt; if ((object)method != null) { - Debug.Assert(conversion.ConversionKind == ConversionKind.MethodGroup); - if (conversion.IsExtensionMethod) { method = ReducedExtensionMethodSymbol.Create(method); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs index c461c0a0129c9..1bf8644bd0d43 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs @@ -13,6 +13,7 @@ using Roslyn.Test.Utilities; using Xunit; using Basic.Reference.Assemblies; +using Microsoft.CodeAnalysis.Test.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols { @@ -425,6 +426,112 @@ static void Main() Assert.True(conversion.IsNumeric); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36377")] + public void GetSymbolInfo_ExplicitCastOnMethodGroup() + { + var src = """ +public sealed class C +{ + public static void M() + { + C x = (C)C.Test; + } + + public static int Test() => 1; + + public static explicit operator C(System.Func intDelegate) + { + return new C(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Equal("System.Int32 C.Test()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36377")] + public void GetSymbolInfo_TwoExplicitCastsOnMethodGroup() + { + var src = """ +public sealed class C +{ + public static void M() + { + D x = (D)(C)C.Test; + } + + public static int Test() => 1; + + public static explicit operator C(System.Func intDelegate) => throw null; +} +public sealed class D +{ + public static explicit operator D(C c) => throw null; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Equal("System.Int32 C.Test()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36377")] + public void GetSymbolInfo_NoConversion() + { + var src = """ +public sealed class C +{ + public static void M() + { + int x = C.Test; + } + + public static int Test() => 1; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,19): error CS0428: Cannot convert method group 'Test' to non-delegate type 'int'. Did you intend to invoke the method? + // int x = C.Test; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "Test").WithArguments("Test", "int").WithLocation(5, 19)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36377")] + public void GetSymbolInfo_MethodGroupConversion() + { + var src = """ +public sealed class C +{ + public static void M() + { + System.Func x = C.Test; + } + + public static int Test() => 1; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Test"); + Assert.Equal("System.Int32 C.Test()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + #region "Diagnostics" [Fact] public void VarianceRelationFail()