Skip to content

Commit 867e192

Browse files
committed
Updated SA1619
1 parent 1319e88 commit 867e192

4 files changed

Lines changed: 139 additions & 18 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1618UnitTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,6 @@ public void TestMethod<T>(T param1) { }
317317
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
318318
}
319319

320-
321320
/// <summary>
322321
/// Verifies that a generic type without a typeparam in included documentation will flag.
323322
/// </summary>

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1619UnitTests.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ namespace StyleCop.Analyzers.Test.DocumentationRules
77
using System.Threading;
88
using System.Threading.Tasks;
99
using Analyzers.DocumentationRules;
10+
using Microsoft.CodeAnalysis;
1011
using Microsoft.CodeAnalysis.Diagnostics;
12+
using StyleCop.Analyzers.Test.Helpers;
1113
using TestHelper;
1214
using Xunit;
1315

@@ -146,6 +148,89 @@ public async Task TestPartialTypesWithContentDocumentationAsync(string p)
146148
await this.VerifyCSharpDiagnosticAsync(testCode.Replace("##", p), EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
147149
}
148150

151+
/// <summary>
152+
/// Verifies that a generic partial type with included documentation will work.
153+
/// </summary>
154+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
155+
[Fact]
156+
public async Task TestGenericPartialTypeWithIncludedDocumentationAsync()
157+
{
158+
var testCode = @"
159+
/// <include file='ClassWithTypeparamDoc.xml' path='/TestClass/*'/>
160+
public partial class TestClass<T>
161+
{
162+
}
163+
";
164+
165+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
166+
}
167+
168+
/// <summary>
169+
/// Verifies that a generic partial type without a typeparam in included documentation will flag.
170+
/// </summary>
171+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
172+
[Fact]
173+
public async Task TestGenericPartialTypeWithoutTypeparamInIncludedDocumentationAsync()
174+
{
175+
var testCode = @"
176+
/// <include file='ClassWithoutTypeparamDoc.xml' path='/TestClass/*'/>
177+
public partial class TestClass<T>
178+
{
179+
}
180+
";
181+
182+
var expected = this.CSharpDiagnostic().WithLocation(3, 32).WithArguments("T");
183+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
184+
}
185+
186+
/// <summary>
187+
/// Verifies that a generic partial type with &lt;inheritdoc&gt; in included documentation will work.
188+
/// </summary>
189+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
190+
[Fact]
191+
public async Task TestGenericPartialTypeWithInheritdocInIncludedDocumentationAsync()
192+
{
193+
var testCode = @"
194+
/// <include file='ClassWithIneheritdoc.xml' path='/TestClass/*'/>
195+
public partial class TestClass<T>
196+
{
197+
}
198+
";
199+
200+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
201+
}
202+
203+
protected override Project ApplyCompilationOptions(Project project)
204+
{
205+
var resolver = new TestXmlReferenceResolver();
206+
207+
string contentClassWithTypeparamDoc = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
208+
<TestClass>
209+
<summary>Test class</summary>
210+
<typeparam name=""T"">Param 1</typeparam>
211+
</TestClass>
212+
";
213+
resolver.XmlReferences.Add("ClassWithTypeparamDoc.xml", contentClassWithTypeparamDoc);
214+
215+
string contentClassWithoutTypeparamDoc = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
216+
<TestClass>
217+
<summary>Test class</summary>
218+
</TestClass>
219+
";
220+
resolver.XmlReferences.Add("ClassWithoutTypeparamDoc.xml", contentClassWithoutTypeparamDoc);
221+
222+
string contentClassInheritdoc = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
223+
<TestClass>
224+
<inheritdoc/>
225+
</TestClass>
226+
";
227+
resolver.XmlReferences.Add("ClassWithIneheritdoc.xml", contentClassInheritdoc);
228+
229+
project = base.ApplyCompilationOptions(project);
230+
project = project.WithCompilationOptions(project.CompilationOptions.WithXmlReferenceResolver(resolver));
231+
return project;
232+
}
233+
149234
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
150235
{
151236
yield return new SA1619GenericTypeParametersMustBeDocumentedPartialClass();

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ internal class PropertySummaryDocumentationAnalyzer : PropertyDocumentationBase
2020
{
2121
public const string ExpectedTextKey = "ExpectedText";
2222
public const string TextToRemoveKey = "TextToRemove";
23-
public const string NoCodeFixKey = "NoCodeFix";
2423

2524
private const string SA1623DiagnosticId = "SA1623";
2625
private const string SA1624DiagnosticId = "SA1624";

StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/SA1619GenericTypeParametersMustBeDocumentedPartialClass.cs

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace StyleCop.Analyzers.DocumentationRules
66
using System;
77
using System.Collections.Immutable;
88
using System.Linq;
9+
using System.Xml.Linq;
910
using Microsoft.CodeAnalysis;
1011
using Microsoft.CodeAnalysis.CSharp;
1112
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -128,28 +129,65 @@ private static void HandleTypeDeclaration(SyntaxNodeAnalysisContext context)
128129
return;
129130
}
130131

131-
if (documentation.Content.GetFirstXmlElement(XmlCommentHelper.InheritdocXmlTag) != null)
132+
var includeElement = documentation.Content.GetFirstXmlElement(XmlCommentHelper.IncludeXmlTag);
133+
if (includeElement != null)
132134
{
133-
// Ignore nodes with an <inheritdoc/> tag.
134-
return;
135-
}
135+
string rawDocumentation;
136136

137-
if (documentation.Content.GetFirstXmlElement(XmlCommentHelper.SummaryXmlTag) == null)
138-
{
139-
// Ignore nodes without a <summary> tag.
140-
return;
141-
}
137+
var declaration = context.SemanticModel.GetDeclaredSymbol(typeDeclaration, context.CancellationToken);
138+
rawDocumentation = declaration?.GetDocumentationCommentXml(expandIncludes: true, cancellationToken: context.CancellationToken);
139+
var completeDocumentation = XElement.Parse(rawDocumentation, LoadOptions.None);
140+
if (completeDocumentation.Nodes().OfType<XElement>().Any(element => element.Name == XmlCommentHelper.InheritdocXmlTag))
141+
{
142+
// Ignore nodes with an <inheritdoc/> tag in the included XML.
143+
return;
144+
}
145+
146+
if (completeDocumentation.Nodes().OfType<XElement>().All(element => element.Name != XmlCommentHelper.SummaryXmlTag))
147+
{
148+
// Ignore nodes without a <summary> tag.
149+
return;
150+
}
142151

143-
var xmlParameterNames = documentation.Content.GetXmlElements(XmlCommentHelper.TypeParamXmlTag)
144-
.Select(XmlCommentHelper.GetFirstAttributeOrDefault<XmlNameAttributeSyntax>)
145-
.Where(x => x != null)
146-
.ToImmutableArray();
152+
var typeParameterAttributes = completeDocumentation.Nodes()
153+
.OfType<XElement>()
154+
.Where(element => element.Name == XmlCommentHelper.TypeParamXmlTag)
155+
.Select(element => element.Attribute(XmlCommentHelper.NameArgumentName))
156+
.Where(x => x != null);
147157

148-
foreach (var parameter in typeDeclaration.TypeParameterList.Parameters)
158+
foreach (var parameter in typeDeclaration.TypeParameterList.Parameters)
159+
{
160+
if (!typeParameterAttributes.Any(x => x.Value == parameter.Identifier.ValueText))
161+
{
162+
context.ReportDiagnostic(Diagnostic.Create(Descriptor, parameter.Identifier.GetLocation(), parameter.Identifier.ValueText));
163+
}
164+
}
165+
}
166+
else
149167
{
150-
if (!xmlParameterNames.Any(x => x.Identifier.Identifier.ValueText == parameter.Identifier.ValueText))
168+
if (documentation.Content.GetFirstXmlElement(XmlCommentHelper.InheritdocXmlTag) != null)
169+
{
170+
// Ignore nodes with an <inheritdoc/> tag.
171+
return;
172+
}
173+
174+
if (documentation.Content.GetFirstXmlElement(XmlCommentHelper.SummaryXmlTag) == null)
175+
{
176+
// Ignore nodes without a <summary> tag.
177+
return;
178+
}
179+
180+
var xmlParameterNames = documentation.Content.GetXmlElements(XmlCommentHelper.TypeParamXmlTag)
181+
.Select(XmlCommentHelper.GetFirstAttributeOrDefault<XmlNameAttributeSyntax>)
182+
.Where(x => x != null)
183+
.ToImmutableArray();
184+
185+
foreach (var parameter in typeDeclaration.TypeParameterList.Parameters)
151186
{
152-
context.ReportDiagnostic(Diagnostic.Create(Descriptor, parameter.Identifier.GetLocation(), parameter.Identifier.ValueText));
187+
if (!xmlParameterNames.Any(x => x.Identifier.Identifier.ValueText == parameter.Identifier.ValueText))
188+
{
189+
context.ReportDiagnostic(Diagnostic.Create(Descriptor, parameter.Identifier.GetLocation(), parameter.Identifier.ValueText));
190+
}
153191
}
154192
}
155193
}

0 commit comments

Comments
 (0)