Skip to content

Commit d92b483

Browse files
committed
Merge pull request #2021 from nbarbettini/element-docs-include
Update element documentation diagnostics to handle included documentation
2 parents ede601c + 5db7b46 commit d92b483

19 files changed

Lines changed: 1721 additions & 251 deletions

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/DocumentationRules/SA1615SA1616CodeFixProvider.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,13 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
7575
DocumentationCommentTriviaSyntax documentationComment =
7676
methodDeclarationSyntax?.GetDocumentationCommentTriviaSyntax()
7777
?? delegateDeclarationSyntax?.GetDocumentationCommentTriviaSyntax();
78-
if (documentationComment == null)
78+
bool canIgnoreDocumentation =
79+
documentationComment == null
80+
|| documentationComment.Content
81+
.Where(x => x is XmlElementSyntax || x is XmlEmptyElementSyntax)
82+
.All(x => string.Equals(x.GetName()?.ToString(), XmlCommentHelper.IncludeXmlTag));
83+
84+
if (canIgnoreDocumentation)
7985
{
8086
return document;
8187
}

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1608UnitTests.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ namespace StyleCop.Analyzers.Test.DocumentationRules
77
using System.Threading;
88
using System.Threading.Tasks;
99
using Analyzers.DocumentationRules;
10+
using Helpers;
11+
using Microsoft.CodeAnalysis;
1012
using Microsoft.CodeAnalysis.Diagnostics;
1113
using TestHelper;
1214
using Xunit;
@@ -142,6 +144,77 @@ public class ClassName
142144
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
143145
}
144146

147+
[Fact]
148+
public async Task TestClassWithIncludedEmptyDocumentationAsync()
149+
{
150+
var testCode = @"
151+
/// <include file='ClassWithoutSummary.xml' path='/ClassName/*' />
152+
public class ClassName
153+
{
154+
}";
155+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
156+
}
157+
158+
[Fact]
159+
public async Task TestClassWithIncludedSummaryDocumentationAsync()
160+
{
161+
var testCode = @"
162+
/// <include file='ClassWithSummary.xml' path='/ClassName/*' />
163+
public class ClassName
164+
{
165+
}";
166+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
167+
}
168+
169+
[Fact]
170+
public async Task TestClassWithIncludedDefaultSummaryDocumentationAsync()
171+
{
172+
var testCode = @"
173+
/// <include file='ClassWithDefaultSummary.xml' path='/ClassName/*' />
174+
public class ClassName
175+
{
176+
}";
177+
178+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(3, 14);
179+
180+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
181+
}
182+
183+
/// <inheritdoc/>
184+
protected override Project ApplyCompilationOptions(Project project)
185+
{
186+
var resolver = new TestXmlReferenceResolver();
187+
188+
string contentWithoutSummary = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
189+
<ClassName>
190+
</ClassName>
191+
";
192+
resolver.XmlReferences.Add("ClassWithoutSummary.xml", contentWithoutSummary);
193+
194+
string contentWithSummary = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
195+
<ClassName>
196+
<summary>
197+
Foo
198+
</summary>
199+
</ClassName>
200+
";
201+
resolver.XmlReferences.Add("ClassWithSummary.xml", contentWithSummary);
202+
203+
string contentWithDefaultSummary = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
204+
<ClassName>
205+
<summary>
206+
Summary description for the ClassName class.
207+
</summary>
208+
</ClassName>
209+
";
210+
resolver.XmlReferences.Add("ClassWithDefaultSummary.xml", contentWithDefaultSummary);
211+
212+
project = base.ApplyCompilationOptions(project);
213+
project = project.WithCompilationOptions(project.CompilationOptions.WithXmlReferenceResolver(resolver));
214+
return project;
215+
}
216+
217+
/// <inheritdoc/>
145218
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
146219
{
147220
yield return new SA1608ElementDocumentationMustNotHaveDefaultSummary();

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

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ namespace StyleCop.Analyzers.Test.DocumentationRules
77
using System.Threading;
88
using System.Threading.Tasks;
99
using Analyzers.DocumentationRules;
10+
using Helpers;
11+
using Microsoft.CodeAnalysis;
1012
using Microsoft.CodeAnalysis.Diagnostics;
1113
using TestHelper;
1214
using Xunit;
@@ -228,6 +230,120 @@ public static explicit operator TestClass(int value)
228230
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
229231
}
230232

