Skip to content

Commit 24dd901

Browse files
Update SA1612 to also check primary constructor parameters
#3786
1 parent 045aba1 commit 24dd901

File tree

2 files changed

+70
-21
lines changed

2 files changed

+70
-21
lines changed

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

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules
1111
using System.Threading.Tasks;
1212
using Microsoft.CodeAnalysis.Testing;
1313
using StyleCop.Analyzers.DocumentationRules;
14+
using StyleCop.Analyzers.Lightup;
1415
using StyleCop.Analyzers.Test.Verifiers;
1516
using Xunit;
1617
using static StyleCop.Analyzers.Test.Verifiers.CustomDiagnosticVerifier<StyleCop.Analyzers.DocumentationRules.SA1612ElementParameterDocumentationMustMatchElementParameters>;
@@ -24,9 +25,26 @@ public static IEnumerable<object[]> Declarations
2425
{
2526
get
2627
{
27-
yield return new object[] { " public ClassName {|#0:Method|}(string foo, string bar, string @new) { return null; }" };
28-
yield return new object[] { " public delegate ClassName {|#0:Method|}(string foo, string bar, string @new);" };
29-
yield return new object[] { " public ClassName {|#0:this|}[string foo, string bar, string @new] { get { return null; } set { } }" };
28+
yield return new[] { " public ClassName {|#0:Method|}(string foo, string bar, string @new) { return null; }" };
29+
yield return new[] { " public delegate ClassName {|#0:Method|}(string foo, string bar, string @new);" };
30+
yield return new[] { " public ClassName {|#0:this|}[string foo, string bar, string @new] { get { return null; } set { } }" };
31+
32+
if (LightupHelpers.SupportsCSharp9)
33+
{
34+
yield return new[] { " public record {|#0:TestType|}(string foo, string bar, string @new) {}" };
35+
}
36+
37+
if (LightupHelpers.SupportsCSharp10)
38+
{
39+
yield return new[] { " public record struct {|#0:TestType|}(string foo, string bar, string @new) {}" };
40+
yield return new[] { " public record class {|#0:TestType|}(string foo, string bar, string @new) {}" };
41+
}
42+
43+
if (LightupHelpers.SupportsCSharp12)
44+
{
45+
yield return new[] { " public struct {|#0:TestType|}(string foo, string bar, string @new) {}" };
46+
yield return new[] { " public class {|#0:TestType|}(string foo, string bar, string @new) {}" };
47+
}
3048
}
3149
}
3250

@@ -165,11 +183,12 @@ public class ClassName
165183
var diagnostic = Diagnostic()
166184
.WithMessageFormat("The parameter documentation for '{0}' should be at position {1}");
167185

168-
var expected = new[]
186+
var normallyExpected = new[]
169187
{
170188
diagnostic.WithLocation(10, 21).WithArguments("new", 3),
171189
diagnostic.WithLocation(11, 21).WithArguments("foo", 1),
172190
};
191+
var expected = GetExpectedDiagnostics(normallyExpected, declaration);
173192

174193
await VerifyCSharpDiagnosticAsync(testCode.Replace("$$", declaration), expected, CancellationToken.None).ConfigureAwait(false);
175194
}
@@ -193,12 +212,13 @@ public class ClassName
193212
$$
194213
}";
195214

196-
var expected = new[]
215+
var normallyExpected = new[]
197216
{
198217
Diagnostic().WithLocation(10, 21).WithArguments("boo"),
199218
Diagnostic().WithLocation(11, 21).WithArguments("far"),
200219
Diagnostic().WithLocation(12, 21).WithArguments("foe"),
201220
};
221+
var expected = GetExpectedDiagnostics(normallyExpected, declaration);
202222

203223
await VerifyCSharpDiagnosticAsync(testCode.Replace("$$", declaration), expected, CancellationToken.None).ConfigureAwait(false);
204224
}
@@ -248,12 +268,13 @@ public class ClassName
248268
var diagnostic = Diagnostic()
249269
.WithMessageFormat("The parameter documentation for '{0}' should be at position {1}");
250270

251-
var expected = new[]
271+
var normallyExpected = new[]
252272
{
253273
diagnostic.WithLocation(10, 22).WithArguments("bar", 2),
254274
diagnostic.WithLocation(11, 22).WithArguments("new", 3),
255275
diagnostic.WithLocation(12, 22).WithArguments("foo", 1),
256276
};
277+
var expected = GetExpectedDiagnostics(normallyExpected, declaration);
257278

