Skip to content

Commit c8903b3

Browse files
committed
Updated SA1609
1 parent e024ed7 commit c8903b3

5 files changed

Lines changed: 157 additions & 8 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1609UnitTests.cs

Lines changed: 108 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.CodeFixes;
1113
using Microsoft.CodeAnalysis.Diagnostics;
1214
using TestHelper;
@@ -281,6 +283,112 @@ public int Property
281283
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
282284
}
283285

286+
/// <summary>
287+
/// Verifies that included property documentation will be accepted.
288+
/// </summary>
289+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
290+
[Fact]
291+
public async Task TestPropertyWithValidIncludeAsync()
292+
{
293+
var testCode = @"
294+
public class ClassName
295+
{
296+
/// <include file='PropertyWithValue.xml' path='/ClassName/Property/*'/>
297+
public int Property
298+
{
299+
get;
300+
}
301+
}";
302+
303+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
304+
}
305+
306+
/// <summary>
307+
/// Verifies that included property documentation without a value tag will be flagged.
308+
/// </summary>
309+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
310+
[Fact]
311+
public async Task TestPropertyWithoutValueInIncludeAsync()
312+
{
313+
var testCode = @"
314+
public class ClassName
315+
{
316+
/// <include file='PropertyWithoutValue.xml' path='/ClassName/Property/*'/>
317+
public int Property
318+
{
319+
get;
320+
}
321+
}";
322+
var expected = this.CSharpDiagnostic().WithLocation(5, 16);
323+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
324+
}
325+
326+
/// <summary>
327+
/// Verifies that included property documentation containing &gt;inheritdoc/&lt; will be accepted.
328+
/// </summary>
329+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
330+
[Fact]
331+
public async Task TestPropertyWithInheritdocInIncludeAsync()
332+
{
333+
var testCode = @"
334+
public interface ITestInterface
335+
{
336+
/// <summary>
337+
/// Gets the test property value.
338+
/// </summary>
339+
/// <value>Test number.</value>
340+
int Property { get; }
341+
}
342+
343+
public class ClassName : ITestInterface
344+
{
345+
/// <include file='PropertyWithInheritdoc.xml' path='/ClassName/Property/*'/>
346+
public int Property
347+
{
348+
get;
349+
}
350+
}";
351+
352+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
353+
}
354+
355+
protected override Project ApplyCompilationOptions(Project project)
356+
{
357+
var resolver = new TestXmlReferenceResolver();
358+
359+
string contentWithValue = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
360+
<ClassName>
361+
<Property>
362+
<summary>Foo</summary>
363+
<value>Bar</value>
364+
</Property>
365+
</ClassName>
366+
";
367+
resolver.XmlReferences.Add("PropertyWithValue.xml", contentWithValue);
368+
369+
string contentWithoutValue = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
370+
<ClassName>
371+
<Property>
372+
<summary>Foo</summary>
373+
</Property>
374+
</ClassName>
375+
";
376+
resolver.XmlReferences.Add("PropertyWithoutValue.xml", contentWithoutValue);
377+
378+
string contentWithInheritdocValue = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
379+
<ClassName>
380+
<Property>
381+
<inheritdoc/>
382+
</Property>
383+
</ClassName>
384+
";
385+
resolver.XmlReferences.Add("PropertyWithInheritdoc.xml", contentWithInheritdocValue);
386+
387+
project = base.ApplyCompilationOptions(project);
388+
project = project.WithCompilationOptions(project.CompilationOptions.WithXmlReferenceResolver(resolver));
389+
return project;
390+
}
391+
284392
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
285393
{
286394
yield return new SA1609PropertyDocumentationMustHaveValue();

StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/PropertyDocumentationBase.cs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
namespace StyleCop.Analyzers.DocumentationRules
55
{
66
using System;
7+
using System.Linq;
8+
using System.Xml.Linq;
79
using Microsoft.CodeAnalysis;
810
using Microsoft.CodeAnalysis.CSharp;
911
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -43,8 +45,12 @@ public override void Initialize(AnalysisContext context)
4345
/// <param name="context">The current analysis context.</param>
4446
/// <param name="syntax">The <see cref="XmlElementSyntax"/> or <see cref="XmlEmptyElementSyntax"/> of the node
4547
/// to examine.</param>
48+
/// <param name="completeDocumentation">The complete documentation for the declared symbol, with any
49+
/// <c>&lt;include&gt;</c> elements expanded. If the XML documentation comment included a <c>&lt;summary&gt;</c>
50+
/// element, this value will be <see langword="null"/>, even if the XML documentation comment also included an
51+
/// <c>&lt;include&gt;</c> element.</param>
4652
/// <param name="diagnosticLocation">The location where diagnostics, if any, should be reported.</param>
47-
protected abstract void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, Location diagnosticLocation);
53+
protected abstract void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, XElement completeDocumentation, Location diagnosticLocation);
4854

