Skip to content

Commit 4cd6bfb

Browse files
authored
Extensions: fix issue with paramref (#81269)
Fixes #81217
1 parent b9f26f0 commit 4cd6bfb

File tree

3 files changed

+171
-6
lines changed

3 files changed

+171
-6
lines changed

src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,9 +1175,11 @@ public override Binder VisitXmlNameAttribute(XmlNameAttributeSyntax parent)
11751175
switch (elementKind)
11761176
{
11771177
case XmlNameAttributeElementKind.Parameter:
1178-
case XmlNameAttributeElementKind.ParameterReference:
11791178
extraInfo = NodeUsage.DocumentationCommentParameter;
11801179
break;
1180+
case XmlNameAttributeElementKind.ParameterReference:
1181+
extraInfo = NodeUsage.DocumentationCommentParameterReference;
1182+
break;
11811183
case XmlNameAttributeElementKind.TypeParameter:
11821184
extraInfo = NodeUsage.DocumentationCommentTypeParameter;
11831185
break;
@@ -1236,7 +1238,7 @@ public override Binder VisitXmlNameAttribute(XmlNameAttributeSyntax parent)
12361238
/// </summary>
12371239
private Binder GetParameterNameAttributeValueBinder(MemberDeclarationSyntax memberSyntax, bool isParamRef, Binder nextBinder)
12381240
{
1239-
if (memberSyntax is BaseMethodDeclarationSyntax { ParameterList: { ParameterCount: > 0 } } baseMethodDeclSyntax)
1241+
if (memberSyntax is BaseMethodDeclarationSyntax baseMethodDeclSyntax)
12401242
{
12411243
Binder outerBinder = VisitCore(memberSyntax.Parent);
12421244
MethodSymbol method = GetMethodSymbol(baseMethodDeclSyntax, outerBinder);
@@ -1246,7 +1248,14 @@ private Binder GetParameterNameAttributeValueBinder(MemberDeclarationSyntax memb
12461248
nextBinder = new WithExtensionParameterBinder(method.ContainingType, nextBinder);
12471249
}
12481250

1249-
return new WithParametersBinder(method.Parameters, nextBinder);
1251+
if (method.ParameterCount > 0)
1252+
{
1253+
return new WithParametersBinder(method.Parameters, nextBinder);
1254+
}
1255+
else
1256+
{
1257+
return nextBinder;
1258+
}
12501259
}
12511260
else if (memberSyntax is ExtensionBlockDeclarationSyntax extensionDeclaration)
12521261
{

src/Compilers/CSharp/Portable/Binder/BinderFactory.NodeUsage.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ internal enum NodeUsage : byte
2727
CompilationUnitScriptUsings = 1 << 2,
2828

2929
DocumentationCommentParameter = 1 << 0,
30-
DocumentationCommentTypeParameter = 1 << 1,
31-
DocumentationCommentTypeParameterReference = 1 << 2,
30+
DocumentationCommentParameterReference = 1 << 1,
31+
DocumentationCommentTypeParameter = 1 << 2,
32+
DocumentationCommentTypeParameterReference = 1 << 3,
3233

3334
CrefParameterOrReturnType = 1 << 0,
3435
}

src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs

Lines changed: 156 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51371,6 +51371,10 @@ static class E
5137151371
// (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)
5137251372
// public void M(object o2) => throw null!;
5137351373
Diagnostic(ErrorCode.WRN_MissingParamTag, "o2").WithArguments("o2", "E.extension(object).M(object)").WithLocation(9, 30));
51374+
51375+
var tree = comp.SyntaxTrees.Single();
51376+
var model = comp.GetSemanticModel(tree);
51377+
AssertEx.SequenceEqual(["(o, null)"], PrintXmlNameSymbols(tree, model));
5137451378
}
5137551379

5137651380
[Fact]
@@ -51391,6 +51395,10 @@ static class E
5139151395
""";
5139251396
var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments);
5139351397
comp.VerifyEmitDiagnostics();
51398+
51399+
var tree = comp.SyntaxTrees.Single();
51400+
var model = comp.GetSemanticModel(tree);
51401+
AssertEx.SequenceEqual(["(o, System.Object o)"], PrintXmlNameSymbols(tree, model));
5139451402
}
5139551403

5139651404
[Fact]
@@ -51509,7 +51517,7 @@ static class E
5150951517
/// <summary>Summary for extension block</summary>
5151051518
extension<T>(T t)
5151151519
{
51512-
/// <summary>Summary for M</summary>
51520+
/// <summary>Summary for M <typeparamref name="T"/> </summary>
5151351521
/// <typeparam name="T">Description for T</typeparam>
5151451522
public static void M<U>(U u) => throw null!;
5151551523
}
@@ -51735,6 +51743,86 @@ public static int P4 { set { } }
5173551743
Diagnostic(ErrorCode.WRN_UnmatchedParamRefTag, "value").WithArguments("value", "E.extension(object).P2").WithLocation(8, 53));
5173651744
}
5173751745

51746+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81217")]
51747+
public void XmlDoc_ParamRef_04()
51748+
{
51749+
// No parameter on method
51750+
var src = """
51751+
static class E
51752+
{
51753+
extension(object o)
51754+
{
51755+
/// <returns><paramref name="o"/></returns>
51756+
public object M() => o;
51757+
}
51758+
}
51759+
""";
51760+
var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments,
51761+
assemblyName: "paramref_04");
51762+
comp.VerifyEmitDiagnostics();
51763+
51764+
var tree = comp.SyntaxTrees.Single();
51765+
var model = comp.GetSemanticModel(tree);
51766+
AssertEx.SequenceEqual(["(o, System.Object o)"], PrintXmlNameSymbols(tree, model));
51767+
}
51768+
51769+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81217")]
51770+
public void XmlDoc_ParamRef_05()
51771+
{
51772+
// One parameter on method
51773+
var src = """
51774+
static class E
51775+
{
51776+
extension(object o)
51777+
{
51778+
/// <summary><paramref name="o"/></summary>
51779+
public object M(int i) => o;
51780+
}
51781+
}
51782+
""";
51783+
var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments,
51784+
assemblyName: "paramref_05");
51785+
comp.VerifyEmitDiagnostics();
51786+
51787+
var tree = comp.SyntaxTrees.Single();
51788+
var model = comp.GetSemanticModel(tree);
51789+
AssertEx.SequenceEqual(["(o, System.Object o)"], PrintXmlNameSymbols(tree, model));
51790+
}
51791+
51792+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81217")]
51793+
public void XmlDoc_ParamRef_06()
51794+
{
51795+
// <params> preceeds <paramref>
51796+
var src = """
51797+
using System;
51798+
51799+
static class E
51800+
{
51801+
/// <param name="value">Param value</param>
51802+
extension(ReadOnlySpan<char> value)
51803+
{
51804+
/// <param name="n">Param n</param>
51805+
/// <param name="delimiter">Param delimiter</param>
51806+
/// <returns><paramref name="value"/></returns>
51807+
public ReadOnlySpan<char> GetNthDelimitedItem(int n, ReadOnlySpan<char> delimiter) => throw null !;
51808+
}
51809+
}
51810+
""";
51811+
var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments,
51812+
assemblyName: "paramref_06", targetFramework: TargetFramework.Net100);
51813+
51814+
comp.VerifyEmitDiagnostics();
51815+
51816+
var tree = comp.SyntaxTrees.Single();
51817+
var model = comp.GetSemanticModel(tree);
51818+
AssertEx.SequenceEqual([
51819+
"(value, System.ReadOnlySpan<System.Char> value)",
51820+
"(n, System.Int32 n)",
51821+
"(delimiter, System.ReadOnlySpan<System.Char> delimiter)",
51822+
"(value, System.ReadOnlySpan<System.Char> value)"],
51823+
PrintXmlNameSymbols(tree, model));
51824+
}
51825+
5173851826
[Fact]
5173951827
public void XmlDoc_TypeParamRef_01()
5174051828
{
@@ -51791,6 +51879,73 @@ static class E
5179151879
AssertEx.SequenceEqual(["(T, null)", "(T, T)"], PrintXmlNameSymbols(tree, model));
5179251880
}
5179351881

51882+
[Fact]
51883+
public void XmlDoc_TypeParamRef_03()
51884+
{
51885+
var src = """
51886+
static class E
51887+
{
51888+
/// <typeparam name="T1"/>
51889+
extension<T1>(int)
51890+
{
51891+
/// <summary><typeparamref name="T1"/></summary>
51892+
/// <typeparam name="T2"/>
51893+
public static void M<T2>() => throw null!;
51894+
}
51895+
}
51896+
""";
51897+
var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments);
51898+
comp.VerifyEmitDiagnostics();
51899+
51900+
var tree = comp.SyntaxTrees.Single();
51901+
var model = comp.GetSemanticModel(tree);
51902+
AssertEx.SequenceEqual(["(T1, T1)", "(T1, T1)", "(T2, T2)"], PrintXmlNameSymbols(tree, model));
51903+
}
51904+
51905+
[Fact]
51906+
public void XmlDoc_TypeParamRef_04()
51907+
{
51908+
var src = """
51909+
static class E
51910+
{
51911+
/// <typeparam name="T1"/>
51912+
extension<T1>(int)
51913+
{
51914+
/// <summary><typeparamref name="T1"/></summary>
51915+
public static void M() => throw null!;
51916+
}
51917+
}
51918+
""";
51919+
var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments);
51920+
comp.VerifyEmitDiagnostics();
51921+
51922+
var tree = comp.SyntaxTrees.Single();
51923+
var model = comp.GetSemanticModel(tree);
51924+
AssertEx.SequenceEqual(["(T1, T1)", "(T1, T1)"], PrintXmlNameSymbols(tree, model));
51925+
}
51926+
51927+
[Fact]
51928+
public void XmlDoc_TypeParamRef_05()
51929+
{
51930+
var src = """
51931+
static class E
51932+
{
51933+
/// <typeparam name="T1"/>
51934+
extension<T1>(T1)
51935+
{
51936+
/// <summary><typeparamref name="T1"/></summary>
51937+
public static int Property => throw null!;
51938+
}
51939+
}
51940+
""";
51941+
var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments);
51942+
comp.VerifyEmitDiagnostics();
51943+
51944+
var tree = comp.SyntaxTrees.Single();
51945+
var model = comp.GetSemanticModel(tree);
51946+
AssertEx.SequenceEqual(["(T1, T1)", "(T1, T1)"], PrintXmlNameSymbols(tree, model));
51947+
}
51948+
5179451949
[Fact]
5179551950
public void AnalyzerActions_01()
5179651951
{

0 commit comments

Comments
 (0)