diff --git a/src/EditorFeatures/CSharpTest/Formatting/CSharpNewDocumentFormattingServiceTests.cs b/src/EditorFeatures/CSharpTest/Formatting/CSharpNewDocumentFormattingServiceTests.cs index 12f72de194563..7213dbbad00a3 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/CSharpNewDocumentFormattingServiceTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/CSharpNewDocumentFormattingServiceTests.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Test.Utilities.Formatting; using Roslyn.Test.Utilities; using Xunit; @@ -171,6 +172,31 @@ internal class C }); } + [Fact] + public async Task TestAccessibilityModifiers_FileScopedNamespace() + { + await TestAsync(testCode: @"using System; + +namespace Goo +{ + class C + { + } +}", + expected: @"using System; + +namespace Goo; +internal class C +{ +} +", + options: new (OptionKey, object)[] + { + (new OptionKey(CSharpCodeStyleOptions.NamespaceDeclarations), new CodeStyleOption2(NamespaceDeclarationPreference.FileScoped, NotificationOption2.Error)), + (new OptionKey(CodeStyleOptions2.RequireAccessibilityModifiers, Language), new CodeStyleOption2(AccessibilityModifiersRequired.Always, NotificationOption2.Error)) + }); + } + [Fact] [WorkItem(55703, "https://github.com/dotnet/roslyn/issues/55703")] public async Task TestAccessibilityModifiers_IgnoresPartial() diff --git a/src/EditorFeatures/TestUtilities/Formatting/AbstractNewDocumentFormattingServiceTests.cs b/src/EditorFeatures/TestUtilities/Formatting/AbstractNewDocumentFormattingServiceTests.cs index dc38f4d5bff3c..c51a70e87b39a 100644 --- a/src/EditorFeatures/TestUtilities/Formatting/AbstractNewDocumentFormattingServiceTests.cs +++ b/src/EditorFeatures/TestUtilities/Formatting/AbstractNewDocumentFormattingServiceTests.cs @@ -40,6 +40,14 @@ internal Task TestAsync(string testCode, string expected, (Option2, T)[]? parseOptions); } + internal Task TestAsync(string testCode, string expected, (OptionKey, object)[]? options = null, ParseOptions? parseOptions = null) + { + return TestCoreAsync(testCode, + expected, + options, + parseOptions); + } + private async Task TestCoreAsync(string testCode, string expected, (OptionKey, T)[]? options, ParseOptions? parseOptions) { using (var workspace = CreateTestWorkspace(testCode, parseOptions)) @@ -60,9 +68,6 @@ private async Task TestCoreAsync(string testCode, string expected, (OptionKey var formattingService = document.GetRequiredLanguageService(); var formattedDocument = await formattingService.FormatNewDocumentAsync(document, hintDocument: null, CancellationToken.None); - // Format to match what AbstractEditorFactory does - formattedDocument = await Formatter.FormatAsync(formattedDocument); - var actual = await formattedDocument.GetTextAsync(); AssertEx.EqualOrDiff(expected, actual.ToString()); } diff --git a/src/Features/Core/Portable/Formatting/AbstractNewDocumentFormattingService.cs b/src/Features/Core/Portable/Formatting/AbstractNewDocumentFormattingService.cs index ab129104e169e..8be7bfe176e3e 100644 --- a/src/Features/Core/Portable/Formatting/AbstractNewDocumentFormattingService.cs +++ b/src/Features/Core/Portable/Formatting/AbstractNewDocumentFormattingService.cs @@ -7,8 +7,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host.Mef; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting { @@ -41,7 +43,16 @@ public async Task FormatNewDocumentAsync(Document document, Document? // other, so this shouldn't cause problems. try { + // First we ask the provider to "format" the document. This could be formatting in terms + // of adjusting block scopes to file scopes etc., but it could also be more akin to fixers + // like adding access modifiers, or adding .ConfigureAwait() calls etc. document = await provider.FormatNewDocumentAsync(document, hintDocument, cancellationToken).ConfigureAwait(false); + + // Now that the above has changed the document, we use the code action engine to clean up the document + // before we call the next provider, otherwise they might not see things as they are meant to be. + // Because formatting providers often re-use code fix logic, they are often written assuming this will + // happen. + document = await CodeAction.CleanupDocumentAsync(document, cancellationToken).ConfigureAwait(false); } catch (Exception ex) when (FatalError.ReportAndCatchUnlessCanceled(ex, cancellationToken, ErrorSeverity.General)) {