Skip to content

Commit e2f3451

Browse files
authored
Merge pull request #3513 from JakubLinhart/fix/SA1516-filescopedns
Support file-scoped namespaces in SA1516
2 parents b80f0c2 + 67bd1ba commit e2f3451

3 files changed

Lines changed: 276 additions & 0 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1516CodeFixProvider.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@ private static SyntaxNode GetRelevantNode(SyntaxNode innerNode)
152152
return currentNode;
153153
}
154154

155+
if (currentNode is ExternAliasDirectiveSyntax)
156+
{
157+
return currentNode;
158+
}
159+
155160
currentNode = currentNode.Parent;
156161
}
157162

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/LayoutRules/SA1516CSharp10UnitTests.cs

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,241 @@
33

44
namespace StyleCop.Analyzers.Test.CSharp10.LayoutRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis;
9+
using Microsoft.CodeAnalysis.CSharp;
10+
using Microsoft.CodeAnalysis.Testing;
611
using StyleCop.Analyzers.Test.CSharp9.LayoutRules;
12+
using Xunit;
13+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
14+
StyleCop.Analyzers.LayoutRules.SA1516ElementsMustBeSeparatedByBlankLine,
15+
StyleCop.Analyzers.LayoutRules.SA1516CodeFixProvider>;
716

