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