Skip to content

Commit fdb3d67

Browse files
authored
Merge pull request #3315 from sharwell/handle-null
Fix handling of <include> on fields
2 parents 24755d2 + bd3c7b8 commit fdb3d67

File tree

10 files changed

+206
-18
lines changed

10 files changed

+206
-18
lines changed

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

+57
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,18 @@ public class ClassName
185185
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
186186
}
187187

188+
[Fact]
189+
[WorkItem(3150, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3150")]
190+
public async Task TestClassWithIncludedMissingDocumentationAsync()
191+
{
192+
var testCode = @"
193+
/// <include file='MissingFile.xml' path='/ClassName/*' />
194+
public class ClassName
195+
{
196+
}";
197+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
198+
}
199+
188200
[Fact]
189201
public async Task TestClassWithIncludedSummaryDocumentationAsync()
190202
{
@@ -210,6 +222,35 @@ public class ClassName
210222
await VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
211223
}
212224

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+
213254
private static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult expected, CancellationToken cancellationToken)
214255
=> VerifyCSharpDiagnosticAsync(source, new[] { expected }, cancellationToken);
215256

@@ -232,6 +273,20 @@ private static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult[
232273
Summary description for the ClassName class.
233274
</summary>
234275
</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>
235290
";
236291

237292
var test = new StyleCopDiagnosticVerifier<SA1608ElementDocumentationMustNotHaveDefaultSummary>.CSharpTest
@@ -242,6 +297,8 @@ private static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult[
242297
{ "ClassWithoutSummary.xml", contentWithoutSummary },
243298
{ "ClassWithSummary.xml", contentWithSummary },
244299
{ "ClassWithDefaultSummary.xml", contentWithDefaultSummary },
300+
{ "FieldWithSummary.xml", fieldContentWithSummary },
301+
{ "FieldWithDefaultSummary.xml", fieldContentWithDefaultSummary },
245302
},
246303
};
247304

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

+30
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,36 @@ public void TestMethod(string param1, string param2, string param3)
312312
await VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
313313
}
314314