258279
await VerifyCSharpDiagnosticAsync(testCode.Replace("$$", declaration), expected, CancellationToken.None).ConfigureAwait(false);
259280

@@ -267,10 +288,11 @@ public class ClassName
267288
}
268289
";
269290

270-
expected = new[]
291+
normallyExpected = new[]
271292
{
272293
diagnostic.WithLocation(12, 22).WithArguments("foo", 1),
273294
};
295+
expected = GetExpectedDiagnostics(normallyExpected, declaration);
274296

275297
await VerifyCSharpDiagnosticAsync(testCode.Replace("$$", declaration), testSettings, expected, CancellationToken.None).ConfigureAwait(false);
276298
}
@@ -298,7 +320,8 @@ public class ClassName
298320
var diagnostic = Diagnostic()
299321
.WithMessageFormat("The parameter documentation for '{0}' should be at position {1}");
300322

301-
var expected = diagnostic.WithLocation(13, 22).WithArguments("bar", 2);
323+
var normallyExpected = diagnostic.WithLocation(13, 22).WithArguments("bar", 2);
324+
var expected = GetExpectedDiagnostics(normallyExpected, declaration);
302325

303326
await VerifyCSharpDiagnosticAsync(testCode.Replace("$$", declaration), expected, CancellationToken.None).ConfigureAwait(false);
304327
}
@@ -409,12 +432,13 @@ public class ClassName
409432
$$
410433
}";
411434

412-
var expected = new[]
435+
var normallyExpected = new[]
413436
{
414437
Diagnostic().WithLocation(0).WithArguments("boo"),
415438
Diagnostic().WithLocation(0).WithArguments("far"),
416439
Diagnostic().WithLocation(0).WithArguments("foe"),
417440
};
441+
var expected = GetExpectedDiagnostics(normallyExpected, declaration);
418442

419443
await VerifyCSharpDiagnosticAsync(testCode.Replace("$$", declaration), expected, CancellationToken.None).ConfigureAwait(false);
420444
}
@@ -451,12 +475,13 @@ public class ClassName
451475
var diagnostic = Diagnostic()
452476
.WithMessageFormat("The parameter documentation for '{0}' should be at position {1}");
453477

454-
var expected = new[]
478+
var normallyExpected = new[]
455479
{
456480
diagnostic.WithLocation(0).WithArguments("new", 3),
457481
diagnostic.WithLocation(0).WithArguments("foo", 1),
458482
diagnostic.WithLocation(0).WithArguments("bar", 2),
459483
};
484+
var expected = GetExpectedDiagnostics(normallyExpected, declaration);
460485

461486
await VerifyCSharpDiagnosticAsync(testCode.Replace("$$", declaration), expected, CancellationToken.None).ConfigureAwait(false);
462487

@@ -473,11 +498,12 @@ public class ClassName
473498
}
474499
";
475500

476-
expected = new[]
501+
normallyExpected = new[]
477502
{
478503
diagnostic.WithLocation(0).WithArguments("foo", 1),
479504
diagnostic.WithLocation(0).WithArguments("bar", 2),
480505
};
506+
expected = GetExpectedDiagnostics(normallyExpected, declaration);
481507

482508
await VerifyCSharpDiagnosticAsync(testCode.Replace("$$", declaration), testSettings, expected, CancellationToken.None).ConfigureAwait(false);
483509
}
@@ -499,7 +525,8 @@ public class ClassName
499525
var diagnostic = Diagnostic()
500526
.WithMessageFormat("The parameter documentation for '{0}' should be at position {1}");
501527

502-
var expected = diagnostic.WithLocation(0).WithArguments("bar", 2);
528+
var normallyExpected = diagnostic.WithLocation(0).WithArguments("bar", 2);
529+
var expected = GetExpectedDiagnostics(normallyExpected, declaration);
503530

504531
await VerifyCSharpDiagnosticAsync(testCode.Replace("$$", declaration), expected, CancellationToken.None).ConfigureAwait(false);
505532
}
@@ -535,8 +562,27 @@ public class ClassName
535562
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
536563
}
537564