817
public class SA1516CSharp10UnitTests : SA1516CSharp9UnitTests
918
{
19+
/// <summary>
20+
/// Verifies that SA1516 is reported for usings and extern alias outside a file scoped namespace.
21+
/// </summary>
22+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
23+
[Fact]
24+
[WorkItem(3512, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3512")]
25+
public async Task TestThatDiagnosticIIsReportedOnUsingsAndExternAliasOutsideFileScopedNamespaceAsync()
26+
{
27+
var testCode = @"extern alias corlib;
28+
[|using|] System;
29+
using System.Linq;
30+
using a = System.Collections;
31+
[|namespace|] Foo;
32+
";
33+
34+
var fixedCode = @"extern alias corlib;
35+
36+
using System;
37+
using System.Linq;
38+
using a = System.Collections;
39+
40+
namespace Foo;
41+
";
42+
43+
await VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
44+
}
45+
46+
/// <summary>
47+
/// Verifies that SA1516 is reported for usings inside a file scoped namespace.
48+
/// </summary>
49+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
50+
[Fact]
51+
[WorkItem(3512, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3512")]
52+
public async Task TestThatDiagnosticIIsReportedOnSpacingWithUsingsInsideFileScopedNamespaceAsync()
53+
{
54+
var testCode = @"namespace Foo;
55+
[|using|] System;
56+
using System.Linq;
57+
using a = System.Collections;
58+
";
59+
60+
var fixedCode = @"namespace Foo;
61+
62+
using System;
63+
using System.Linq;
64+
using a = System.Collections;
65+
";
66+
67+
await VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
68+
}
69+
70+
/// <summary>
71+
/// Verifies that SA1516 is reported for member declarations inside a file scoped namespace.
72+
/// </summary>
73+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
74+
[Fact]
75+
[WorkItem(3512, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3512")]
76+
public async Task TestThatDiagnosticIIsReportedOnMemberDeclarationsInsideFileScopedNamespaceAsync()
77+
{
78+
var testCode = @"namespace Foo;
79+
[|public|] class Bar
80+
{
81+
}
82+
[|public|] enum Foobar
83+
{
84+
}
85+
";
86+
87+
var fixedCode = @"namespace Foo;
88+
89+
public class Bar
90+
{
91+
}
92+
93+
public enum Foobar
94+
{
95+
}
96+
";
97+
98+
await VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
99+
}
100+
101+
/// <summary>
102+
/// Verifies that SA1516 is reported for usings and member declarations inside a file scoped namespace.
103+
/// </summary>
104+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
105+
[Fact]
106+
[WorkItem(3512, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3512")]
107+
public async Task TestThatDiagnosticIIsReportedOnUsingsAndMemberDeclarationsInsideFileScopedNamespaceAsync()
108+
{
109+
var testCode = @"namespace Foo;
110+
[|using|] System;
111+
using System.Linq;
112+
using a = System.Collections;
113+
[|public|] class Bar
114+
{
115+
}
116+
[|public|] enum Foobar
117+
{
118+
}
119+
";
120+
121+
var fixedCode = @"namespace Foo;
122+
123+
using System;
124+
using System.Linq;
125+
using a = System.Collections;
126+
127+
public class Bar
128+
{
129+
}
130+
131+
public enum Foobar
132+
{
133+
}
134+
";
135+
136+
await VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
137+
}
138+
139+
/// <summary>
140+
/// Verifies that SA1516 is reported extern alias inside a file scoped namespace.
141+
/// </summary>
142+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
143+
[Fact]
144+
[WorkItem(3512, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3512")]
145+
public async Task TestThatDiagnosticIIsReportedOnExternAliasInsideFileScopedNamespaceAsync()
146+
{
147+
var testCode = @"namespace Foo;
148+
[|extern|] alias corlib;
149+
";
150+
151+
var fixedCode = @"namespace Foo;
152+
153+
extern alias corlib;
154+
";
155+
156+
await VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
157+
}
158+
159+
/// <summary>
160+
/// Verifies that SA1516 is reported extern alias and usings inside a file scoped namespace.
161+
/// </summary>
162+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
163+
[Fact]
164+
[WorkItem(3512, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3512")]
165+
public async Task TestThatDiagnosticIIsReportedOnExternAliasAndUsingsInsideFileScopedNamespaceAsync()
166+
{
167+
var testCode = @"namespace Foo;
168+
[|extern|] alias corlib;
169+
[|using|] System;
170+
using System.Linq;
171+
using a = System.Collections;
172+
";
173+
174+
var fixedCode = @"namespace Foo;
175+
176+
extern alias corlib;
177+
178+
using System;
179+
using System.Linq;
180+
using a = System.Collections;
181+
";
182+
183+
await VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
184+
}
185+
186+
/// <summary>
187+
/// Verifies that SA1516 is reported extern alias, usings and member declarations
188+
/// inside a file scoped namespace.
189+
/// </summary>
190+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
191+
[Fact]
192+
[WorkItem(3512, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3512")]
193+
public async Task TestThatDiagnosticIIsReportedOnExternAliasUsingsAndMemberDeclarationsInsideFileScopedNamespaceAsync()
194+
{
195+
var testCode = @"namespace Foo;
196+
[|extern|] alias corlib;
197+
[|using|] System;
198+
using System.Linq;
199+
using a = System.Collections;
200+
[|public|] class Bar
201+
{
202+
}
203+
[|public|] enum Foobar
204+
{
205+
}
206+
";
207+
208+
var fixedCode = @"namespace Foo;
209+
210+
extern alias corlib;
211+
212+
using System;
213+
using System.Linq;
214+
using a = System.Collections;
215+
216+
public class Bar
217+
{
218+
}
219+
220+
public enum Foobar
221+
{
222+
}
223+
";
224+
225+
await VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
226+
}
227+
228+
private static Task VerifyCSharpFixAsync(string testCode, string fixedCode)
229+
{
230+
var test = new CSharpTest
231+
{
232+
ReferenceAssemblies = ReferenceAssemblies.Net.Net60,
233+
TestState =
234+
{
235+
Sources = { testCode },
236+
},
237+
FixedCode = fixedCode,
238+
};
239+
240+
return test.RunAsync(CancellationToken.None);
241+
}
10242
}
11243
}

StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1516ElementsMustBeSeparatedByBlankLine.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ namespace StyleCop.Analyzers.LayoutRules
1515
using Microsoft.CodeAnalysis.Diagnostics;
1616
using Microsoft.CodeAnalysis.Text;
1717
using StyleCop.Analyzers.Helpers;
18+
using StyleCop.Analyzers.Lightup;
1819
using StyleCop.Analyzers.Settings.ObjectModel;
1920

2021
/// <summary>
@@ -87,6 +88,7 @@ internal class SA1516ElementsMustBeSeparatedByBlankLine : DiagnosticAnalyzer
8788
private static readonly Action<SyntaxNodeAnalysisContext> TypeDeclarationAction = HandleTypeDeclaration;
8889
private static readonly Action<SyntaxNodeAnalysisContext, StyleCopSettings> CompilationUnitAction = HandleCompilationUnit;
8990
private static readonly Action<SyntaxNodeAnalysisContext, StyleCopSettings> NamespaceDeclarationAction = HandleNamespaceDeclaration;
91+
private static readonly Action<SyntaxNodeAnalysisContext, StyleCopSettings> FileScopedNamespaceDeclarationAction = HandleFileScopedNamespaceDeclaration;
9092
private static readonly Action<SyntaxNodeAnalysisContext> BasePropertyDeclarationAction = HandleBasePropertyDeclaration;
9193

9294
private static readonly ImmutableDictionary<string, string> DiagnosticProperties = ImmutableDictionary<string, string>.Empty.Add(CodeFixActionKey, InsertBlankLineValue);
@@ -129,6 +131,7 @@ public override void Initialize(AnalysisContext context)
129131
context.RegisterSyntaxNodeAction(TypeDeclarationAction, SyntaxKinds.TypeDeclaration);
130132
context.RegisterSyntaxNodeAction(CompilationUnitAction, SyntaxKind.CompilationUnit);
131133
context.RegisterSyntaxNodeAction(NamespaceDeclarationAction, SyntaxKind.NamespaceDeclaration);
134+
context.RegisterSyntaxNodeAction(FileScopedNamespaceDeclarationAction, SyntaxKindEx.FileScopedNamespaceDeclaration);
132135
context.RegisterSyntaxNodeAction(BasePropertyDeclarationAction, SyntaxKinds.BasePropertyDeclaration);
133136
});
134137
}
@@ -212,6 +215,42 @@ private static void HandleCompilationUnit(SyntaxNodeAnalysisContext context, Sty
212215
}
213216
}
214217

218+
private static void HandleFileScopedNamespaceDeclaration(SyntaxNodeAnalysisContext context, StyleCopSettings settings)
219+
{
220+
var namespaceDeclaration = (BaseNamespaceDeclarationSyntaxWrapper)context.Node;
221+
222+
var usings = namespaceDeclaration.Usings;
223+
var members = namespaceDeclaration.Members;
224+
225+
HandleUsings(context, usings, settings);
226+
HandleMemberList(context, members);
227+
228+
if (namespaceDeclaration.Externs.Count > 0)
229+
{
230+
ReportIfThereIsNoBlankLine(context, namespaceDeclaration.Name, namespaceDeclaration.Externs[0]);
231+
}
232+
233+
if (namespaceDeclaration.Usings.Count > 0)
234+
{
235+
ReportIfThereIsNoBlankLine(context, namespaceDeclaration.Name, namespaceDeclaration.Usings[0]);
236+
237+
if (namespaceDeclaration.Externs.Count > 0)
238+
{
239+
ReportIfThereIsNoBlankLine(context, namespaceDeclaration.Externs[namespaceDeclaration.Externs.Count - 1], namespaceDeclaration.Usings[0]);
240+
}
241+
}
242+
243+
if (members.Count > 0)
244+
{
245+
ReportIfThereIsNoBlankLine(context, namespaceDeclaration.Name, members[0]);
246+
247+
if (namespaceDeclaration.Usings.Count > 0)
248+
{
249+
ReportIfThereIsNoBlankLine(context, usings[usings.Count - 1], members[0]);
250+
}
251+
}
252+
}
253+
215254
private static void HandleNamespaceDeclaration(SyntaxNodeAnalysisContext context, StyleCopSettings settings)
216255
{
217256
var namespaceDeclaration = (NamespaceDeclarationSyntax)context.Node;

0 commit comments

Comments
 (0)