Skip to content

Commit bd3c7b8

Browse files
committed
Fix handling of <include> on fields
Fixes #3150
1 parent 71afe9b commit bd3c7b8

File tree

2 files changed

+70
-17
lines changed

2 files changed

+70
-17
lines changed

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,35 @@ public class ClassName
222222
await VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
223223
}
224224

225+
[Fact]
226+
[WorkItem(3150, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3150")]
227+
public async Task TestFieldWithIncludedSummaryDocumentationAsync()
228+
{
229+
var testCode = @"
230+
public class ClassName
231+
{
232+
/// <include file='FieldWithSummary.xml' path='/FieldName/*' />
233+
public int FieldName;
234+
}";
235+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
236+
}
237+
238+
[Fact]
239+
[WorkItem(3150, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3150")]
240+
public async Task TestFieldWithIncludedDefaultSummaryDocumentationAsync()
241+
{
242+
var testCode = @"
243+
public class ClassName
244+
{
245+
/// <include file='FieldWithDefaultSummary.xml' path='/FieldName/*' />
246+
public {|#0:int FieldName|};
247+
}";
248+
249+
DiagnosticResult expected = Diagnostic().WithLocation(0);
250+
251+
await VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
252+
}
253+
225254
private static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult expected, CancellationToken cancellationToken)
226255
=> VerifyCSharpDiagnosticAsync(source, new[] { expected }, cancellationToken);
227256

@@ -244,6 +273,20 @@ private static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult[
244273
Summary description for the ClassName class.
245274
</summary>
246275
</ClassName>
276+
";
277+
string fieldContentWithSummary = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
278+
<FieldName>
279+
<summary>
280+
Foo
281+
</summary>
282+
</FieldName>
283+
";
284+
string fieldContentWithDefaultSummary = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
285+
<FieldName>
286+
<summary>
287+
Summary description for the ClassName class.
288+
</summary>
289+
</FieldName>
247290
";
248291

249292
var test = new StyleCopDiagnosticVerifier<SA1608ElementDocumentationMustNotHaveDefaultSummary>.CSharpTest
@@ -254,6 +297,8 @@ private static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult[
254297
{ "ClassWithoutSummary.xml", contentWithoutSummary },
255298
{ "ClassWithSummary.xml", contentWithSummary },
256299
{ "ClassWithDefaultSummary.xml", contentWithDefaultSummary },
300+
{ "FieldWithSummary.xml", fieldContentWithSummary },
301+
{ "FieldWithDefaultSummary.xml", fieldContentWithDefaultSummary },
257302
},
258303
};
259304

StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/ElementDocumentationBase.cs

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -234,29 +234,37 @@ private void HandleDeclaration(SyntaxNodeAnalysisContext context, StyleCopSettin
234234

235235
if (hasIncludedDocumentation)
236236
{
237-
var declaration = context.SemanticModel.GetDeclaredSymbol(node, context.CancellationToken);
238-
var rawDocumentation = declaration?.GetDocumentationCommentXml(expandIncludes: true, cancellationToken: context.CancellationToken);
239-
var completeDocumentation = XElement.Parse(rawDocumentation, LoadOptions.None);
237+
var declaration = node switch
238+
{
239+
BaseFieldDeclarationSyntax baseFieldDeclaration => baseFieldDeclaration.Declaration.Variables.FirstOrDefault() ?? node,
240+
_ => node,
241+
};
240242

241-
if (this.inheritDocSuppressesWarnings &&
242-
completeDocumentation.Nodes().OfType<XElement>().Any(element => element.Name == XmlCommentHelper.InheritdocXmlTag))
243+
var declaredSymbol = context.SemanticModel.GetDeclaredSymbol(declaration, context.CancellationToken);
244+
if (declaredSymbol is not null)
243245
{
244-
// Ignore nodes with an <inheritdoc/> tag in the included XML.
246+
var rawDocumentation = declaredSymbol?.GetDocumentationCommentXml(expandIncludes: true, cancellationToken: context.CancellationToken);
247+
var completeDocumentation = XElement.Parse(rawDocumentation ?? "<doc></doc>", LoadOptions.None);
248+
249+
if (this.inheritDocSuppressesWarnings &&
250+
completeDocumentation.Nodes().OfType<XElement>().Any(element => element.Name == XmlCommentHelper.InheritdocXmlTag))
251+
{
252+
// Ignore nodes with an <inheritdoc/> tag in the included XML.
253+
return;
254+
}
255+
256+
this.HandleCompleteDocumentation(context, needsComment, completeDocumentation, locations);
245257
return;
246258
}
247-
248-
this.HandleCompleteDocumentation(context, needsComment, completeDocumentation, locations);
249259
}
250-
else
251-
{
252-
IEnumerable<XmlNodeSyntax> matchingXmlElements = string.IsNullOrEmpty(this.matchElementName)
253-
? documentation.Content
254-
.Where(x => x is XmlElementSyntax || x is XmlEmptyElementSyntax)
255-
.Where(x => !string.Equals(x.GetName()?.ToString(), XmlCommentHelper.IncludeXmlTag, StringComparison.Ordinal))
256-
: documentation.Content.GetXmlElements(this.matchElementName);
257260

258-
this.HandleXmlElement(context, settings, needsComment, matchingXmlElements, locations);
259-
}
261+
IEnumerable<XmlNodeSyntax> matchingXmlElements = string.IsNullOrEmpty(this.matchElementName)
262+
? documentation.Content
263+
.Where(x => x is XmlElementSyntax || x is XmlEmptyElementSyntax)
264+
.Where(x => !string.Equals(x.GetName()?.ToString(), XmlCommentHelper.IncludeXmlTag, StringComparison.Ordinal))
265+
: documentation.Content.GetXmlElements(this.matchElementName);
266+
267+
this.HandleXmlElement(context, settings, needsComment, matchingXmlElements, locations);
260268
}
261269
}
262270
}

0 commit comments

Comments
 (0)