233+
/// <summary>
234+
/// Verifies that included documentation with valid documentation does not produce diagnostics.
235+
/// </summary>
236+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
237+
[Fact]
238+
public async Task VerifyIncludedDocumentationAsync()
239+
{
240+
var testCode = @"
241+
/// <summary>
242+
/// Foo
243+
/// </summary>
244+
public class ClassName
245+
{
246+
/// <include file='WithElementDocumentation.xml' path='/TestClass/TestMethod/*' />
247+
public void TestMethod(string param1, string param2, string param3)
248+
{
249+
}
250+
}";
251+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
252+
}
253+
254+
/// <summary>
255+
/// Verifies that included documentation with missing elements produces the expected diagnostics.
256+
/// </summary>
257+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
258+
[Fact]
259+
public async Task VerifyIncludedDocumentationMissingElementsAsync()
260+
{
261+
var testCode = @"
262+
/// <summary>
263+
/// Foo
264+
/// </summary>
265+
public class ClassName
266+
{
267+
/// <include file='MissingElementDocumentation.xml' path='/TestClass/TestMethod/*' />
268+
public void TestMethod(string param1, string param2, string param3)
269+
{
270+
}
271+
}";
272+
DiagnosticResult[] expected =
273+
{
274+
this.CSharpDiagnostic().WithLocation(8, 35).WithArguments("param1"),
275+
this.CSharpDiagnostic().WithLocation(8, 50).WithArguments("param2"),
276+
this.CSharpDiagnostic().WithLocation(8, 65).WithArguments("param3"),
277+
};
278+
279+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
280+
}
281+
282+
/// <summary>
283+
/// Verifies that included documentation with an <c>&lt;inheritdoc&gt;</c> tag is ignored.
284+
/// </summary>
285+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
286+
[Fact]
287+
public async Task VerifyIncludedInheritedDocumentationAsync()
288+
{
289+
var testCode = @"
290+
/// <summary>
291+
/// Foo
292+
/// </summary>
293+
public class ClassName
294+
{
295+
/// <include file='InheritedDocumentation.xml' path='/TestClass/TestMethod/*' />
296+
public void TestMethod(string param1, string param2, string param3)
297+
{
298+
}
299+
}";
300+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
301+
}
302+
303+
/// <inheritdoc/>
304+
protected override Project ApplyCompilationOptions(Project project)
305+
{
306+
var resolver = new TestXmlReferenceResolver();
307+
308+
string contentWithoutElementDocumentation = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
309+
<TestClass>
310+
<TestMethod>
311+
<summary>
312+
Foo
313+
</summary>
314+
</TestMethod>
315+
</TestClass>
316+
";
317+
resolver.XmlReferences.Add("MissingElementDocumentation.xml", contentWithoutElementDocumentation);
318+
319+
string contentWithElementDocumentation = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
320+
<TestClass>
321+
<TestMethod>
322+
<summary>
323+
Foo
324+
</summary>
325+
<param name=""param1"">Param 1</param>
326+
<param name=""param2"">Param 2</param>
327+
<param name=""param3"">Param 3</param>
328+
</TestMethod>
329+
</TestClass>
330+
";
331+
resolver.XmlReferences.Add("WithElementDocumentation.xml", contentWithElementDocumentation);
332+
333+
string contentWithInheritedDocumentation = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
334+
<TestClass>
335+
<TestMethod>
336+
<inheritdoc />
337+
</TestMethod>
338+
</TestClass>
339+
";
340+
resolver.XmlReferences.Add("InheritedDocumentation.xml", contentWithInheritedDocumentation);
341+
342+
project = base.ApplyCompilationOptions(project);
343+
project = project.WithCompilationOptions(project.CompilationOptions.WithXmlReferenceResolver(resolver));
344+
return project;
345+
}
346+
231347
/// <inheritdoc/>
232348
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
233349
{

0 commit comments

Comments
 (0)