4955
private void HandlePropertyDeclaration(SyntaxNodeAnalysisContext context)
5056
{
@@ -72,8 +78,26 @@ private void HandleDeclaration(SyntaxNodeAnalysisContext context, SyntaxNode nod
7278
return;
7379
}
7480

75-
var valueXmlElement = documentation.Content.GetFirstXmlElement(this.XmlTagToHandle);
76-
this.HandleXmlElement(context, valueXmlElement, location);
81+
XElement completeDocumentation = null;
82+
83+
var relevantXmlElement = documentation.Content.GetFirstXmlElement(this.XmlTagToHandle);
84+
if (relevantXmlElement == null)
85+
{
86+
relevantXmlElement = documentation.Content.GetFirstXmlElement(XmlCommentHelper.IncludeXmlTag);
87+
if (relevantXmlElement != null)
88+
{
89+
var declaration = context.SemanticModel.GetDeclaredSymbol(node, context.CancellationToken);
90+
var rawDocumentation = declaration?.GetDocumentationCommentXml(expandIncludes: true, cancellationToken: context.CancellationToken);
91+
completeDocumentation = XElement.Parse(rawDocumentation, LoadOptions.None);
92+
if (completeDocumentation.Nodes().OfType<XElement>().Any(element => element.Name == XmlCommentHelper.InheritdocXmlTag))
93+
{
94+
// Ignore nodes with an <inheritdoc/> tag in the included XML.
95+
return;
96+
}
97+
}
98+
}
99+
100+
this.HandleXmlElement(context, relevantXmlElement, completeDocumentation, location);
77101
}
78102
}
79103
}

StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/PropertySummaryDocumentationAnalyzer.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace StyleCop.Analyzers.DocumentationRules
55
{
66
using System;
77
using System.Collections.Immutable;
8+
using System.Xml.Linq;
89
using Microsoft.CodeAnalysis;
910
using Microsoft.CodeAnalysis.CSharp;
1011
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -63,7 +64,7 @@ internal class PropertySummaryDocumentationAnalyzer : PropertyDocumentationBase
6364
protected override string XmlTagToHandle => XmlCommentHelper.SummaryXmlTag;
6465

6566
/// <inheritdoc/>
66-
protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, Location diagnosticLocation)
67+
protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, XElement completeDocumentation, Location diagnosticLocation)
6768
{
6869
var propertyDeclaration = (PropertyDeclarationSyntax)context.Node;
6970
var propertyType = context.SemanticModel.GetTypeInfo(propertyDeclaration.Type);

StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/SA1609PropertyDocumentationMustHaveValue.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
namespace StyleCop.Analyzers.DocumentationRules
55
{
66
using System.Collections.Immutable;
7+
using System.Linq;
8+
using System.Xml.Linq;
79
using Microsoft.CodeAnalysis;
810
using Microsoft.CodeAnalysis.CSharp.Syntax;
911
using Microsoft.CodeAnalysis.Diagnostics;
@@ -47,12 +49,25 @@ internal class SA1609PropertyDocumentationMustHaveValue : PropertyDocumentationB
4749
protected override string XmlTagToHandle => XmlCommentHelper.ValueXmlTag;
4850

4951
/// <inheritdoc/>
50-
protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, Location diagnosticLocation)
52+
protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, XElement completeDocumentation, Location diagnosticLocation)
5153
{
52-
if (syntax == null)
54+
if (completeDocumentation != null)
5355
{
54-
context.ReportDiagnostic(Diagnostic.Create(Descriptor, diagnosticLocation));
56+
var hasValueTag = completeDocumentation.Nodes().OfType<XElement>().Any(element => element.Name == XmlCommentHelper.ValueXmlTag);
57+
if (hasValueTag)
58+
{
59+
return;
60+
}
5561
}
62+
else
63+
{
64+
if (syntax != null)
65+
{
66+
return;
67+
}
68+
}
69+
70+
context.ReportDiagnostic(Diagnostic.Create(Descriptor, diagnosticLocation));
5671
}
5772
}
5873
}

StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/SA1610PropertyDocumentationMustHaveValueText.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
namespace StyleCop.Analyzers.DocumentationRules
55
{
66
using System.Collections.Immutable;
7+
using System.Xml.Linq;
78
using Helpers;
89
using Microsoft.CodeAnalysis;
910
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -47,7 +48,7 @@ internal class SA1610PropertyDocumentationMustHaveValueText : PropertyDocumentat
4748
protected override string XmlTagToHandle => XmlCommentHelper.ValueXmlTag;
4849

4950
/// <inheritdoc/>
50-
protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, Location diagnosticLocation)
51+
protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, XmlNodeSyntax syntax, XElement completeDocumentation, Location diagnosticLocation)
5152
{
5253
if (syntax != null && XmlCommentHelper.IsConsideredEmpty(syntax))
5354
{

0 commit comments

Comments
 (0)