Skip to content

Commit ff34293

Browse files
Ensure that newline is inserted after file scoped namespace declarations (#77821)
Fixes issue #67400
2 parents bb0cb49 + 46630b8 commit ff34293

File tree

4 files changed

+50
-23
lines changed

4 files changed

+50
-23
lines changed

src/Analyzers/CSharp/Tests/MisplacedUsingDirectives/MisplacedUsingDirectivesTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1042,7 +1042,7 @@ namespace TestNamespace
10421042
}
10431043

10441044
[Fact]
1045-
public Task WhenInsidePreferred_UsingsInBoth_UsingsMoved_FileScopedNamespaec()
1045+
public Task WhenInsidePreferred_UsingsInBoth_UsingsMoved_FileScopedNamespace()
10461046
{
10471047
var testCode = """
10481048
[|using Microsoft.CodeAnalysis;|]
@@ -1054,6 +1054,7 @@ namespace TestNamespace;
10541054

10551055
var fixedTestCode = """
10561056
namespace TestNamespace;
1057+
10571058
{|Warning:using Microsoft.CodeAnalysis;|}
10581059
10591060
using System;

src/Features/CSharpTest/ExtractClass/ExtractClassTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,6 +1269,7 @@ class Program : MyBase
12691269
// this is my real document header
12701270
12711271
namespace ConsoleApp185;
1272+
12721273
using System;
12731274
using System.Collections.Generic;
12741275

src/Workspaces/CSharpTest/Formatting/FormattingTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12087,6 +12087,25 @@ class C { }
1208712087
""");
1208812088
}
1208912089

12090+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67400")]
12091+
public async Task FileScopedNamespaceNewline()
12092+
{
12093+
await AssertFormatAsync(
12094+
expected: """
12095+
namespace Some.Namespace;
12096+
12097+
public class MyClass
12098+
{
12099+
}
12100+
""",
12101+
code: """
12102+
namespace Some.Namespace;
12103+
public class MyClass
12104+
{
12105+
}
12106+
""");
12107+
}
12108+
1209012109
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")]
1209112110
public async Task NewInImplicitObjectCreation()
1209212111
{

src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/TokenBasedFormattingRule.cs

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ public override AbstractFormattingRule WithOptions(SyntaxFormattingOptions optio
190190
return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
191191
}
192192

193-
// ; * or ; * for using directive
193+
// ; * or ; * for using directive and file scoped namespace
194194
if (previousToken.Kind() == SyntaxKind.SemicolonToken)
195195
{
196196
return AdjustNewLinesAfterSemicolonToken(previousToken, currentToken);
@@ -225,31 +225,37 @@ MemberDeclarationSyntax or
225225
private AdjustNewLinesOperation AdjustNewLinesAfterSemicolonToken(
226226
SyntaxToken previousToken, SyntaxToken currentToken)
227227
{
228-
// between anything that isn't a using directive, we don't touch newlines after a semicolon
229-
if (previousToken.Parent is not UsingDirectiveSyntax previousUsing)
230-
return CreateAdjustNewLinesOperation(0, AdjustNewLinesOption.PreserveLines);
231-
232-
// if the user is separating using-groups, and we're between two usings, and these
233-
// usings *should* be separated, then do so (if the usings were already properly
234-
// sorted).
235-
if (_options.SeparateImportDirectiveGroups &&
236-
currentToken.Parent is UsingDirectiveSyntax currentUsing &&
237-
UsingsAndExternAliasesOrganizer.NeedsGrouping(previousUsing, currentUsing))
238-
{
239-
RoslynDebug.AssertNotNull(currentUsing.Parent);
240-
241-
var usings = GetUsings(currentUsing.Parent);
242-
if (usings.IsSorted(UsingsAndExternAliasesDirectiveComparer.SystemFirstInstance) ||
243-
usings.IsSorted(UsingsAndExternAliasesDirectiveComparer.NormalInstance))
228+
if (previousToken.Parent is UsingDirectiveSyntax previousUsing)
229+
{
230+
// if the user is separating using-groups, and we're between two usings, and these
231+
// usings *should* be separated, then do so (if the usings were already properly
232+
// sorted).
233+
if (_options.SeparateImportDirectiveGroups &&
234+
currentToken.Parent is UsingDirectiveSyntax currentUsing &&
235+
UsingsAndExternAliasesOrganizer.NeedsGrouping(previousUsing, currentUsing))
244236
{
245-
// Force at least one blank line here.
246-
return CreateAdjustNewLinesOperation(2, AdjustNewLinesOption.PreserveLines);
237+
RoslynDebug.AssertNotNull(currentUsing.Parent);
238+
239+
var usings = GetUsings(currentUsing.Parent);
240+
if (usings.IsSorted(UsingsAndExternAliasesDirectiveComparer.SystemFirstInstance) ||
241+
usings.IsSorted(UsingsAndExternAliasesDirectiveComparer.NormalInstance))
242+
{
243+
// Force at least one blank line here.
244+
return CreateAdjustNewLinesOperation(2, AdjustNewLinesOption.PreserveLines);
245+
}
247246
}
247+
248+
// For all other cases where we have a using-directive, just make sure it's followed by
249+
// a new-line.
250+
return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
248251
}
249252

250-
// For all other cases where we have a using-directive, just make sure it's followed by
251-
// a new-line.
252-
return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
253+
// ensure that there is a newline after a file scoped namespace declaration
254+
if (previousToken.Parent is FileScopedNamespaceDeclarationSyntax)
255+
return CreateAdjustNewLinesOperation(2, AdjustNewLinesOption.PreserveLines);
256+
257+
// between anything that isn't a using directive or file scoped namespace declaration, we don't touch newlines after a semicolon
258+
return CreateAdjustNewLinesOperation(0, AdjustNewLinesOption.PreserveLines);
253259
}
254260

255261
private static SyntaxList<UsingDirectiveSyntax> GetUsings(SyntaxNode node)

0 commit comments

Comments
 (0)