diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs index fee4dd5258e00..0800ea7252b7d 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs @@ -2169,6 +2169,7 @@ public static ExpressionSyntax GetStandaloneExpression(ExpressionSyntax expressi case SyntaxKind.IndexerMemberCref: case SyntaxKind.OperatorMemberCref: case SyntaxKind.ConversionOperatorMemberCref: + case SyntaxKind.ExtensionMemberCref: case SyntaxKind.ArrayType: case SyntaxKind.NullableType: // Adjustment may be required. @@ -2225,8 +2226,8 @@ public static ExpressionSyntax GetStandaloneExpression(ExpressionSyntax expressi if (((NameMemberCrefSyntax)parent).Name == node) { CSharpSyntaxNode? grandparent = parent.Parent; - return grandparent != null && grandparent.Kind() == SyntaxKind.QualifiedCref - ? grandparent + return grandparent != null && grandparent is CrefSyntax + ? GetStandaloneNode(grandparent) : parent; } @@ -2240,6 +2241,14 @@ public static ExpressionSyntax GetStandaloneExpression(ExpressionSyntax expressi break; + case SyntaxKind.ExtensionMemberCref: + if (((ExtensionMemberCrefSyntax)parent).Member == node) + { + return GetStandaloneNode(parent); + } + + break; + case SyntaxKind.ArrayCreationExpression: if (((ArrayCreationExpressionSyntax)parent).Type == node) { diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 31ecb9f18fbcb..bd186ec37c15c 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -7160,6 +7160,122 @@ class Nested Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M()").WithArguments("extension(int).M()").WithLocation(10, 28)); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81710")] + public void Cref_68() + { + var src = """ +/// +static class E +{ + extension(int i) + { + public void M(string s) => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var extensionCref = GetSyntax(tree, "extension(int).M(string)", descendIntoTrivia: true); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal("E.extension(int).M(string)", model.GetSymbolInfo(extensionCref).Symbol.ToDisplayString()); + AssertEx.Equal("E.extension(int).M(string)", model.GetSymbolInfo(extensionCref.Member).Symbol.ToDisplayString()); + + var m = ((NameMemberCrefSyntax)extensionCref.Member).Name; + Assert.Equal("M", m.ToString()); + AssertEx.Equal("E.extension(int).M(string)", model.GetSymbolInfo(m).Symbol.ToDisplayString()); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81710")] + public void Cref_69() + { + var src = """ +/// +static class E +{ + extension(int i) + { + public int Property => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var extensionCref = GetSyntax(tree, "extension(int).Property", descendIntoTrivia: true); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal("E.extension(int).Property", model.GetSymbolInfo(extensionCref).Symbol.ToDisplayString()); + AssertEx.Equal("E.extension(int).Property", model.GetSymbolInfo(extensionCref.Member).Symbol.ToDisplayString()); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81710")] + public void Cref_70() + { + var src = """ +namespace N; + +/// +static class E +{ + extension(int i) + { + public void M(string s) => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var qualifiedCref = GetSyntax(tree, "N.E.extension(int).M(string)", descendIntoTrivia: true); + AssertEx.Equal("N.E.extension(int).M(string)", model.GetSymbolInfo(qualifiedCref).Symbol.ToDisplayString()); + + var extensionCref = GetSyntax(tree, "extension(int).M(string)", descendIntoTrivia: true); + AssertEx.Equal("N.E.extension(int).M(string)", model.GetSymbolInfo(extensionCref).Symbol.ToDisplayString()); + AssertEx.Equal("N.E.extension(int).M(string)", model.GetSymbolInfo(extensionCref.Member).Symbol.ToDisplayString()); + + var m = ((NameMemberCrefSyntax)extensionCref.Member).Name; + Assert.Equal("M", m.ToString()); + AssertEx.Equal("N.E.extension(int).M(string)", model.GetSymbolInfo(m).Symbol.ToDisplayString()); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81710")] + public void Cref_71() + { + var src = """ +/// +extension(int i) +{ + public void M(string s) => throw null!; +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).M(string)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M(string)").WithArguments("extension(int).M(string)").WithLocation(1, 16), + // (2,1): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(int i) + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(2, 1), + // (4,17): warning CS1591: Missing XML comment for publicly visible type or member 'extension(int).M(string)' + // public void M(string s) => throw null!; + Diagnostic(ErrorCode.WRN_MissingXMLComment, "M").WithArguments("extension(int).M(string)").WithLocation(4, 17)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var extensionCref = GetSyntax(tree, "extension(int).M(string)", descendIntoTrivia: true); + Assert.Null(model.GetSymbolInfo(extensionCref).Symbol); + Assert.Null(model.GetSymbolInfo(extensionCref.Member).Symbol); + + var m = ((NameMemberCrefSyntax)extensionCref.Member).Name; + Assert.Null(model.GetSymbolInfo(m).Symbol); + } + [Fact] public void PropertyAccess_Set_01() { diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 7ee467dae91c7..f4a8747d3a820 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -1295,16 +1295,16 @@ namespace System.Runtime.CompilerServices; public class RuntimeAsyncMethodGenerationAttribute(bool runtimeAsync) : Attribute(); """; - protected static T GetSyntax(SyntaxTree tree, string text) + protected static T GetSyntax(SyntaxTree tree, string text, bool descendIntoTrivia = false) where T : notnull { - return GetSyntaxes(tree, text).Single(); + return GetSyntaxes(tree, text, descendIntoTrivia).Single(); } - protected static IEnumerable GetSyntaxes(SyntaxTree tree, string text) + protected static IEnumerable GetSyntaxes(SyntaxTree tree, string text, bool descendIntoTrivia = false) where T : notnull { - return tree.GetRoot().DescendantNodes().OfType().Where(e => e.ToString() == text); + return tree.GetRoot().DescendantNodes(descendIntoTrivia: descendIntoTrivia).OfType().Where(e => e.ToString() == text); } protected static CSharpCompilationOptions WithNullableEnable(CSharpCompilationOptions? options = null) diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ExtensionSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ExtensionSymbols.vb index 5fb1e547c049e..25f19516c50dc 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ExtensionSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ExtensionSymbols.vb @@ -246,6 +246,27 @@ public static class E Await TestAPIAndFeature(input, kind, host) End Function + + + Public Async Function FindReferences_ExtensionBlockMethod_Cref(kind As TestKind, host As TestHost) As Task + Dim input = + + + +/// <see cref="E.extension(int).[|M|]()"/> +public static class E +{ + extension(int i) + { + public void {|Definition:$$M|}() { } + } +} + + + + Await TestAPIAndFeature(input, kind, host) + End Function + Public Async Function FindReferences_ExtensionBlockProperty(kind As TestKind, host As TestHost) As Task @@ -429,6 +450,12 @@ class C7 { _ = new C() { [|P|] = 1 }; } +} + + +/// <see cref="E.extension(C).[|P|]"/> +class C8 +{ } @@ -445,6 +472,27 @@ public static class E Await TestAPIAndFeature(input, kind, host) End Function + + + Public Async Function FindReferences_ExtensionBlockProperty_FromAccess_Cref(kind As TestKind, host As TestHost) As Task + Dim input = + + + +/// <see cref="E.extension(int).[|P|]"/> +public static class E +{ + extension(int i) + { + public int {|Definition:$$P|} { get => i; set { } } + } +} + + + + Await TestAPIAndFeature(input, kind, host) + End Function + Public Async Function FindReferences_ExtensionBlockProperty_FromImplementationMethodInvocation(kind As TestKind, host As TestHost) As Task @@ -688,5 +736,25 @@ public static class E Await TestAPIAndFeature(input, kind, host) End Function + + Public Async Function FindReferences_ExtensionBlockMethod_GenericCref(kind As TestKind, host As TestHost) As Task + Dim input = + + + +/// <see cref="E.extension{$${|Definition:U|}}([|U|]).M([|U|])"/> +public static class E +{ + extension<T>(T t1) + { + public void M(T t2) { } + } +} + + + + Await TestAPIAndFeature(input, kind, host) + End Function + End Class End Namespace