Skip to content

Commit 54a37e3

Browse files
authored
Merge pull request #3777 from SapiensAnatis/feature/SA-1611-primary-ctor
Fix SA1611 not triggering on class primary constructors
2 parents b88cf52 + 7e20fa1 commit 54a37e3

File tree

4 files changed

+176
-2
lines changed

4 files changed

+176
-2
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp12/DocumentationRules/SA1611CSharp12UnitTests.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,109 @@
33

44
namespace StyleCop.Analyzers.Test.CSharp12.DocumentationRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.Testing;
69
using StyleCop.Analyzers.Test.CSharp11.DocumentationRules;
10+
using Xunit;
11+
using static StyleCop.Analyzers.Test.Verifiers.CustomDiagnosticVerifier<StyleCop.Analyzers.DocumentationRules.SA1611ElementParametersMustBeDocumented>;
712

813
public partial class SA1611CSharp12UnitTests : SA1611CSharp11UnitTests
914
{
15+
public static TheoryData<string> NonRecordDeclarationKeywords { get; } = new TheoryData<string>() { "class", "struct" };
16+
17+
[Theory]
18+
[MemberData(nameof(NonRecordDeclarationKeywords))]
19+
[WorkItem(3770, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3770")]
20+
public async Task TestPrimaryConstructorMissingParametersAsync(string keyword)
21+
{
22+
var testCode = $@"
23+
/// <summary>
24+
/// Type.
25+
/// </summary>
26+
public {keyword} C(int {{|#0:param1|}}, string {{|#1:param2|}}) {{ }}";
27+
28+
DiagnosticResult[] expectedResults = new[]
29+
{
30+
Diagnostic().WithLocation(0).WithArguments("param1"),
31+
Diagnostic().WithLocation(1).WithArguments("param2"),
32+
};
33+
34+
await VerifyCSharpDiagnosticAsync(testCode, expectedResults, CancellationToken.None).ConfigureAwait(false);
35+
}
36+
37+
[Theory]
38+
[MemberData(nameof(NonRecordDeclarationKeywords))]
39+
[WorkItem(3770, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3770")]
40+
public async Task TestPrimaryConstructorPartiallyMissingParametersAsync(string keyword)
41+
{
42+
var testCode = $@"
43+
/// <summary>
44+
/// Type.
45+
/// </summary>
46+
/// <param name=""param1"">Parameter one.</param>
47+
public {keyword} C(int param1, string {{|#0:param2|}}) {{ }}";
48+
49+
await VerifyCSharpDiagnosticAsync(testCode, new[] { Diagnostic().WithLocation(0).WithArguments("param2") }, CancellationToken.None).ConfigureAwait(false);
50+
}
51+
52+
[Theory]
53+
[MemberData(nameof(NonRecordDeclarationKeywords))]
54+
[WorkItem(3770, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3770")]
55+
public async Task TestPrimaryConstructorNoMissingParametersAsync(string keyword)
56+
{
57+
var testCode = $@"
58+
/// <summary>
59+
/// Type.
60+
/// </summary>
61+
/// <param name=""param1"">Parameter one.</param>
62+
/// <param name=""param2"">Parameter two.</param>
63+
public {keyword} C(int param1, string param2) {{ }}";
64+
65+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
66+
}
67+
68+
[Theory]
69+
[MemberData(nameof(NonRecordDeclarationKeywords))]
70+
[WorkItem(3770, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3770")]
71+
public async Task TestPrimaryConstructorIncludeMissingParametersAsync(string keyword)
72+
{
73+
var testCode = $@"
74+
/// <include file='MissingClassDocumentation.xml' path='/TestType/*' />
75+
public {keyword} C(int {{|#0:param1|}}, string {{|#1:param2|}}, bool {{|#2:param3|}}) {{ }}";
76+
77+
DiagnosticResult[] expectedResults = new[]
78+
{
79+
Diagnostic().WithLocation(0).WithArguments("param1"),
80+
Diagnostic().WithLocation(1).WithArguments("param2"),
81+
Diagnostic().WithLocation(2).WithArguments("param3"),
82+
};
83+
84+
await VerifyCSharpDiagnosticAsync(testCode, expectedResults, CancellationToken.None).ConfigureAwait(false);
85+
}
86+
87+
[Theory]
88+
[MemberData(nameof(NonRecordDeclarationKeywords))]
89+
[WorkItem(3770, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3770")]
90+
public async Task TestPrimaryConstructorIncludePartiallyMissingParametersAsync(string keyword)
91+
{
92+
var testCode = $@"
93+
/// <include file='WithPartialClassDocumentation.xml' path='/TestType/*' />
94+
public {keyword} C(int {{|#0:param1|}}, string param2, bool param3) {{ }}";
95+
96+
await VerifyCSharpDiagnosticAsync(testCode, new[] { Diagnostic().WithLocation(0).WithArguments("param1") }, CancellationToken.None).ConfigureAwait(false);
97+
}
98+
99+
[Theory]
100+
[MemberData(nameof(NonRecordDeclarationKeywords))]
101+
[WorkItem(3770, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3770")]
102+
public async Task TestPrimaryConstructorIncludeNoMissingParametersAsync(string keyword)
103+
{
104+
var testCode = $@"
105+
/// <include file='WithClassDocumentation.xml' path='/TestType/*' />
106+
public {keyword} C(int param1, string param2, bool param3) {{ }}";
107+
108+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
109+
}
10110
}
11111
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/DocumentationRules/SA1611CSharp9UnitTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,39 @@
33

44
namespace StyleCop.Analyzers.Test.CSharp9.DocumentationRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.Testing;
69
using StyleCop.Analyzers.Test.CSharp8.DocumentationRules;
10+
using StyleCop.Analyzers.Test.Helpers;
11+
using Xunit;
712

813
public partial class SA1611CSharp9UnitTests : SA1611CSharp8UnitTests
914
{
15+
[Theory]
16+
[MemberData(nameof(CommonMemberData.RecordTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
17+
[WorkItem(3770, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3770")]
18+
public async Task TestPrimaryRecordConstructorMissingParametersAsync(string keyword)
19+
{
20+
var testCode = $@"
21+
/// <summary>
22+
/// Record.
23+
/// </summary>
24+
public {keyword} R(int Param1, string Param2);";
25+
26+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
27+
}
28+
29+
[Theory]
30+
[MemberData(nameof(CommonMemberData.RecordTypeDeclarationKeywords), MemberType = typeof(CommonMemberData))]
31+
[WorkItem(3770, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3770")]
32+
public async Task TestPrimaryRecordConstructorIncludeMissingParametersAsync(string keyword)
33+
{
34+
var testCode = $@"
35+
/// <include file='MissingClassDocumentation.xml' path='/TestType/*' />
36+
public {keyword} R(int Param1, string Param2);";
37+
38+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
39+
}
1040
}
1141
}

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1611UnitTests.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ public void TestMethod(string param1, string param2, string param3)
414414
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
415415
}
416416

417-
private static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult[] expected, CancellationToken cancellationToken)
417+
protected static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult[] expected, CancellationToken cancellationToken)
418418
{
419419
string contentWithoutElementDocumentation = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
420420
<TestClass>
@@ -455,6 +455,32 @@ private static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult[
455455
</TestMethod>
456456
</TestClass>
457457
";
458+
string classWithoutElementDocumentation = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
459+
<TestType>
460+
<summary>
461+
Foo
462+
</summary>
463+
</TestType>
464+
";
465+
string classWithPartialElementDocumentation = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
466+
<TestType>
467+
<summary>
468+
Foo
469+
</summary>
470+
<param name=""param2"">Param 2</param>
471+
<param name=""param3"">Param 3</param>
472+
</TestType>
473+
";
474+
string classWithElementDocumentation = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
475+
<TestType>
476+
<summary>
477+
Foo
478+
</summary>
479+
<param name=""param1"">Param 1</param>
480+
<param name=""param2"">Param 2</param>
481+
<param name=""param3"">Param 3</param>
482+
</TestType>
483+
";
458484

459485
var test = new StyleCopDiagnosticVerifier<SA1611ElementParametersMustBeDocumented>.CSharpTest
460486
{
@@ -465,6 +491,9 @@ private static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult[
465491
{ "WithElementDocumentation.xml", contentWithElementDocumentation },
466492
{ "WithPartialElementDocumentation.xml", contentWithPartialElementDocumentation },
467493
{ "InheritedDocumentation.xml", contentWithInheritedDocumentation },
494+
{ "MissingClassDocumentation.xml", classWithoutElementDocumentation },
495+
{ "WithClassDocumentation.xml", classWithElementDocumentation },
496+
{ "WithPartialClassDocumentation.xml", classWithPartialElementDocumentation },
468497
},
469498
};
470499

StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/SA1611ElementParametersMustBeDocumented.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ namespace StyleCop.Analyzers.DocumentationRules
1010
using System.Linq;
1111
using System.Xml.Linq;
1212
using Microsoft.CodeAnalysis;
13+
using Microsoft.CodeAnalysis.CSharp;
1314
using Microsoft.CodeAnalysis.CSharp.Syntax;
1415
using Microsoft.CodeAnalysis.Diagnostics;
1516
using StyleCop.Analyzers.Helpers;
17+
using StyleCop.Analyzers.Lightup;
1618
using StyleCop.Analyzers.Settings.ObjectModel;
1719

1820
/// <summary>
@@ -60,6 +62,12 @@ protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, Styl
6062
}
6163

6264
var node = context.Node;
65+
if (node.IsKind(SyntaxKindEx.RecordDeclaration) || node.IsKind(SyntaxKindEx.RecordStructDeclaration))
66+
{
67+
// Record parameters are covered by SA1600 instead.
68+
return;
69+
}
70+
6371
var parameterList = GetParameters(node);
6472
if (parameterList == null)
6573
{
@@ -84,6 +92,12 @@ protected override void HandleCompleteDocumentation(SyntaxNodeAnalysisContext co
8492
}
8593

8694
var node = context.Node;
95+
if (node.IsKind(SyntaxKindEx.RecordDeclaration) || node.IsKind(SyntaxKindEx.RecordStructDeclaration))
96+
{
97+
// Record parameters are covered by SA1600 instead.
98+
return;
99+
}
100+
87101
var parameterList = GetParameters(node);
88102
if (parameterList == null)
89103
{
@@ -106,7 +120,8 @@ private static IEnumerable<ParameterSyntax> GetParameters(SyntaxNode node)
106120
{
107121
return (node as BaseMethodDeclarationSyntax)?.ParameterList?.Parameters
108122
?? (node as IndexerDeclarationSyntax)?.ParameterList?.Parameters
109-
?? (node as DelegateDeclarationSyntax)?.ParameterList?.Parameters;
123+
?? (node as DelegateDeclarationSyntax)?.ParameterList?.Parameters
124+
?? (node as TypeDeclarationSyntax)?.ParameterList()?.Parameters;
110125
}
111126

112127
private static void ReportMissingParameters(SyntaxNodeAnalysisContext context, IEnumerable<ParameterSyntax> parameterList, IEnumerable<string> documentationParameterNames)

0 commit comments

Comments
 (0)