Skip to content

Commit a123f02

Browse files
committed
Update SA1612 to suppress messages when documentation for an element is optional
Fixes #2452
1 parent 48d6e70 commit a123f02

2 files changed

Lines changed: 130 additions & 14 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1612UnitTests.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ namespace StyleCop.Analyzers.Test.DocumentationRules
1818
/// </summary>
1919
public class SA1612UnitTests : DiagnosticVerifier
2020
{
21+
private string currentTestSettings;
22+
2123
public static IEnumerable<object[]> Declarations
2224
{
2325
get
@@ -81,6 +83,68 @@ public class ClassName
8183
await this.VerifyCSharpDiagnosticAsync(testCode.Replace("$$", declaration), EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
8284
}
8385

86+
[Theory]
87+
[MemberData(nameof(Declarations))]
88+
[WorkItem(2452, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2452")]
89+
public async Task TestMemberWithMissingNotRequiredParamAsync(string declaration)
90+
{
91+
var testCode = @"
92+
/// <summary>
93+
/// Foo
94+
/// </summary>
95+
public class ClassName
96+
{
97+
/// <summary>
98+
/// Foo
99+
/// </summary>
100+
///<param name=""foo"">Test</param>
101+
///<param name=""new"">Test</param>
102+
$$
103+
}";
104+
105+
this.currentTestSettings = @"
106+
{
107+
""settings"": {
108+
""documentationRules"": {
109+
""documentExposedElements"": false
110+
}
111+
}
112+
}
113+
";
114+
await this.VerifyCSharpDiagnosticAsync(testCode.Replace("$$", declaration), EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
115+
}
116+
117+
[Theory]
118+
[MemberData(nameof(Declarations))]
119+
[WorkItem(2452, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2452")]
120+
public async Task TestMemberWithMissingNotRequiredReorderedParamAsync(string declaration)
121+
{
122+
var testCode = @"
123+
/// <summary>
124+
/// Foo
125+
/// </summary>
126+
public class ClassName
127+
{
128+
/// <summary>
129+
/// Foo
130+
/// </summary>
131+
///<param name=""new"">Test</param>
132+
///<param name=""foo"">Test</param>
133+
$$
134+
}";
135+
136+
var diagnostic = this.CSharpDiagnostic()
137+
.WithMessageFormat("The parameter documentation for '{0}' should be at position {1}.");
138+
139+
var expected = new[]
140+
{
141+
diagnostic.WithLocation(10, 21).WithArguments("new", 3),
142+
diagnostic.WithLocation(11, 21).WithArguments("foo", 1),
143+
};
144+
145+
await this.VerifyCSharpDiagnosticAsync(testCode.Replace("$$", declaration), expected, CancellationToken.None).ConfigureAwait(false);
146+
}
147+
84148
[Theory]
85149
[MemberData(nameof(Declarations))]
86150
public async Task TestMemberWithInvalidParamsAsync(string declaration)
@@ -301,6 +365,27 @@ public class ClassName
301365
};
302366

303367
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
368+
369+
// This is even reported if the documentation is not required, except that no warning is reported for the
370+
// first param element (which is actually the last parameter) since it would otherwise be allowed to skip
371+
// the documentation for the first two parameters.
372+
this.currentTestSettings = @"
373+
{
374+
""settings"": {
375+
""documentationRules"": {
376+
""documentExposedElements"": false
377+
}
378+
}
379+
}
380+
";
381+
382+
expected = new[]
383+
{
384+
diagnostic.WithLocation(8, 22).WithArguments("bar", 1),
385+
diagnostic.WithLocation(8, 22).WithArguments("new", 2),
386+
};
387+
388+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
304389
}
305390

306391
[Fact]
@@ -428,6 +513,11 @@ protected override Project ApplyCompilationOptions(Project project)
428513
return project;
429514
}
430515

516+
protected override string GetSettings()
517+
{
518+
return this.currentTestSettings ?? base.GetSettings();
519+
}
520+
431521
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
432522
{
433523
yield return new SA1612ElementParameterDocumentationMustMatchElementParameters();

StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/SA1612ElementParameterDocumentationMustMatchElementParameters.cs

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,27 @@ protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, bool
9797
{
9898
context.ReportDiagnostic(Diagnostic.Create(MissingParameterDescriptor, location ?? identifierLocation, nameAttributeText));
9999
}
100-
else if (parentParameters.Length <= index || parentParameters[index] != parentParameter)
100+
else
101101
{
102-
context.ReportDiagnostic(
103-
Diagnostic.Create(
104-
OrderDescriptor,
105-
location ?? identifierLocation,
106-
nameAttributeText,
107-
parentParameters.IndexOf(parentParameter) + 1));
102+
if (!needsComment)
103+
{
104+
// Parameter documentation is allowed to be omitted, so skip parameters for which there is no
105+
// documentation.
106+
while (index < parentParameters.Length && parentParameters[index] != parentParameter)
107+
{
108+
index++;
109+
}
110+
}
111+
112+
if (parentParameters.Length <= index || parentParameters[index] != parentParameter)
113+
{
114+
context.ReportDiagnostic(
115+
Diagnostic.Create(
116+
OrderDescriptor,
117+
location ?? identifierLocation,
118+
nameAttributeText,
119+
parentParameters.IndexOf(parentParameter) + 1));
120+
}
108121
}
109122

110123
index++;
@@ -155,14 +168,27 @@ protected override void HandleCompleteDocumentation(SyntaxNodeAnalysisContext co
155168
{
156169
context.ReportDiagnostic(Diagnostic.Create(MissingParameterDescriptor, identifierLocation, nameAttributeText));
157170
}
158-
else if (parentParameters.Length <= index || parentParameters[index] != parentParameter)
171+
else
159172
{
160-
context.ReportDiagnostic(
161-
Diagnostic.Create(
162-
OrderDescriptor,
163-
identifierLocation,
164-
nameAttributeText,
165-
parentParameters.IndexOf(parentParameter) + 1));
173+
if (!needsComment)
174+
{
175+
// Parameter documentation is allowed to be omitted, so skip parameters for which there is no
176+
// documentation.
177+
while (index < parentParameters.Length && parentParameters[index] != parentParameter)
178+
{
179+
index++;
180+
}
181+
}
182+
183+
if (parentParameters.Length <= index || parentParameters[index] != parentParameter)
184+
{
185+
context.ReportDiagnostic(
186+
Diagnostic.Create(
187+
OrderDescriptor,
188+
identifierLocation,
189+
nameAttributeText,
190+
parentParameters.IndexOf(parentParameter) + 1));
191+
}
166192
}
167193

168194
index++;

0 commit comments

Comments
 (0)