diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs
index a84a68327cc4c..bec14793b4f3a 100644
--- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs
+++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs
@@ -1175,9 +1175,11 @@ public override Binder VisitXmlNameAttribute(XmlNameAttributeSyntax parent)
switch (elementKind)
{
case XmlNameAttributeElementKind.Parameter:
- case XmlNameAttributeElementKind.ParameterReference:
extraInfo = NodeUsage.DocumentationCommentParameter;
break;
+ case XmlNameAttributeElementKind.ParameterReference:
+ extraInfo = NodeUsage.DocumentationCommentParameterReference;
+ break;
case XmlNameAttributeElementKind.TypeParameter:
extraInfo = NodeUsage.DocumentationCommentTypeParameter;
break;
@@ -1236,7 +1238,7 @@ public override Binder VisitXmlNameAttribute(XmlNameAttributeSyntax parent)
///
private Binder GetParameterNameAttributeValueBinder(MemberDeclarationSyntax memberSyntax, bool isParamRef, Binder nextBinder)
{
- if (memberSyntax is BaseMethodDeclarationSyntax { ParameterList: { ParameterCount: > 0 } } baseMethodDeclSyntax)
+ if (memberSyntax is BaseMethodDeclarationSyntax baseMethodDeclSyntax)
{
Binder outerBinder = VisitCore(memberSyntax.Parent);
MethodSymbol method = GetMethodSymbol(baseMethodDeclSyntax, outerBinder);
@@ -1246,7 +1248,14 @@ private Binder GetParameterNameAttributeValueBinder(MemberDeclarationSyntax memb
nextBinder = new WithExtensionParameterBinder(method.ContainingType, nextBinder);
}
- return new WithParametersBinder(method.Parameters, nextBinder);
+ if (method.ParameterCount > 0)
+ {
+ return new WithParametersBinder(method.Parameters, nextBinder);
+ }
+ else
+ {
+ return nextBinder;
+ }
}
else if (memberSyntax is ExtensionBlockDeclarationSyntax extensionDeclaration)
{
diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.NodeUsage.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.NodeUsage.cs
index 6f97d8ae01491..19f7da4bad93d 100644
--- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.NodeUsage.cs
+++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.NodeUsage.cs
@@ -27,8 +27,9 @@ internal enum NodeUsage : byte
CompilationUnitScriptUsings = 1 << 2,
DocumentationCommentParameter = 1 << 0,
- DocumentationCommentTypeParameter = 1 << 1,
- DocumentationCommentTypeParameterReference = 1 << 2,
+ DocumentationCommentParameterReference = 1 << 1,
+ DocumentationCommentTypeParameter = 1 << 2,
+ DocumentationCommentTypeParameterReference = 1 << 3,
CrefParameterOrReturnType = 1 << 0,
}
diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs
index 9c0dd360ae364..29a536b9b0ae0 100644
--- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs
+++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs
@@ -51327,6 +51327,10 @@ static class E
// (9,30): warning CS1573: Parameter 'o2' has no matching param tag in the XML comment for 'E.extension(object).M(object)' (but other parameters do)
// public void M(object o2) => throw null!;
Diagnostic(ErrorCode.WRN_MissingParamTag, "o2").WithArguments("o2", "E.extension(object).M(object)").WithLocation(9, 30));
+
+ var tree = comp.SyntaxTrees.Single();
+ var model = comp.GetSemanticModel(tree);
+ AssertEx.SequenceEqual(["(o, null)"], PrintXmlNameSymbols(tree, model));
}
[Fact]
@@ -51347,6 +51351,10 @@ static class E
""";
var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments);
comp.VerifyEmitDiagnostics();
+
+ var tree = comp.SyntaxTrees.Single();
+ var model = comp.GetSemanticModel(tree);
+ AssertEx.SequenceEqual(["(o, System.Object o)"], PrintXmlNameSymbols(tree, model));
}
[Fact]
@@ -51465,7 +51473,7 @@ static class E
/// Summary for extension block
extension(T t)
{
- /// Summary for M
+ /// Summary for M
/// Description for T
public static void M(U u) => throw null!;
}
@@ -51691,6 +51699,86 @@ public static int P4 { set { } }
Diagnostic(ErrorCode.WRN_UnmatchedParamRefTag, "value").WithArguments("value", "E.extension(object).P2").WithLocation(8, 53));
}
+ [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81217")]
+ public void XmlDoc_ParamRef_04()
+ {
+ // No parameter on method
+ var src = """
+static class E
+{
+ extension(object o)
+ {
+ ///
+ public object M() => o;
+ }
+}
+""";
+ var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments,
+ assemblyName: "paramref_04");
+ comp.VerifyEmitDiagnostics();
+
+ var tree = comp.SyntaxTrees.Single();
+ var model = comp.GetSemanticModel(tree);
+ AssertEx.SequenceEqual(["(o, System.Object o)"], PrintXmlNameSymbols(tree, model));
+ }
+
+ [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81217")]
+ public void XmlDoc_ParamRef_05()
+ {
+ // One parameter on method
+ var src = """
+static class E
+{
+ extension(object o)
+ {
+ ///
+ public object M(int i) => o;
+ }
+}
+""";
+ var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments,
+ assemblyName: "paramref_05");
+ comp.VerifyEmitDiagnostics();
+
+ var tree = comp.SyntaxTrees.Single();
+ var model = comp.GetSemanticModel(tree);
+ AssertEx.SequenceEqual(["(o, System.Object o)"], PrintXmlNameSymbols(tree, model));
+ }
+
+ [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81217")]
+ public void XmlDoc_ParamRef_06()
+ {
+ // preceeds
+ var src = """
+using System;
+
+static class E
+{
+ /// Param value
+ extension(ReadOnlySpan value)
+ {
+ /// Param n
+ /// Param delimiter
+ ///
+ public ReadOnlySpan GetNthDelimitedItem(int n, ReadOnlySpan delimiter) => throw null !;
+ }
+}
+""";
+ var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments,
+ assemblyName: "paramref_06", targetFramework: TargetFramework.Net100);
+
+ comp.VerifyEmitDiagnostics();
+
+ var tree = comp.SyntaxTrees.Single();
+ var model = comp.GetSemanticModel(tree);
+ AssertEx.SequenceEqual([
+ "(value, System.ReadOnlySpan value)",
+ "(n, System.Int32 n)",
+ "(delimiter, System.ReadOnlySpan delimiter)",
+ "(value, System.ReadOnlySpan value)"],
+ PrintXmlNameSymbols(tree, model));
+ }
+
[Fact]
public void XmlDoc_TypeParamRef_01()
{
@@ -51747,6 +51835,73 @@ static class E
AssertEx.SequenceEqual(["(T, null)", "(T, T)"], PrintXmlNameSymbols(tree, model));
}
+ [Fact]
+ public void XmlDoc_TypeParamRef_03()
+ {
+ var src = """
+static class E
+{
+ ///
+ extension(int)
+ {
+ ///
+ ///
+ public static void M() => throw null!;
+ }
+}
+""";
+ var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments);
+ comp.VerifyEmitDiagnostics();
+
+ var tree = comp.SyntaxTrees.Single();
+ var model = comp.GetSemanticModel(tree);
+ AssertEx.SequenceEqual(["(T1, T1)", "(T1, T1)", "(T2, T2)"], PrintXmlNameSymbols(tree, model));
+ }
+
+ [Fact]
+ public void XmlDoc_TypeParamRef_04()
+ {
+ var src = """
+static class E
+{
+ ///
+ extension(int)
+ {
+ ///
+ public static void M() => throw null!;
+ }
+}
+""";
+ var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments);
+ comp.VerifyEmitDiagnostics();
+
+ var tree = comp.SyntaxTrees.Single();
+ var model = comp.GetSemanticModel(tree);
+ AssertEx.SequenceEqual(["(T1, T1)", "(T1, T1)"], PrintXmlNameSymbols(tree, model));
+ }
+
+ [Fact]
+ public void XmlDoc_TypeParamRef_05()
+ {
+ var src = """
+static class E
+{
+ ///
+ extension(T1)
+ {
+ ///
+ public static int Property => throw null!;
+ }
+}
+""";
+ var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments);
+ comp.VerifyEmitDiagnostics();
+
+ var tree = comp.SyntaxTrees.Single();
+ var model = comp.GetSemanticModel(tree);
+ AssertEx.SequenceEqual(["(T1, T1)", "(T1, T1)"], PrintXmlNameSymbols(tree, model));
+ }
+
[Fact]
public void AnalyzerActions_01()
{