315+
/// <summary>
316+
/// Verifies that included documentation with missing documentation file produces no diagnostics.
317+
/// </summary>
318+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
319+
[Fact]
320+
[WorkItem(3150, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3150")]
321+
public async Task VerifyIncludedMissingDocumentationAsync()
322+
{
323+
var testCode = @"
324+
/// <summary>
325+
/// Foo
326+
/// </summary>
327+
public class ClassName
328+
{
329+
/// <include file='MissingFile.xml' path='/TestClass/TestMethod/*' />
330+
public void TestMethod(string {|#0:param1|}, string {|#1:param2|}, string {|#2:param3|})
331+
{
332+
}
333+
}";
334+
335+
DiagnosticResult[] expected =
336+
{
337+
Diagnostic().WithLocation(0).WithArguments("param1"),
338+
Diagnostic().WithLocation(1).WithArguments("param2"),
339+
Diagnostic().WithLocation(2).WithArguments("param3"),
340+
};
341+
342+
await VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
343+
}
344+
315345
/// <summary>
316346
/// Verifies that included documentation with missing elements documented produces the expected diagnostics.
317347
/// </summary>

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

+16
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,22 @@ public class ClassName
285285
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
286286
}
287287

288+
[Fact]
289+
[WorkItem(3150, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3150")]
290+
public async Task VerifyIncludedMissingFileIsNotReportedAsync()
291+
{
292+
var testCode = @"
293+
/// <summary>
294+
/// Foo
295+
/// </summary>
296+
public class ClassName
297+
{
298+
/// <include file='MissingFile.xml' path='/ClassName/Method/*' />
299+
public ClassName Method() { return null; }
300+
}";
301+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
302+
}
303+
288304
[Fact]
289305
public async Task VerifyIncludedMemberWithValidParamsIsNotReportedAsync()
290306
{

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1613UnitTests.cs

+16
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,22 @@ public class ClassName
154154
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
155155
}
156156

157+
[Fact]
158+
[WorkItem(3150, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3150")]
159+
public async Task VerifyIncludedMissingFileAsync()
160+
{
161+
var testCode = @"
162+
/// <summary>
163+
/// Foo
164+
/// </summary>
165+
public class ClassName
166+
{
167+
/// <include file='MissingFile.xml' path='/ClassName/Method/*' />
168+
public ClassName Method(string foo, string bar) { return null; }
169+
}";
170+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
171+
}
172+
157173
[Fact]
158174
public async Task VerifyMemberWithValidParamsAndIncludedDocumentationAsync()
159175
{

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1614UnitTests.cs

+16
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,22 @@ public class ClassName
154154
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
155155
}
156156

157+
[Fact]
158+
[WorkItem(3150, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3150")]
159+
public async Task VerifyIncludedMissingFileAsync()
160+
{
161+
var testCode = @"
162+
/// <summary>
163+
/// Foo
164+
/// </summary>
165+
public class ClassName
166+
{
167+
/// <include file='MissingFile.xml' path='/ClassName/Method/*' />
168+
public ClassName Method(string foo, string bar) { return null; }
169+
}";
170+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
171+
}
172+
157173
[Fact]
158174
public async Task VerifyMemberIncludedDocumentationWithoutParamsAsync()
159175
{

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1616UnitTests.cs

+16
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,22 @@ public class ClassName
443443
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
444444
}
445445

446+
[Fact]
447+
[WorkItem(3150, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3150")]
448+
public async Task VerifyMemberIncludedMissingFileAsync()
449+
{
450+
var testCode = @"
451+
/// <summary>
452+
/// Foo
453+
/// </summary>
454+
public class ClassName
455+
{
456+
/// <include file='MissingFile.xml' path='/ClassName/Method/*' />
457+
public ClassName Method(string foo, string bar) { return null; }
458+
}";
459+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
460+
}
461+
446462
[Fact]
447463
public async Task VerifyMemberIncludedDocumentationWithoutReturnsAsync()
448464
{

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1625UnitTests.cs

+15
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,21 @@ public class TestClass2 {{ }}
321321
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
322322
}
323323

324+
[Fact]
325+
[WorkItem(3150, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3150")]
326+
public async Task VerifyThatMissingIncludedDocumentationDoesNotReportADiagnosticAsync()
327+
{
328+
var testCode = $@"
329+
public class TestClass
330+
{{
331+
/// <include file='MissingFile.xml' path='/TestClass/Test/*' />
332+
public void Test() {{ }}
333+
}}
334+
public class TestClass2 {{ }}
335+
";
336+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
337+
}
338+
324339
[Fact]
325340
public async Task VerifyThatTheAnalyzerDoesNotCrashOnIncludedInheritDocAsync()
326341
{

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1629UnitTests.cs

+14
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,20 @@ public class TestClass
315315
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
316316
}
317317

318+
[Fact]
319+
[WorkItem(3150, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3150")]
320+
public async Task TestIncludedMissingFileAsync()
321+
{
322+
var testCode = @"
323+
/// <include file='MissingFile.xml' path='/TestClass/*'/>
324+
public class TestClass
325+
{
326+
}
327+
";
328+
329+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
330+
}
331+
318332
[Fact]
319333
[WorkItem(2680, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2680")]
320334
public async Task TestReportingAfterEmptyElementAsync()

StyleCop.Analyzers/StyleCop.Analyzers.Test/StyleCop.Analyzers.Test.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
<ItemGroup>
2020
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="1.3.2" />
21-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.0.1-beta1.20623.3" />
21+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.0.1-beta1.21159.2" />
2222
<PackageReference Include="Microsoft.VisualStudio.Composition" Version="16.1.8" />
2323
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
2424
<PackageReference Include="xunit" Version="2.4.1" />

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

+25-17
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)