Skip to content

Commit 025a3fd

Browse files
committed
Update SA1612 to handle included documentation (fixes #1905)
1 parent 277421e commit 025a3fd

2 files changed

Lines changed: 293 additions & 80 deletions

File tree

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

Lines changed: 231 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.Diagnostics;
1113
using TestHelper;
1214
using Xunit;
@@ -184,6 +186,235 @@ public class ClassName
184186
await this.VerifyCSharpDiagnosticAsync(testCode.Replace("$$", p), expected, CancellationToken.None).ConfigureAwait(false);
185187
}
186188

189+
[Theory]
190+
[MemberData(nameof(Declarations))]
191+
public async Task VerifyInheritedDocumentationReportsNoDiagnosticsAsync(string declaration)
192+
{
193+
var testCode = @"
194+
/// <summary>
195+
/// Foo
196+
/// </summary>
197+
public class ClassName
198+
{
199+
/// <inheritdoc/>
200+
$$
201+
}";
202+
await this.VerifyCSharpDiagnosticAsync(testCode.Replace("$$", declaration), EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
203+
}
204+
205+
[Fact]
206+
public async Task VerifyIncludedMemberWithoutParamsIsNotReportedAsync()
207+
{
208+
var testCode = @"
209+
/// <summary>
210+
/// Foo
211+
/// </summary>
212+
public class ClassName
213+
{
214+
/// <include file='MissingParamDocumentation.xml' path='/ClassName/Method/*' />
215+
public ClassName Method() { return null; }
216+
}";
217+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
218+
}
219+
220+
[Fact]
221+
public async Task VerifyIncludedMemberWithValidParamsIsNotReportedAsync()
222+
{
223+
var testCode = @"
224+
/// <summary>
225+
/// Foo
226+
/// </summary>
227+
public class ClassName
228+
{
229+
/// <include file='WithParamDocumentation.xml' path='/ClassName/Method/*' />
230+
public ClassName Method(string foo, string bar) { return null; }
231+
}";
232+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
233+
}
234+
235+
[Fact]
236+
public async Task VerifyIncludedMemberWithInvalidParamsIsReportedAsync()
237+
{
238+
var testCode = @"
239+
/// <summary>
240+
/// Foo
241+
/// </summary>
242+
public class ClassName
243+
{
244+
/// <include file='WithInvalidParamDocumentation.xml' path='/ClassName/Method/*' />
245+
public ClassName Method(string foo, string bar) { return null; }
246+
}";
247+
248+
var expected = new[]
249+
{
250+
this.CSharpDiagnostic().WithLocation(8, 22).WithArguments("boo"),
251+
this.CSharpDiagnostic().WithLocation(8, 22).WithArguments("far")
252+
};
253+
254+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
255+
}
256+
257+
[Fact]
258+
public async Task VerifyIncludedMemberWithInvalidParamsThatShouldBeHandledBySA1613IsNotReportedAsync()
259+
{
260+
var testCode = @"
261+
/// <summary>
262+
/// Foo
263+
/// </summary>
264+
public class ClassName
265+
{
266+
/// <include file='WithSA1613ParamDocumentation.xml' path='/ClassName/Method/*' />
267+
public ClassName Method(string foo, string bar) { return null; }
268+
}";
269+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
270+
}
271+
272+
[Fact]
273+
public async Task VerifyIncludedMemberWithAllDocumentationWrongOrderIsReportedAsync()
274+
{
275+
var testCode = @"
276+
/// <summary>
277+
/// Foo
278+
/// </summary>
279+
public class ClassName
280+
{
281+
/// <include file='WithParamDocumentation.xml' path='/ClassName/Method/*' />
282+
public ClassName Method(string bar, string foo) { return null; }
283+
}";
284+
285+
var diagnostic = this.CSharpDiagnostic()
286+
.WithMessageFormat("The parameter documentation for '{0}' should be at position {1}.");
287+
288+
var expected = new[]
289+
{
290+
diagnostic.WithLocation(8, 22).WithArguments("foo", 2),
291+
diagnostic.WithLocation(8, 22).WithArguments("bar", 1),
292+
};
293+
294+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
295+
}
296+
297+
[Fact]
298+
public async Task VerifyIncludedMemberWithTooManyDocumentationIsReportedAsync()
299+
{
300+
var testCode = @"
301+
/// <summary>
302+
/// Foo
303+
/// </summary>
304+
public class ClassName
305+
{
306+
/// <include file='WithTooManyParamDocumentation.xml' path='/ClassName/Method/*' />
307+
public ClassName Method(string foo, string bar) { return null; }
308+
}";
309+
310+
var diagnostic = this.CSharpDiagnostic()
311+
.WithMessageFormat("The parameter documentation for '{0}' should be at position {1}.");
312+
313+
var expected = diagnostic.WithLocation(8, 22).WithArguments("bar", 2);
314+
315+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
316+
}
317+
318+
[Fact]
319+
public async Task VerifyIncludedInheritedDocumentationIsNotReportedAsync()
320+
{
321+
var testCode = @"
322+
/// <summary>
323+
/// Foo
324+
/// </summary>
325+
public class ClassName
326+
{
327+
/// <include file='WithInheritedDocumentation.xml' path='/ClassName/Method/*' />
328+
public ClassName Method(string foo, string bar) { return null; }
329+
}";
330+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
331+
}
332+
333+
/// <inheritdoc/>
334+
protected override Project CreateProject(string[] sources, string language = "C#", string[] filenames = null)
335+
{
336+
var resolver = new TestXmlReferenceResolver();
337+
338+
string contentWithoutParamDocumentation = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
339+
<ClassName>
340+
<Method>
341+
<summary>
342+
Foo
343+
</summary>
344+
</Method>
345+
</ClassName>
346+
";
347+
resolver.XmlReferences.Add("MissingParamDocumentation.xml", contentWithoutParamDocumentation);
348+
349+
string contentWithParamDocumentation = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
350+
<ClassName>
351+
<Method>
352+
<summary>
353+
Foo
354+
</summary>
355+
<param name=""foo"">Param 1</param>
356+
<param name=""bar"">Param 2</param>
357+
</Method>
358+
</ClassName>
359+
";
360+
resolver.XmlReferences.Add("WithParamDocumentation.xml", contentWithParamDocumentation);
361+
362+
string contentWithInvalidParamDocumentation = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
363+
<ClassName>
364+
<Method>
365+
<summary>
366+
Foo
367+
</summary>
368+
<param name=""boo"">Param 1</param>
369+
<param name=""far"">Param 2</param>
370+
</Method>
371+
</ClassName>
372+
";
373+
resolver.XmlReferences.Add("WithInvalidParamDocumentation.xml", contentWithInvalidParamDocumentation);
374+
375+
string contentWithSA1613ParamDocumentation = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
376+
<ClassName>
377+
<Method>
378+
<summary>
379+
Foo
380+
</summary>
381+
<param>Test</param>
382+
<param/>
383+
<param name="""">Test</param>
384+
<param name="" "">Test</param>
385+
</Method>
386+
</ClassName>
387+
";
388+
resolver.XmlReferences.Add("WithSA1613ParamDocumentation.xml", contentWithSA1613ParamDocumentation);
389+
390+
string contentWithTooManyParamDocumentation = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
391+
<ClassName>
392+
<Method>
393+
<summary>
394+
Foo
395+
</summary>
396+
<param name=""foo"">Param 1</param>
397+
<param name=""bar"">Param 2</param>
398+
<param name=""bar"">Param 3</param>
399+
</Method>
400+
</ClassName>
401+
";
402+
resolver.XmlReferences.Add("WithTooManyParamDocumentation.xml", contentWithTooManyParamDocumentation);
403+
404+
string contentWithInheritedDocumentation = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
405+
<ClassName>
406+
<Method>
407+
<inheritdoc />
408+
</Method>
409+
</ClassName>
410+
";
411+
resolver.XmlReferences.Add("WithInheritedDocumentation.xml", contentWithInheritedDocumentation);
412+
413+
Project project = base.CreateProject(sources, language, filenames);
414+
project = project.WithCompilationOptions(project.CompilationOptions.WithXmlReferenceResolver(resolver));
415+
return project;
416+
}
417+
187418
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
188419
{
189420
yield return new SA1612ElementParameterDocumentationMustMatchElementParameters();

0 commit comments

Comments
 (0)