Skip to content

Commit c14bf31

Browse files
authored
Merge pull request #2864 from sharwell/allow-para
Allow property standard summary text to use paragraph elements
2 parents f684a86 + 3a12f8f commit c14bf31

4 files changed

Lines changed: 134 additions & 2 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/DocumentationRules/PropertySummaryDocumentationCodeFixProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
6262
var documentation = node.GetDocumentationCommentTriviaSyntax();
6363

6464
var summaryElement = (XmlElementSyntax)documentation.Content.GetFirstXmlElement(XmlCommentHelper.SummaryXmlTag);
65-
var textElement = (XmlTextSyntax)summaryElement.Content.FirstOrDefault();
65+
var textElement = XmlCommentHelper.TryGetFirstTextElementWithContent(summaryElement);
6666
if (textElement == null)
6767
{
6868
return document;

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1623UnitTests.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,106 @@ public int Property
168168
await VerifyCSharpFixAsync(testCode, expected, testCode, CancellationToken.None).ConfigureAwait(false);
169169
}
170170

171+
[Fact]
172+
[WorkItem(1934, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1934")]
173+
public async Task SummaryInParagraphIsAllowedAsync()
174+
{
175+
var testCode = @"public class ClassName
176+
{
177+
/// <summary><para>Gets the first test value.</para></summary>
178+
public int Property1
179+
{
180+
get;
181+
}
182+
183+
/// <summary>
184+
/// <para>Gets the second test value.</para>
185+
/// </summary>
186+
public int Property2
187+
{
188+
get;
189+
}
190+
191+
/// <summary>
192+
/// <para>
193+
/// Gets the third test value.
194+
/// </para>
195+
/// </summary>
196+
public int Property3
197+
{
198+
get;
199+
}
200+
}";
201+
202+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
203+
}
204+
205+
[Fact]
206+
[WorkItem(1934, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1934")]
207+
public async Task SummaryInParagraphCanBeFixedAsync()
208+
{
209+
var testCode = @"public class ClassName
210+
{
211+
/// <summary><para>Gets the first test value.</para></summary>
212+
public int Property1
213+
{
214+
set { }
215+
}
216+
217+
/// <summary>
218+
/// <para>Gets the second test value.</para>
219+
/// </summary>
220+
public int Property2
221+
{
222+
set { }
223+
}
224+
225+
/// <summary>
226+
/// <para>
227+
/// Gets the third test value.
228+
/// </para>
229+
/// </summary>
230+
public int Property3
231+
{
232+
set { }
233+
}
234+
}";
235+
var fixedTestCode = @"public class ClassName
236+
{
237+
/// <summary><para>Sets the first test value.</para></summary>
238+
public int Property1
239+
{
240+
set { }
241+
}
242+
243+
/// <summary>
244+
/// <para>Sets the second test value.</para>
245+
/// </summary>
246+
public int Property2
247+
{
248+
set { }
249+
}
250+
251+
/// <summary>
252+
/// <para>
253+
/// Sets the third test value.
254+
/// </para>
255+
/// </summary>
256+
public int Property3
257+
{
258+
set { }
259+
}
260+
}";
261+
262+
DiagnosticResult[] expected =
263+
{
264+
Diagnostic(PropertySummaryDocumentationAnalyzer.SA1623Descriptor).WithLocation(4, 16).WithArguments("Sets"),
265+
Diagnostic(PropertySummaryDocumentationAnalyzer.SA1623Descriptor).WithLocation(12, 16).WithArguments("Sets"),
266+
Diagnostic(PropertySummaryDocumentationAnalyzer.SA1623Descriptor).WithLocation(22, 16).WithArguments("Sets"),
267+
};
268+
await VerifyCSharpFixAsync(testCode, expected, fixedTestCode, CancellationToken.None).ConfigureAwait(false);
269+
}
270+
171271
/// <summary>
172272
/// Verifies that an incorrect summary tag with a known prefix will be fixed correctly.
173273
/// </summary>

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ private static void AnalyzeSummaryElement(SyntaxNodeAnalysisContext context, Xml
126126
diagnosticProperties.Add(NoCodeFixKey, string.Empty);
127127
}
128128

129-
var text = !(summaryElement.Content.FirstOrDefault() is XmlTextSyntax textElement) ? string.Empty : XmlCommentHelper.GetText(textElement, true).TrimStart();
129+
var textElement = XmlCommentHelper.TryGetFirstTextElementWithContent(summaryElement);
130+
string text = textElement is null ? string.Empty : XmlCommentHelper.GetText(textElement, normalizeWhitespace: true).TrimStart();
130131

131132
bool prefixIsGetsOrSets = text.StartsWith(startingTextGetsOrSets, StringComparison.OrdinalIgnoreCase);
132133
bool prefixIsGets = !prefixIsGetsOrSets && text.StartsWith(startingTextGets, StringComparison.OrdinalIgnoreCase);

StyleCop.Analyzers/StyleCop.Analyzers/Helpers/XmlCommentHelper.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,37 @@ internal static bool IsConsideredEmpty(XNode node)
170170
return true;
171171
}
172172

173+
/// <summary>
174+
/// Returns the first <see cref="XmlTextSyntax"/> which is not simply empty or whitespace.
175+
/// </summary>
176+
/// <param name="node">The XML content to search.</param>
177+
/// <returns>The first <see cref="XmlTextSyntax"/> which is not simply empty or whitespace, or
178+
/// <see langword="null"/> if no such element exists.</returns>
179+
internal static XmlTextSyntax TryGetFirstTextElementWithContent(XmlNodeSyntax node)
180+
{
181+
if (node is XmlEmptyElementSyntax)
182+
{
183+
return null;
184+
}
185+
else if (node is XmlTextSyntax xmlText)
186+
{
187+
return !IsConsideredEmpty(node) ? xmlText : null;
188+
}
189+
else if (node is XmlElementSyntax xmlElement)
190+
{
191+
foreach (var child in xmlElement.Content)
192+
{
193+
var nestedContent = TryGetFirstTextElementWithContent(child);
194+
if (nestedContent != null)
195+
{
196+
return nestedContent;
197+
}
198+
}
199+
}
200+
201+
return null;
202+
}
203+
173204
/// <summary>
174205
/// Checks if a <see cref="SyntaxTrivia"/> contains a <see cref="DocumentationCommentTriviaSyntax"/> and returns
175206
/// <see langword="true"/> if it is considered empty.

0 commit comments

Comments
 (0)