Skip to content

Commit 67e8fd5

Browse files
Update SA1110 to also check the primary constructor argument list in a base list
#3784
1 parent f1f6d83 commit 67e8fd5

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/ReadabilityRules/SA1110CSharp9UnitTests.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,95 @@ public async Task TestPrimaryConstructorWithParametersAsync(string typeKeyword)
5656
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
5757
}
5858

59+
[Theory]
60+
[MemberData(nameof(CommonMemberData.ReferenceTypeKeywordsWhichSupportPrimaryConstructors), MemberType = typeof(CommonMemberData))]
61+
[WorkItem(3784, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3784")]
62+
public async Task TestPrimaryConstructorBaseListWithParametersOnSameLineAsync(string typeKeyword)
63+
{
64+
var testCode = $@"
65+
{typeKeyword} Foo(int x)
66+
{{
67+
}}
68+
69+
{typeKeyword} Bar(int x) : Foo(x)
70+
{{
71+
}}";
72+
73+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
74+
}
75+
76+
[Theory]
77+
[MemberData(nameof(CommonMemberData.ReferenceTypeKeywordsWhichSupportPrimaryConstructors), MemberType = typeof(CommonMemberData))]
78+
[WorkItem(3784, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3784")]
79+
public async Task TestPrimaryConstructorBaseListWithParametersAsync(string typeKeyword)
80+
{
81+
var testCode = $@"
82+
{typeKeyword} Foo(int x)
83+
{{
84+
}}
85+
86+
{typeKeyword} Bar(int x) : Foo
87+
{{|#0:(|}}x)
88+
{{
89+
}}";
90+
91+
var fixedCode = $@"
92+
{typeKeyword} Foo(int x)
93+
{{
94+
}}
95+
96+
{typeKeyword} Bar(int x) : Foo(
97+
x)
98+
{{
99+
}}";
100+
101+
var expected = this.GetExpectedResultTestPrimaryConstructorBaseList();
102+
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
103+
}
104+
105+
[Theory]
106+
[MemberData(nameof(CommonMemberData.ReferenceTypeKeywordsWhichSupportPrimaryConstructors), MemberType = typeof(CommonMemberData))]
107+
[WorkItem(3784, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3784")]
108+
public async Task TestPrimaryConstructorBaseListWithoutParametersAsync(string typeKeyword)
109+
{
110+
var testCode = $@"
111+
{typeKeyword} Foo()
112+
{{
113+
}}
114+
115+
{typeKeyword} Bar(int x) : Foo
116+
{{|#0:(|}})
117+
{{
118+
}}";
119+
120+
var fixedCode = $@"
121+
{typeKeyword} Foo()
122+
{{
123+
}}
124+
125+
{typeKeyword} Bar(int x) : Foo()
126+
{{
127+
}}";
128+
129+
var expected = this.GetExpectedResultTestPrimaryConstructorBaseList();
130+
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
131+
}
132+
59133
protected virtual DiagnosticResult[] GetExpectedResultTestPrimaryConstructor()
60134
{
61135
return new[]
62136
{
137+
// Diagnostic issued twice because of https://github.com/dotnet/roslyn/issues/53136
138+
Diagnostic().WithLocation(0),
139+
Diagnostic().WithLocation(0),
140+
};
141+
}
142+
143+
protected virtual DiagnosticResult[] GetExpectedResultTestPrimaryConstructorBaseList()
144+
{
145+
return new[]
146+
{
147+
// Diagnostic issued twice because of https://github.com/dotnet/roslyn/issues/70488
63148
Diagnostic().WithLocation(0),
64149
Diagnostic().WithLocation(0),
65150
};

StyleCop.Analyzers/StyleCop.Analyzers.Test/Helpers/CommonMemberData.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,5 +137,13 @@ public static IEnumerable<object[]> TypeKeywordsWhichSupportPrimaryConstructors
137137
}
138138
}
139139
}
140+
141+
public static IEnumerable<object[]> ReferenceTypeKeywordsWhichSupportPrimaryConstructors
142+
{
143+
get
144+
{
145+
return TypeKeywordsWhichSupportPrimaryConstructors.Where(x => !((string)x[0]).Contains("struct"));
146+
}
147+
}
140148
}
141149
}

StyleCop.Analyzers/StyleCop.Analyzers/Lightup/SyntaxKindEx.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ internal static class SyntaxKindEx
6767
public const SyntaxKind WithExpression = (SyntaxKind)9061;
6868
public const SyntaxKind WithInitializerExpression = (SyntaxKind)9062;
6969
public const SyntaxKind RecordDeclaration = (SyntaxKind)9063;
70+
public const SyntaxKind PrimaryConstructorBaseType = (SyntaxKind)9065;
7071
public const SyntaxKind FunctionPointerUnmanagedCallingConventionList = (SyntaxKind)9066;
7172
public const SyntaxKind RecordStructDeclaration = (SyntaxKind)9068;
7273
public const SyntaxKind CollectionExpression = (SyntaxKind)9076;

StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1110OpeningParenthesisMustBeOnDeclarationLine.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ internal class SA1110OpeningParenthesisMustBeOnDeclarationLine : DiagnosticAnaly
5454
new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, AnalyzerCategory.ReadabilityRules, DiagnosticSeverity.Warning, AnalyzerConstants.EnabledByDefault, Description, HelpLink);
5555

5656
private static readonly Action<SyntaxNodeAnalysisContext> TypeDeclarationAction = HandleTypeDeclaration;
57+
private static readonly Action<SyntaxNodeAnalysisContext> PrimaryConstructorBaseTypeAction = HandlePrimaryConstructorBaseType;
5758
private static readonly Action<SyntaxNodeAnalysisContext> MethodDeclarationAction = HandleMethodDeclaration;
5859
private static readonly Action<SyntaxNodeAnalysisContext> LocalFunctionStatementAction = HandleLocalFunctionStatement;
5960
private static readonly Action<SyntaxNodeAnalysisContext> ConstructorDeclarationAction = HandleConstructorDeclaration;
@@ -79,6 +80,7 @@ public override void Initialize(AnalysisContext context)
7980
context.EnableConcurrentExecution();
8081

8182
context.RegisterSyntaxNodeAction(TypeDeclarationAction, SyntaxKinds.TypeDeclaration);
83+
context.RegisterSyntaxNodeAction(PrimaryConstructorBaseTypeAction, SyntaxKindEx.PrimaryConstructorBaseType);
8284
context.RegisterSyntaxNodeAction(MethodDeclarationAction, SyntaxKind.MethodDeclaration);
8385
context.RegisterSyntaxNodeAction(LocalFunctionStatementAction, SyntaxKindEx.LocalFunctionStatement);
8486
context.RegisterSyntaxNodeAction(ConstructorDeclarationAction, SyntaxKind.ConstructorDeclaration);
@@ -345,6 +347,27 @@ private static void HandleTypeDeclaration(SyntaxNodeAnalysisContext context)
345347
}
346348
}
347349

350+
private static void HandlePrimaryConstructorBaseType(SyntaxNodeAnalysisContext context)
351+
{
352+
var primaryConstructorBaseType = (PrimaryConstructorBaseTypeSyntaxWrapper)context.Node;
353+
354+
var identifierName = ((BaseTypeSyntax)primaryConstructorBaseType).ChildNodes()
355+
.OfType<IdentifierNameSyntax>()
356+
.FirstOrDefault();
357+
if (identifierName == null || identifierName.Identifier.IsMissing)
358+
{
359+
return;
360+
}
361+
362+
var argumentListSyntax = primaryConstructorBaseType.ArgumentList;
363+
364+
if (argumentListSyntax != null && !argumentListSyntax.OpenParenToken.IsMissing)
365+
{
366+
bool preserveLayout = argumentListSyntax.Arguments.Any();
367+
CheckIfLocationOfPreviousTokenAndOpenTokenAreTheSame(context, argumentListSyntax.OpenParenToken, preserveLayout);
368+
}
369+
}
370+
348371
private static void CheckIfLocationOfPreviousTokenAndOpenTokenAreTheSame(SyntaxNodeAnalysisContext context, SyntaxToken openToken, bool preserveLayout)
349372
{
350373
var previousToken = openToken.GetPreviousToken();

0 commit comments

Comments
 (0)