538-
private static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult expected, CancellationToken cancellationToken)
539-
=> VerifyCSharpDiagnosticAsync(source, testSettings: null, new[] { expected }, ignoreCompilerDiagnostics: false, cancellationToken);
565+
private static DiagnosticResult[] GetExpectedDiagnostics(DiagnosticResult normallyExpected, string declaration)
566+
{
567+
return GetExpectedDiagnostics(new[] { normallyExpected }, declaration);
568+
}
569+
570+
// Syntax node actions for type declarations with a primary constructor were called twice
571+
// before support for c# 11 was added.
572+
private static DiagnosticResult[] GetExpectedDiagnostics(DiagnosticResult[] normallyExpected, string declaration)
573+
{
574+
var isPrimaryConstructor = declaration.Contains("record") || declaration.Contains("class") || declaration.Contains("struct");
575+
576+
if (isPrimaryConstructor && !LightupHelpers.SupportsCSharp11)
577+
{
578+
// Diagnostic issued twice because of https://github.com/dotnet/roslyn/issues/53136 and https://github.com/dotnet/roslyn/issues/70488
579+
return normallyExpected.Concat(normallyExpected).ToArray();
580+
}
581+
else
582+
{
583+
return normallyExpected;
584+
}
585+
}
540586

541587
private static Task VerifyCSharpDiagnosticAsync(string source, DiagnosticResult[] expected, CancellationToken cancellationToken)
542588
=> VerifyCSharpDiagnosticAsync(source, testSettings: null, expected, ignoreCompilerDiagnostics: false, cancellationToken);

StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/SA1612ElementParameterDocumentationMustMatchElementParameters.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace StyleCop.Analyzers.DocumentationRules
1313
using Microsoft.CodeAnalysis.CSharp.Syntax;
1414
using Microsoft.CodeAnalysis.Diagnostics;
1515
using StyleCop.Analyzers.Helpers;
16+
using StyleCop.Analyzers.Lightup;
1617
using StyleCop.Analyzers.Settings.ObjectModel;
1718

1819
/// <summary>
@@ -45,7 +46,7 @@ internal class SA1612ElementParameterDocumentationMustMatchElementParameters : E
4546
new DiagnosticDescriptor(DiagnosticId, Title, MissingParamForDocumentationMessageFormat, AnalyzerCategory.DocumentationRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, HelpLink);
4647

4748
private static readonly DiagnosticDescriptor OrderDescriptor =
48-
new DiagnosticDescriptor(DiagnosticId, Title, ParamWrongOrderMessageFormat, AnalyzerCategory.DocumentationRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, HelpLink);
49+
new DiagnosticDescriptor(DiagnosticId, Title, ParamWrongOrderMessageFormat, AnalyzerCategory.DocumentationRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, HelpLink);
4950

5051
public SA1612ElementParameterDocumentationMustMatchElementParameters()
5152
: base(matchElementName: XmlCommentHelper.ParamXmlTag, inheritDocSuppressesWarnings: true)
@@ -70,8 +71,8 @@ protected override void HandleXmlElement(SyntaxNodeAnalysisContext context, Styl
7071

7172
var parameterList = GetParameters(node)?.ToImmutableArray();
7273

73-
bool hasNoParameters = !parameterList?.Any() ?? false;
74-
if (hasNoParameters)
74+
bool hasParameters = parameterList?.Any() ?? false;
75+
if (!hasParameters)
7576
{
7677
return;
7778
}
@@ -141,8 +142,8 @@ protected override void HandleCompleteDocumentation(SyntaxNodeAnalysisContext co
141142
var identifierLocation = identifier.Value.GetLocation();
142143
var parameterList = GetParameters(node)?.ToImmutableArray();
143144

144-
bool hasNoParameters = !parameterList?.Any() ?? false;
145-
if (hasNoParameters)
145+
bool hasParameters = parameterList?.Any() ?? false;
146+
if (!hasParameters)
146147
{
147148
return;
148149
}
@@ -203,14 +204,16 @@ private static IEnumerable<ParameterSyntax> GetParameters(SyntaxNode node)
203204
{
204205
return (node as BaseMethodDeclarationSyntax)?.ParameterList?.Parameters
205206
?? (node as IndexerDeclarationSyntax)?.ParameterList?.Parameters
206-
?? (node as DelegateDeclarationSyntax)?.ParameterList?.Parameters;
207+
?? (node as DelegateDeclarationSyntax)?.ParameterList?.Parameters
208+
?? (node as TypeDeclarationSyntax)?.ParameterList()?.Parameters;
207209
}
208210

209211
private static SyntaxToken? GetIdentifier(SyntaxNode node)
210212
{
211213
return (node as MethodDeclarationSyntax)?.Identifier
212214
?? (node as IndexerDeclarationSyntax)?.ThisKeyword
213-
?? (node as DelegateDeclarationSyntax)?.Identifier;
215+
?? (node as DelegateDeclarationSyntax)?.Identifier
216+
?? (node as TypeDeclarationSyntax)?.Identifier;
214217
}
215218
}
216219
}

0 commit comments

Comments
 (0)