From 43a164bb216c5dfeacc6be9ce76d57a7543b1809 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Jul 2021 14:29:30 -0700 Subject: [PATCH 01/45] Add style option (and UI) for preferring file scoped namespaces --- .../Impl/Options/Formatting/StyleViewModel.cs | 49 +++++++++++++++++++ .../Core/Def/ServicesVSResources.resx | 3 ++ .../Core/Def/xlf/ServicesVSResources.cs.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.de.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.es.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.fr.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.it.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.ja.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.ko.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.pl.xlf | 5 ++ .../Def/xlf/ServicesVSResources.pt-BR.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.ru.xlf | 5 ++ .../Core/Def/xlf/ServicesVSResources.tr.xlf | 5 ++ .../Def/xlf/ServicesVSResources.zh-Hans.xlf | 5 ++ .../Def/xlf/ServicesVSResources.zh-Hant.xlf | 5 ++ .../CodeStyle/CSharpCodeStyleOptions.cs | 6 +++ 16 files changed, 123 insertions(+) diff --git a/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs b/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs index 41467fe1ed25d..7570bba67b407 100644 --- a/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs +++ b/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs @@ -857,6 +857,54 @@ public int Age }} //] }} +"; + + private static readonly string s_preferFileScopedNamespace = $@" +//[ +// {ServicesVSResources.Prefer_colon} +using System; + +namespace A.B.C; + +public class Program +{{ +}} +//] +//[ +// {ServicesVSResources.Over_colon} +using System; + +namespace A.B.C +{{ + public class Program + {{ + }} +}} +//] +"; + + private static readonly string s_preferBlockNamespace = $@" +//[ +// {ServicesVSResources.Prefer_colon} +using System; + +namespace A.B.C +{{ + public class Program + {{ + }} +}} +//] +//[ +// {ServicesVSResources.Over_colon} +using System; + +namespace A.B.C; + +public class Program +{{ +}} +//] "; private static readonly string s_preferSimpleUsingStatement = $@" @@ -1998,6 +2046,7 @@ internal StyleViewModel(OptionStore optionStore, IServiceProvider serviceProvide // Code block AddBracesOptions(optionStore, codeBlockPreferencesGroupTitle); + CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferFileScopedNamespace, ServicesVSResources.Prefer_file_scoped_namespace, s_preferFileScopedNamespace, s_preferBlockNamespace, this, optionStore, codeBlockPreferencesGroupTitle)); CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CodeStyleOptions2.PreferAutoProperties, ServicesVSResources.analyzer_Prefer_auto_properties, s_preferAutoProperties, s_preferAutoProperties, this, optionStore, codeBlockPreferencesGroupTitle)); CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferSimpleUsingStatement, ServicesVSResources.Prefer_simple_using_statement, s_preferSimpleUsingStatement, s_preferSimpleUsingStatement, this, optionStore, codeBlockPreferencesGroupTitle)); CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CodeStyleOptions2.PreferSystemHashCode, ServicesVSResources.Prefer_System_HashCode_in_GetHashCode, s_preferSystemHashCode, s_preferSystemHashCode, this, optionStore, codeBlockPreferencesGroupTitle)); diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx index caabce2d12c40..b87663df6a4e7 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.resx +++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx @@ -1773,4 +1773,7 @@ I agree to all of the foregoing: Select an appropriate symbol to start value tracking + + Prefer file scoped namespace + \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf index 099d2851e104e..983b412b64713 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf @@ -852,6 +852,11 @@ Preferovat složená přiřazení + + Prefer file scoped namespace + Prefer file scoped namespace + + Prefer index operator Preferovat operátor indexu diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf index 9f1002b4bfab0..30f204e1ac5fe 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf @@ -852,6 +852,11 @@ Zusammengesetzte Zuweisungen bevorzugen + + Prefer file scoped namespace + Prefer file scoped namespace + + Prefer index operator Indexoperator bevorzugen diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf index 969c9d02e88fd..a2067090ded69 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf @@ -852,6 +852,11 @@ Preferir asignaciones compuestas + + Prefer file scoped namespace + Prefer file scoped namespace + + Prefer index operator Preferir operador de índice diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf index 679fc6a454c49..a83a384e345e3 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf @@ -852,6 +852,11 @@ Préférer les affectations composées + + Prefer file scoped namespace + Prefer file scoped namespace + + Prefer index operator Préférer l'opérateur d'index diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf index 5248bd7d012ab..a54a4bc68ce26 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf @@ -852,6 +852,11 @@ Preferisci assegnazioni composte + + Prefer file scoped namespace + Prefer file scoped namespace + + Prefer index operator Preferisci operatore di indice diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf index 9ebdfc4fada3b..ad552a217787f 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf @@ -852,6 +852,11 @@ 複合代入を優先 + + Prefer file scoped namespace + Prefer file scoped namespace + + Prefer index operator インデックス演算子を優先 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf index 5517b47a90ea3..d9b671716a0a9 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf @@ -852,6 +852,11 @@ 복합 대입 선호 + + Prefer file scoped namespace + Prefer file scoped namespace + + Prefer index operator 인덱스 연산자 선호 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf index a88cbdc00479b..879ed0a1b3487 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf @@ -852,6 +852,11 @@ Preferuj przypisania złożone + + Prefer file scoped namespace + Prefer file scoped namespace + + Prefer index operator Preferuj operator indeksowania diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf index 631be6dc09509..d0b3666e850ea 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf @@ -852,6 +852,11 @@ Preferir atribuições de compostos + + Prefer file scoped namespace + Prefer file scoped namespace + + Prefer index operator Preferir operador de índice diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf index 43e37e7fb7556..9685cd62101e7 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf @@ -852,6 +852,11 @@ Предпочитать составные назначения + + Prefer file scoped namespace + Prefer file scoped namespace + + Prefer index operator Предпочитать оператор index diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf index 2d6bb68bb0380..988193d97fe58 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf @@ -852,6 +852,11 @@ Bileşik atamaları tercih et + + Prefer file scoped namespace + Prefer file scoped namespace + + Prefer index operator Dizin işlecini tercih et diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf index 3496d40d78a79..e3db8c68d12c3 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf @@ -852,6 +852,11 @@ 首选复合赋值 + + Prefer file scoped namespace + Prefer file scoped namespace + + Prefer index operator 首选索引运算符 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf index aa3dee1116470..f604da633e660 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf @@ -852,6 +852,11 @@ 優先使用複合指派 + + Prefer file scoped namespace + Prefer file scoped namespace + + Prefer index operator 優先使用索引運算子 diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs index 58b1de4eecbec..59a8751c952f7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs @@ -295,6 +295,12 @@ private static Option2> CreateUsingDirectiv EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental", CodeStyleOptions2.TrueWithSilentEnforcement), new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(AllowBlankLineAfterColonInConstructorInitializer)}")}); + public static readonly Option2> PreferFileScopedNamespace = CreateOption( + CSharpCodeStyleOptionGroups.CodeBlockPreferences, nameof(PreferFileScopedNamespace), + defaultValue: CodeStyleOptions2.FalseWithSilentEnforcement, + "csharp_style_prefer_file_scoped_namespace", + $"TextEditor.CSharp.Specific.{nameof(PreferFileScopedNamespace)}"); + #if false public static readonly Option2> VarElsewhere = CreateOption( From 3a1c6fb6514318b105b53c2d32c73c1a668e716b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Jul 2021 18:20:22 -0700 Subject: [PATCH 02/45] Scaffolding --- ...BuiltInCodeStyleDiagnosticAnalyzer_Core.cs | 4 +- .../Core/Analyzers/EnforceOnBuildValues.cs | 2 + .../Core/Analyzers/IDEDiagnosticIds.cs | 3 + .../Portable/CSharpFeaturesResources.resx | 8 ++ .../ConvertNamespaceCodeFixProvider.cs | 71 ++++++++++++++ ...ConvertNamespaceCodeRefactoringProvider.cs | 82 ++++++++++++++++ .../ConvertNamespaceDiagnosticAnalyzer.cs | 98 +++++++++++++++++++ .../ConvertNamespaceHelper.cs | 97 ++++++++++++++++++ .../xlf/CSharpFeaturesResources.cs.xlf | 10 ++ .../xlf/CSharpFeaturesResources.de.xlf | 10 ++ .../xlf/CSharpFeaturesResources.es.xlf | 10 ++ .../xlf/CSharpFeaturesResources.fr.xlf | 10 ++ .../xlf/CSharpFeaturesResources.it.xlf | 10 ++ .../xlf/CSharpFeaturesResources.ja.xlf | 10 ++ .../xlf/CSharpFeaturesResources.ko.xlf | 10 ++ .../xlf/CSharpFeaturesResources.pl.xlf | 10 ++ .../xlf/CSharpFeaturesResources.pt-BR.xlf | 10 ++ .../xlf/CSharpFeaturesResources.ru.xlf | 10 ++ .../xlf/CSharpFeaturesResources.tr.xlf | 10 ++ .../xlf/CSharpFeaturesResources.zh-Hans.xlf | 10 ++ .../xlf/CSharpFeaturesResources.zh-Hant.xlf | 10 ++ 21 files changed, 493 insertions(+), 2 deletions(-) create mode 100644 src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs create mode 100644 src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs create mode 100644 src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs create mode 100644 src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs diff --git a/src/Analyzers/Core/Analyzers/AbstractBuiltInCodeStyleDiagnosticAnalyzer_Core.cs b/src/Analyzers/Core/Analyzers/AbstractBuiltInCodeStyleDiagnosticAnalyzer_Core.cs index 8d0a0b3688a4f..76272d2c65a65 100644 --- a/src/Analyzers/Core/Analyzers/AbstractBuiltInCodeStyleDiagnosticAnalyzer_Core.cs +++ b/src/Analyzers/Core/Analyzers/AbstractBuiltInCodeStyleDiagnosticAnalyzer_Core.cs @@ -50,13 +50,13 @@ protected static DiagnosticDescriptor CreateDescriptorWithId( string id, EnforceOnBuild enforceOnBuild, LocalizableString title, - LocalizableString messageFormat, + LocalizableString? messageFormat = null, bool isUnnecessary = false, bool isConfigurable = true, LocalizableString? description = null) #pragma warning disable RS0030 // Do not used banned APIs => new( - id, title, messageFormat, + id, title, messageFormat ?? title, DiagnosticCategory.Style, DiagnosticSeverity.Hidden, isEnabledByDefault: true, diff --git a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs index 4d721f4bce342..2b46f1504eb50 100644 --- a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs +++ b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs @@ -26,6 +26,8 @@ internal static class EnforceOnBuildValues public const EnforceOnBuild InvalidSuppressMessageAttribute = /*IDE0076*/ EnforceOnBuild.HighlyRecommended; public const EnforceOnBuild LegacyFormatSuppressMessageAttribute = /*IDE0077*/ EnforceOnBuild.HighlyRecommended; public const EnforceOnBuild RemoveConfusingSuppressionForIsExpression = /*IDE0080*/ EnforceOnBuild.HighlyRecommended; + public const EnforceOnBuild UseRegularNamespace = /*IDE0160*/ EnforceOnBuild.HighlyRecommended; + public const EnforceOnBuild UseFileScopedNamespace = /*IDE0161*/ EnforceOnBuild.HighlyRecommended; /* EnforceOnBuild.Recommended */ public const EnforceOnBuild UseThrowExpression = /*IDE0016*/ EnforceOnBuild.Recommended; diff --git a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs index 9dd78d4e0f845..0095c798b4bf5 100644 --- a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs +++ b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs @@ -162,6 +162,9 @@ internal static class IDEDiagnosticIds public const string UseNullCheckOverTypeCheckDiagnosticId = "IDE0150"; + public const string UseRegularNamespaceDiagnosticId = "IDE0160"; + public const string UseFileScopedNamespaceDiagnosticId = "IDE0161"; + // Analyzer error Ids public const string AnalyzerChangedId = "IDE1001"; public const string AnalyzerDependencyConflictId = "IDE1002"; diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx index 38cffbfe26c88..cdc3214538a68 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx @@ -628,4 +628,12 @@ record struct + + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + + + Convert to regular namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs new file mode 100644 index 0000000000000..ce1fb7981d91a --- /dev/null +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace +{ + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + internal class ConvertNamespaceCodeFixProvider : SyntaxEditorBasedCodeFixProvider + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public ConvertNamespaceCodeFixProvider() + { + } + + internal override CodeFixCategory CodeFixCategory => CodeFixCategory.CodeStyle; + public override ImmutableArray FixableDiagnosticIds + => ImmutableArray.Create(IDEDiagnosticIds.UseRegularNamespaceDiagnosticId, IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId); + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var diagnostic = context.Diagnostics.First(); + + var title = diagnostic.Id == IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId + ? CSharpFeaturesResources.Convert_to_file_scoped_namespace + : CSharpFeaturesResources.Convert_to_regular_namespace; + + context.RegisterCodeFix( + new MyCodeAction(title, c => FixAsync(context.Document, diagnostic, c)), + context.Diagnostics); + + return Task.CompletedTask; + } + + protected override Task FixAllAsync( + Document document, ImmutableArray diagnostics, + SyntaxEditor editor, CancellationToken cancellationToken) + { + var diagnostic = diagnostics.First(); + + var namespaceDecl = (BaseNamespaceDeclarationSyntax)diagnostic.Location.FindNode(cancellationToken); + var converted = ConvertNamespaceHelper.Convert(namespaceDecl); + + editor.ReplaceNode(namespaceDecl, converted); + return Task.CompletedTask; + } + + private class MyCodeAction : CodeAction.DocumentChangeAction + { + public MyCodeAction( + string title, Func> createChangedDocument) + : base(title, createChangedDocument, title) + { + } + } + } +} diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs new file mode 100644 index 0000000000000..adbdf9dc39c53 --- /dev/null +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace +{ + [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] + internal class ConvertNamespaceCodeRefactoringProvider : CodeRefactoringProvider + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public ConvertNamespaceCodeRefactoringProvider() + { + } + + public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) + { + var (document, span, cancellationToken) = context; + if (!span.IsEmpty) + return; + + var position = span.Start; + var root = (CompilationUnitSyntax)await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var token = root.FindToken(position); + var namespaceDecl = token.GetAncestor(); + if (namespaceDecl == null) + return; + + if (!IsValidPosition(namespaceDecl, position)) + return; + + var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + if (ConvertNamespaceHelper.CanOfferUseRegular(optionSet, namespaceDecl, forAnalyzer: false)) + { + context.RegisterRefactoring(new MyCodeAction( + CSharpFeaturesResources.Convert_to_regular_namespace, + c => ConvertNamespaceHelper.ConvertAsync(document, namespaceDecl, c))); + } + + if (ConvertNamespaceHelper.CanOfferUseFileScoped(optionSet, root, namespaceDecl, forAnalyzer: false)) + { + context.RegisterRefactoring(new MyCodeAction( + CSharpFeaturesResources.Convert_to_file_scoped_namespace, + c => ConvertNamespaceHelper.ConvertAsync(document, namespaceDecl, c))); + } + } + + private static bool IsValidPosition(BaseNamespaceDeclarationSyntax baseDeclaration, int position) + { + if (position < baseDeclaration.SpanStart) + return false; + + if (baseDeclaration is FileScopedNamespaceDeclarationSyntax fileScopedNamespace) + return position <= fileScopedNamespace.SemicolonToken.Span.End; + + if (baseDeclaration is NamespaceDeclarationSyntax namespaceDeclaration) + return position <= namespaceDeclaration.Name.Span.End; + + throw ExceptionUtilities.UnexpectedValue(baseDeclaration.Kind()); + } + + private class MyCodeAction : CodeAction.DocumentChangeAction + { + public MyCodeAction( + string title, Func> createChangedDocument) + : base(title, createChangedDocument, title) + { + } + } + } +} diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..79055a6d0a649 --- /dev/null +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal class ConvertNamespaceDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer + { + private static readonly DiagnosticDescriptor s_useRegularNamespaceDescriptor = CreateDescriptorWithId( + IDEDiagnosticIds.UseRegularNamespaceDiagnosticId, + EnforceOnBuildValues.UseRegularNamespace, + new LocalizableResourceString(nameof(CSharpFeaturesResources.Convert_to_regular_namespace), CSharpFeaturesResources.ResourceManager, typeof(CSharpFeaturesResources))); + + private static readonly DiagnosticDescriptor s_useFileScopedNamespaceDescriptor = CreateDescriptorWithId( + IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId, + EnforceOnBuildValues.UseFileScopedNamespace, + new LocalizableResourceString(nameof(CSharpFeaturesResources.Convert_to_file_scoped_namespace), CSharpFeaturesResources.ResourceManager, typeof(CSharpFeaturesResources))); + + private static readonly ImmutableDictionary s_descriptors = + ImmutableDictionary.Empty + .Add(s_useRegularNamespaceDescriptor, CSharpCodeStyleOptions.PreferFileScopedNamespace) + .Add(s_useFileScopedNamespaceDescriptor, CSharpCodeStyleOptions.PreferFileScopedNamespace); + + public ConvertNamespaceDiagnosticAnalyzer() + : base(s_descriptors, LanguageNames.CSharp) + { + } + + public sealed override DiagnosticAnalyzerCategory GetAnalyzerCategory() + => DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis; + + protected override void InitializeWorker(AnalysisContext context) + => context.RegisterSyntaxNodeAction(AnalyzeNamespace, SyntaxKind.NamespaceDeclaration, SyntaxKind.FileScopedNamespaceDeclaration); + + private void AnalyzeNamespace(SyntaxNodeAnalysisContext context) + { + var options = context.Options; + var namespaceDeclaration = (BaseNamespaceDeclarationSyntax)context.Node; + var syntaxTree = namespaceDeclaration.SyntaxTree; + + var cancellationToken = context.CancellationToken; + var root = (CompilationUnitSyntax)syntaxTree.GetRoot(cancellationToken); + + var optionSet = options.GetAnalyzerOptionSet(syntaxTree, cancellationToken); + + var diagnostic = AnalyzeNamespace(optionSet, root, namespaceDeclaration); + if (diagnostic != null) + context.ReportDiagnostic(diagnostic); + } + + private static Diagnostic? AnalyzeNamespace(OptionSet optionSet, CompilationUnitSyntax root, BaseNamespaceDeclarationSyntax declaration) + { + var tree = declaration.SyntaxTree; + var preferFileScopedNamespace = optionSet.GetOption(CSharpCodeStyleOptions.PreferFileScopedNamespace); + var severity = preferFileScopedNamespace.Notification.Severity; + + if (ConvertNamespaceHelper.CanOfferUseRegular(optionSet, declaration, forAnalyzer: true)) + { + var location = GetDiagnosticLocation(declaration, tree, severity); + + var additionalLocations = ImmutableArray.Create(declaration.GetLocation()); + return DiagnosticHelper.Create( + s_useRegularNamespaceDescriptor, location, severity, + additionalLocations, ImmutableDictionary.Empty); + } + + if (ConvertNamespaceHelper.CanOfferUseFileScoped(optionSet, root, declaration, forAnalyzer: true)) + { + var location = GetDiagnosticLocation(declaration, tree, severity); + + var additionalLocations = ImmutableArray.Create(declaration.GetLocation()); + return DiagnosticHelper.Create( + s_useFileScopedNamespaceDescriptor, location, severity, + additionalLocations, ImmutableDictionary.Empty); + } + + return null; + } + + private static Location GetDiagnosticLocation(BaseNamespaceDeclarationSyntax declaration, SyntaxTree tree, ReportDiagnostic severity) + { + // if the diagnostic is hidden, show it anywhere from the `namespace` keyword through the name. + // otherwise, if it's not hidden, just squiggle the name. + return severity.WithDefaultSeverity(DiagnosticSeverity.Hidden) == ReportDiagnostic.Hidden + ? tree.GetLocation(TextSpan.FromBounds(declaration.SpanStart, declaration.Name.Span.End)) + : declaration.Name.GetLocation(); + } + } +} diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs new file mode 100644 index 0000000000000..8e8260e781fa2 --- /dev/null +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs @@ -0,0 +1,97 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace +{ + internal static class ConvertNamespaceHelper + { + internal static bool CanOfferUseRegular(OptionSet optionSet, BaseNamespaceDeclarationSyntax declaration, bool forAnalyzer) + { + if (declaration is not FileScopedNamespaceDeclarationSyntax) + return false; + + var currentOptionValue = optionSet.GetOption(CSharpCodeStyleOptions.PreferFileScopedNamespace); + var preference = currentOptionValue.Value; + var userPrefersRegularNamespaces = preference == false; + var analyzerDisabled = currentOptionValue.Notification.Severity == ReportDiagnostic.Suppress; + + // If the user likes regular namespaces, then we offer regular namespaces from the diagnostic analyzer. + // If the user does not like regular namespaces then we offer regular namespaces bodies from the refactoring provider. + // If the analyzer is disabled completely, the refactoring is enabled in both directions. + var canOffer = userPrefersRegularNamespaces == forAnalyzer || (!forAnalyzer && analyzerDisabled); + return canOffer; + } + + internal static bool CanOfferUseFileScoped(OptionSet optionSet, CompilationUnitSyntax root, BaseNamespaceDeclarationSyntax declaration, bool forAnalyzer) + { + if (declaration is not NamespaceDeclarationSyntax) + return false; + + var currentOptionValue = optionSet.GetOption(CSharpCodeStyleOptions.PreferFileScopedNamespace); + var preference = currentOptionValue.Value; + var userPrefersFileScopedNamespaces = preference == true; + var analyzerDisabled = currentOptionValue.Notification.Severity == ReportDiagnostic.Suppress; + + // If the user likes file scoped namespaces, then we offer file scopedregular namespaces from the diagnostic analyzer. + // If the user does not like file scoped namespaces then we offer file scoped namespaces from the refactoring provider. + // If the analyzer is disabled completely, the refactoring is enabled in both directions. + var canOffer = userPrefersFileScopedNamespaces == forAnalyzer || (!forAnalyzer && analyzerDisabled); + if (!canOffer) + return false; + + // even if we could offer this here, we have to make sure it would be legal. A file scoped namespace is + // only legal if it's the only namespace in the file and there are no top level statements. + var tooManyNamespaces = root.DescendantNodesAndSelf(n => n is CompilationUnitSyntax || n is BaseNamespaceDeclarationSyntax) + .OfType() + .Take(2) + .Count() != 1; + if (tooManyNamespaces) + return false; + + if (root.Members.Any(m => m is GlobalStatementSyntax)) + return false; + + return true; + } + + public static async Task ConvertAsync( + Document document, BaseNamespaceDeclarationSyntax baseNamespace, CancellationToken cancellationToken) + { + var converted = Convert(baseNamespace); + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + + return document.WithSyntaxRoot(root.ReplaceNode(baseNamespace, converted)); + } + + public static BaseNamespaceDeclarationSyntax Convert(BaseNamespaceDeclarationSyntax baseNamespace) + { + return baseNamespace switch + { + FileScopedNamespaceDeclarationSyntax fileScopedNamespace => ConvertFileScopedNamespace(fileScopedNamespace), + NamespaceDeclarationSyntax namespaceDeclaration => ConvertNamespaceDeclaration(namespaceDeclaration), + _ => throw ExceptionUtilities.UnexpectedValue(baseNamespace.Kind()), + }; + } + + private static FileScopedNamespaceDeclarationSyntax ConvertNamespaceDeclaration(NamespaceDeclarationSyntax namespaceDeclaration) + { + throw new System.NotImplementedException(); + } + + private static NamespaceDeclarationSyntax ConvertFileScopedNamespace(FileScopedNamespaceDeclarationSyntax fileScopedNamespace) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf index 08ba0eb2114a7..05cfb388fb37e 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf @@ -92,11 +92,21 @@ Porovnat s {0} + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to method Převést na metodu + + Convert to regular namespace + Convert to regular namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to regular string Převést na běžný řetězec diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf index de9c02bc3832f..be4300375e1a0 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf @@ -92,11 +92,21 @@ Mit "{0}" vergleichen + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to method In Methode konvertieren + + Convert to regular namespace + Convert to regular namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to regular string In reguläre Zeichenfolge konvertieren diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf index 5c7ef32e33dc3..33cd42c16ae6b 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf @@ -92,11 +92,21 @@ Comparar con "{0}" + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to method Convertir al método + + Convert to regular namespace + Convert to regular namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to regular string Convertir en cadena regular diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf index 538d55038f6f8..71f3d98eeb02e 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf @@ -92,11 +92,21 @@ Comparer à '{0}' + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to method Convertir en méthode + + Convert to regular namespace + Convert to regular namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to regular string Convertir en chaîne classique diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf index 8f30b216c1f96..9a7c3fdffe22b 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf @@ -92,11 +92,21 @@ Confronta con '{0}' + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to method Converti in metodo + + Convert to regular namespace + Convert to regular namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to regular string Converti in stringa normale diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf index e208f3a042481..8456c67dff9e4 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf @@ -92,11 +92,21 @@ "{0}" と比較 + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to method メソッドに変換 + + Convert to regular namespace + Convert to regular namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to regular string 正規文字列に変換する diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf index 723cfd6052082..efd2b7a648b3d 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf @@ -92,11 +92,21 @@ '{0}'과(와) 비교 + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to method 메서드로 변환 + + Convert to regular namespace + Convert to regular namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to regular string 일반 문자열로 변환 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf index ef5c616525221..fda60e5141f1b 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf @@ -92,11 +92,21 @@ Porównaj z „{0}” + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to method Konwertuj na metodę + + Convert to regular namespace + Convert to regular namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to regular string Konwertuj na zwykły ciąg diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf index 5036c840aeb58..44167f5fcb886 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf @@ -92,11 +92,21 @@ Comparar a '{0}' + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to method Converter em método + + Convert to regular namespace + Convert to regular namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to regular string Converter para cadeia de caracteres regular diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf index 36ccd194ffd1d..494fcf7163122 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf @@ -92,11 +92,21 @@ Сравнить с "{0}" + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to method Преобразовать в метод + + Convert to regular namespace + Convert to regular namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to regular string Преобразовать в обычную строку diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf index ac6d1057e20c2..95b8ff509d218 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf @@ -92,11 +92,21 @@ '{0}' ile karşılaştır + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to method Yönteme dönüştür + + Convert to regular namespace + Convert to regular namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to regular string Normal dizeye dönüştür diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf index fbb88a49aabe3..0d9a284dd157a 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf @@ -92,11 +92,21 @@ 与 "{0}" 比较 + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to method 转换为方法 + + Convert to regular namespace + Convert to regular namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to regular string 转换为正则字符串 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf index 02922e9c7ae33..1017978393887 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf @@ -92,11 +92,21 @@ 與 '{0}' 比較 + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to method 轉換為方法 + + Convert to regular namespace + Convert to regular namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to regular string 轉換為一般字串 From f8b88541ce69a1ba2aac90f09b4ee4dbaf3a1d76 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Jul 2021 22:14:53 -0700 Subject: [PATCH 03/45] Working --- .../ConvertNamespaceCodeFixProvider.cs | 2 +- .../ConvertNamespaceDiagnosticAnalyzer.cs | 2 +- .../ConvertNamespaceHelper.cs | 23 +++++++++++++++++-- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs index ce1fb7981d91a..5abb248c373a9 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace { - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(ConvertNamespaceCodeFixProvider)), Shared] internal class ConvertNamespaceCodeFixProvider : SyntaxEditorBasedCodeFixProvider { [ImportingConstructor] diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs index 79055a6d0a649..5081be5de54bd 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs @@ -36,7 +36,7 @@ public ConvertNamespaceDiagnosticAnalyzer() } public sealed override DiagnosticAnalyzerCategory GetAnalyzerCategory() - => DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis; + => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; protected override void InitializeWorker(AnalysisContext context) => context.RegisterSyntaxNodeAction(AnalyzeNamespace, SyntaxKind.NamespaceDeclaration, SyntaxKind.FileScopedNamespaceDeclaration); diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs index 8e8260e781fa2..196ee23eafc23 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -86,12 +87,30 @@ public static BaseNamespaceDeclarationSyntax Convert(BaseNamespaceDeclarationSyn private static FileScopedNamespaceDeclarationSyntax ConvertNamespaceDeclaration(NamespaceDeclarationSyntax namespaceDeclaration) { - throw new System.NotImplementedException(); + return SyntaxFactory.FileScopedNamespaceDeclaration( + namespaceDeclaration.AttributeLists, + namespaceDeclaration.Modifiers, + namespaceDeclaration.NamespaceKeyword, + namespaceDeclaration.Name, + SyntaxFactory.Token(SyntaxKind.SemicolonToken).WithTrailingTrivia(namespaceDeclaration.OpenBraceToken.TrailingTrivia), + namespaceDeclaration.Externs, + namespaceDeclaration.Usings, + namespaceDeclaration.Members).WithAdditionalAnnotations(Formatter.Annotation); } private static NamespaceDeclarationSyntax ConvertFileScopedNamespace(FileScopedNamespaceDeclarationSyntax fileScopedNamespace) { - throw new System.NotImplementedException(); + return SyntaxFactory.NamespaceDeclaration( + fileScopedNamespace.AttributeLists, + fileScopedNamespace.Modifiers, + fileScopedNamespace.NamespaceKeyword, + fileScopedNamespace.Name, + SyntaxFactory.Token(SyntaxKind.OpenBraceToken).WithTrailingTrivia(fileScopedNamespace.SemicolonToken.TrailingTrivia), + fileScopedNamespace.Externs, + fileScopedNamespace.Usings, + fileScopedNamespace.Members, + SyntaxFactory.Token(SyntaxKind.CloseBraceToken), + semicolonToken: default).WithAdditionalAnnotations(Formatter.Annotation); } } } From 4e04d3a0dc096eb1d28627eb890599f3bf9c27b0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Jul 2021 22:23:33 -0700 Subject: [PATCH 04/45] Whitespace --- .../ConvertNamespace/ConvertNamespaceHelper.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs index 196ee23eafc23..129d105ca971f 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs @@ -36,7 +36,10 @@ internal static bool CanOfferUseRegular(OptionSet optionSet, BaseNamespaceDeclar internal static bool CanOfferUseFileScoped(OptionSet optionSet, CompilationUnitSyntax root, BaseNamespaceDeclarationSyntax declaration, bool forAnalyzer) { - if (declaration is not NamespaceDeclarationSyntax) + if (declaration is not NamespaceDeclarationSyntax namespaceDeclaration) + return false; + + if (namespaceDeclaration.OpenBraceToken.IsMissing) return false; var currentOptionValue = optionSet.GetOption(CSharpCodeStyleOptions.PreferFileScopedNamespace); @@ -87,12 +90,20 @@ public static BaseNamespaceDeclarationSyntax Convert(BaseNamespaceDeclarationSyn private static FileScopedNamespaceDeclarationSyntax ConvertNamespaceDeclaration(NamespaceDeclarationSyntax namespaceDeclaration) { + var semicolon = SyntaxFactory.Token(SyntaxKind.SemicolonToken).WithTrailingTrivia(namespaceDeclaration.OpenBraceToken.TrailingTrivia); + var firstBodyToken = namespaceDeclaration.OpenBraceToken.GetNextToken(); + if (firstBodyToken != namespaceDeclaration.CloseBraceToken) + { + if (!firstBodyToken.LeadingTrivia.Any(SyntaxKind.EndOfLineTrivia)) + semicolon = semicolon.WithAppendedTrailingTrivia(SyntaxFactory.ElasticCarriageReturnLineFeed); + } + return SyntaxFactory.FileScopedNamespaceDeclaration( namespaceDeclaration.AttributeLists, namespaceDeclaration.Modifiers, namespaceDeclaration.NamespaceKeyword, namespaceDeclaration.Name, - SyntaxFactory.Token(SyntaxKind.SemicolonToken).WithTrailingTrivia(namespaceDeclaration.OpenBraceToken.TrailingTrivia), + semicolon, namespaceDeclaration.Externs, namespaceDeclaration.Usings, namespaceDeclaration.Members).WithAdditionalAnnotations(Formatter.Annotation); From e1ca37a70c2e6c4dfec14d1f32b75dda917d9fd5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Jul 2021 22:38:50 -0700 Subject: [PATCH 05/45] working --- .../ConvertNamespaceHelper.cs | 54 +++++++++++++++---- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs index 129d105ca971f..e50d791961b37 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs @@ -88,30 +88,55 @@ public static BaseNamespaceDeclarationSyntax Convert(BaseNamespaceDeclarationSyn }; } - private static FileScopedNamespaceDeclarationSyntax ConvertNamespaceDeclaration(NamespaceDeclarationSyntax namespaceDeclaration) + private static bool HasLeadingBlankLine( + SyntaxToken token, out SyntaxToken withoutBlankLine) { - var semicolon = SyntaxFactory.Token(SyntaxKind.SemicolonToken).WithTrailingTrivia(namespaceDeclaration.OpenBraceToken.TrailingTrivia); - var firstBodyToken = namespaceDeclaration.OpenBraceToken.GetNextToken(); - if (firstBodyToken != namespaceDeclaration.CloseBraceToken) + var leadingTrivia = token.LeadingTrivia; + + if (leadingTrivia.Count == 1 && leadingTrivia[0].Kind() == SyntaxKind.EndOfLineTrivia) + { + withoutBlankLine = token.WithLeadingTrivia(leadingTrivia.RemoveAt(0)); + return true; + } + + if (leadingTrivia.Count == 2 && leadingTrivia[0].IsKind(SyntaxKind.WhitespaceTrivia) && leadingTrivia[1].IsKind(SyntaxKind.EndOfLineTrivia)) { - if (!firstBodyToken.LeadingTrivia.Any(SyntaxKind.EndOfLineTrivia)) - semicolon = semicolon.WithAppendedTrailingTrivia(SyntaxFactory.ElasticCarriageReturnLineFeed); + withoutBlankLine = token.WithLeadingTrivia(leadingTrivia.Skip(2)); + return true; } - return SyntaxFactory.FileScopedNamespaceDeclaration( + withoutBlankLine = default; + return false; + } + + private static FileScopedNamespaceDeclarationSyntax ConvertNamespaceDeclaration(NamespaceDeclarationSyntax namespaceDeclaration) + { + var fileScopedNamespace = SyntaxFactory.FileScopedNamespaceDeclaration( namespaceDeclaration.AttributeLists, namespaceDeclaration.Modifiers, namespaceDeclaration.NamespaceKeyword, namespaceDeclaration.Name, - semicolon, + SyntaxFactory.Token(SyntaxKind.SemicolonToken).WithTrailingTrivia(namespaceDeclaration.OpenBraceToken.TrailingTrivia), namespaceDeclaration.Externs, namespaceDeclaration.Usings, namespaceDeclaration.Members).WithAdditionalAnnotations(Formatter.Annotation); + + // Ensure there's a blank line between the namespace line and the first body member. + var firstBodyToken = fileScopedNamespace.SemicolonToken.GetNextToken(); + if (firstBodyToken.Kind() != SyntaxKind.EndOfFileToken && + !HasLeadingBlankLine(firstBodyToken, out _)) + { + fileScopedNamespace = fileScopedNamespace.ReplaceToken( + firstBodyToken, + firstBodyToken.WithPrependedLeadingTrivia(SyntaxFactory.ElasticCarriageReturnLineFeed)); + } + + return fileScopedNamespace; } private static NamespaceDeclarationSyntax ConvertFileScopedNamespace(FileScopedNamespaceDeclarationSyntax fileScopedNamespace) { - return SyntaxFactory.NamespaceDeclaration( + var namespaceDeclaration = SyntaxFactory.NamespaceDeclaration( fileScopedNamespace.AttributeLists, fileScopedNamespace.Modifiers, fileScopedNamespace.NamespaceKeyword, @@ -122,6 +147,17 @@ private static NamespaceDeclarationSyntax ConvertFileScopedNamespace(FileScopedN fileScopedNamespace.Members, SyntaxFactory.Token(SyntaxKind.CloseBraceToken), semicolonToken: default).WithAdditionalAnnotations(Formatter.Annotation); + + // Ensure there is no errant blank line between the open curly and the first body element. + var firstBodyToken = namespaceDeclaration.OpenBraceToken.GetNextToken(); + if (firstBodyToken != namespaceDeclaration.CloseBraceToken && + firstBodyToken.Kind() != SyntaxKind.EndOfFileToken && + HasLeadingBlankLine(firstBodyToken, out var firstBodyTokenWithoutBlankLine)) + { + namespaceDeclaration = namespaceDeclaration.ReplaceToken(firstBodyToken, firstBodyTokenWithoutBlankLine); + } + + return namespaceDeclaration; } } } From da4a8372352171fb115b08655c4d9b0c483f995b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Jul 2021 22:44:54 -0700 Subject: [PATCH 06/45] Fix check --- .../Portable/ConvertNamespace/ConvertNamespaceHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs index e50d791961b37..f67cef109d640 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs @@ -93,13 +93,13 @@ private static bool HasLeadingBlankLine( { var leadingTrivia = token.LeadingTrivia; - if (leadingTrivia.Count == 1 && leadingTrivia[0].Kind() == SyntaxKind.EndOfLineTrivia) + if (leadingTrivia.Count >= 1 && leadingTrivia[0].Kind() == SyntaxKind.EndOfLineTrivia) { withoutBlankLine = token.WithLeadingTrivia(leadingTrivia.RemoveAt(0)); return true; } - if (leadingTrivia.Count == 2 && leadingTrivia[0].IsKind(SyntaxKind.WhitespaceTrivia) && leadingTrivia[1].IsKind(SyntaxKind.EndOfLineTrivia)) + if (leadingTrivia.Count >= 2 && leadingTrivia[0].IsKind(SyntaxKind.WhitespaceTrivia) && leadingTrivia[1].IsKind(SyntaxKind.EndOfLineTrivia)) { withoutBlankLine = token.WithLeadingTrivia(leadingTrivia.Skip(2)); return true; From fe29b72770f6e5f03203babee431080a19269f6f Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 21 Jul 2021 22:57:15 -0700 Subject: [PATCH 07/45] Update src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs --- .../ConvertNamespace/ConvertNamespaceCodeFixProvider.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs index 5abb248c373a9..cd5d5de229e77 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs @@ -61,8 +61,7 @@ protected override Task FixAllAsync( private class MyCodeAction : CodeAction.DocumentChangeAction { - public MyCodeAction( - string title, Func> createChangedDocument) + public MyCodeAction(string title, Func> createChangedDocument) : base(title, createChangedDocument, title) { } From 89f76f34d360639f6be207b34a0d24d286545f35 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 21 Jul 2021 22:58:29 -0700 Subject: [PATCH 08/45] Update src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs --- .../ConvertNamespaceCodeRefactoringProvider.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs index adbdf9dc39c53..8a5fafad9fccc 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs @@ -72,8 +72,7 @@ private static bool IsValidPosition(BaseNamespaceDeclarationSyntax baseDeclarati private class MyCodeAction : CodeAction.DocumentChangeAction { - public MyCodeAction( - string title, Func> createChangedDocument) + public MyCodeAction(string title, Func> createChangedDocument) : base(title, createChangedDocument, title) { } From ec9a8c1af7d497bce6e17d5c0c068749ac4b51ba Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Jul 2021 23:03:50 -0700 Subject: [PATCH 09/45] Simplify --- .../ConvertNamespaceCodeFixProvider.cs | 3 +- ...ConvertNamespaceCodeRefactoringProvider.cs | 22 ++++------- .../ConvertNamespaceDiagnosticAnalyzer.cs | 38 +++++++------------ .../ConvertNamespaceHelper.cs | 2 +- .../Impl/Options/Formatting/StyleViewModel.cs | 8 ---- 5 files changed, 23 insertions(+), 50 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs index 5abb248c373a9..cd5d5de229e77 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs @@ -61,8 +61,7 @@ protected override Task FixAllAsync( private class MyCodeAction : CodeAction.DocumentChangeAction { - public MyCodeAction( - string title, Func> createChangedDocument) + public MyCodeAction(string title, Func> createChangedDocument) : base(title, createChangedDocument, title) { } diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs index adbdf9dc39c53..2d310a0167f5f 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs @@ -41,19 +41,14 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte return; var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - if (ConvertNamespaceHelper.CanOfferUseRegular(optionSet, namespaceDecl, forAnalyzer: false)) - { - context.RegisterRefactoring(new MyCodeAction( - CSharpFeaturesResources.Convert_to_regular_namespace, - c => ConvertNamespaceHelper.ConvertAsync(document, namespaceDecl, c))); - } + var title = + ConvertNamespaceHelper.CanOfferUseRegular(optionSet, namespaceDecl, forAnalyzer: false) ? CSharpFeaturesResources.Convert_to_regular_namespace : + ConvertNamespaceHelper.CanOfferUseFileScoped(optionSet, root, namespaceDecl, forAnalyzer: false) ? CSharpFeaturesResources.Convert_to_file_scoped_namespace : null; + if (title == null) + return; - if (ConvertNamespaceHelper.CanOfferUseFileScoped(optionSet, root, namespaceDecl, forAnalyzer: false)) - { - context.RegisterRefactoring(new MyCodeAction( - CSharpFeaturesResources.Convert_to_file_scoped_namespace, - c => ConvertNamespaceHelper.ConvertAsync(document, namespaceDecl, c))); - } + context.RegisterRefactoring(new MyCodeAction( + title, c => ConvertNamespaceHelper.ConvertAsync(document, namespaceDecl, c))); } private static bool IsValidPosition(BaseNamespaceDeclarationSyntax baseDeclaration, int position) @@ -72,8 +67,7 @@ private static bool IsValidPosition(BaseNamespaceDeclarationSyntax baseDeclarati private class MyCodeAction : CodeAction.DocumentChangeAction { - public MyCodeAction( - string title, Func> createChangedDocument) + public MyCodeAction(string title, Func> createChangedDocument) : base(title, createChangedDocument, title) { } diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs index 5081be5de54bd..d86e9add47166 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs @@ -63,36 +63,24 @@ private void AnalyzeNamespace(SyntaxNodeAnalysisContext context) var preferFileScopedNamespace = optionSet.GetOption(CSharpCodeStyleOptions.PreferFileScopedNamespace); var severity = preferFileScopedNamespace.Notification.Severity; - if (ConvertNamespaceHelper.CanOfferUseRegular(optionSet, declaration, forAnalyzer: true)) - { - var location = GetDiagnosticLocation(declaration, tree, severity); + var descriptor = + ConvertNamespaceHelper.CanOfferUseRegular(optionSet, declaration, forAnalyzer: true) ? s_useRegularNamespaceDescriptor : + ConvertNamespaceHelper.CanOfferUseFileScoped(optionSet, root, declaration, forAnalyzer: true) ? s_useFileScopedNamespaceDescriptor : null; + if (descriptor == null) + return null; - var additionalLocations = ImmutableArray.Create(declaration.GetLocation()); - return DiagnosticHelper.Create( - s_useRegularNamespaceDescriptor, location, severity, - additionalLocations, ImmutableDictionary.Empty); - } - - if (ConvertNamespaceHelper.CanOfferUseFileScoped(optionSet, root, declaration, forAnalyzer: true)) - { - var location = GetDiagnosticLocation(declaration, tree, severity); - - var additionalLocations = ImmutableArray.Create(declaration.GetLocation()); - return DiagnosticHelper.Create( - s_useFileScopedNamespaceDescriptor, location, severity, - additionalLocations, ImmutableDictionary.Empty); - } - - return null; - } - - private static Location GetDiagnosticLocation(BaseNamespaceDeclarationSyntax declaration, SyntaxTree tree, ReportDiagnostic severity) - { // if the diagnostic is hidden, show it anywhere from the `namespace` keyword through the name. // otherwise, if it's not hidden, just squiggle the name. - return severity.WithDefaultSeverity(DiagnosticSeverity.Hidden) == ReportDiagnostic.Hidden + var diagnosticLocation = severity.WithDefaultSeverity(DiagnosticSeverity.Hidden) == ReportDiagnostic.Hidden ? tree.GetLocation(TextSpan.FromBounds(declaration.SpanStart, declaration.Name.Span.End)) : declaration.Name.GetLocation(); + + return DiagnosticHelper.Create( + descriptor, + GetDiagnosticLocation(declaration, tree, severity), + severity, + ImmutableArray.Create(declaration.GetLocation()), + ImmutableDictionary.Empty); } } } diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs index f67cef109d640..8976532430d81 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs @@ -47,7 +47,7 @@ internal static bool CanOfferUseFileScoped(OptionSet optionSet, CompilationUnitS var userPrefersFileScopedNamespaces = preference == true; var analyzerDisabled = currentOptionValue.Notification.Severity == ReportDiagnostic.Suppress; - // If the user likes file scoped namespaces, then we offer file scopedregular namespaces from the diagnostic analyzer. + // If the user likes file scoped namespaces, then we offer file scoped namespaces from the diagnostic analyzer. // If the user does not like file scoped namespaces then we offer file scoped namespaces from the refactoring provider. // If the analyzer is disabled completely, the refactoring is enabled in both directions. var canOffer = userPrefersFileScopedNamespaces == forAnalyzer || (!forAnalyzer && analyzerDisabled); diff --git a/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs b/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs index 7570bba67b407..460f3e5f6129e 100644 --- a/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs +++ b/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs @@ -862,8 +862,6 @@ public int Age private static readonly string s_preferFileScopedNamespace = $@" //[ // {ServicesVSResources.Prefer_colon} -using System; - namespace A.B.C; public class Program @@ -872,8 +870,6 @@ public class Program //] //[ // {ServicesVSResources.Over_colon} -using System; - namespace A.B.C {{ public class Program @@ -886,8 +882,6 @@ public class Program private static readonly string s_preferBlockNamespace = $@" //[ // {ServicesVSResources.Prefer_colon} -using System; - namespace A.B.C {{ public class Program @@ -897,8 +891,6 @@ public class Program //] //[ // {ServicesVSResources.Over_colon} -using System; - namespace A.B.C; public class Program From 6143064e8b2c0189f58da0b29b0bbc520eb58853 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Jul 2021 23:07:18 -0700 Subject: [PATCH 10/45] Simplify --- .../ConvertNamespaceHelper.cs | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs index 8976532430d81..03c1f4974a4e0 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs @@ -22,15 +22,15 @@ internal static bool CanOfferUseRegular(OptionSet optionSet, BaseNamespaceDeclar if (declaration is not FileScopedNamespaceDeclarationSyntax) return false; - var currentOptionValue = optionSet.GetOption(CSharpCodeStyleOptions.PreferFileScopedNamespace); - var preference = currentOptionValue.Value; - var userPrefersRegularNamespaces = preference == false; - var analyzerDisabled = currentOptionValue.Notification.Severity == ReportDiagnostic.Suppress; + var option = optionSet.GetOption(CSharpCodeStyleOptions.PreferFileScopedNamespace); + var userPrefersRegularNamespaces = !option.Value; + var analyzerDisabled = option.Notification.Severity == ReportDiagnostic.Suppress; + var forRefactoring = !forAnalyzer; // If the user likes regular namespaces, then we offer regular namespaces from the diagnostic analyzer. // If the user does not like regular namespaces then we offer regular namespaces bodies from the refactoring provider. // If the analyzer is disabled completely, the refactoring is enabled in both directions. - var canOffer = userPrefersRegularNamespaces == forAnalyzer || (!forAnalyzer && analyzerDisabled); + var canOffer = userPrefersRegularNamespaces == forAnalyzer || (forRefactoring && analyzerDisabled); return canOffer; } @@ -42,15 +42,15 @@ internal static bool CanOfferUseFileScoped(OptionSet optionSet, CompilationUnitS if (namespaceDeclaration.OpenBraceToken.IsMissing) return false; - var currentOptionValue = optionSet.GetOption(CSharpCodeStyleOptions.PreferFileScopedNamespace); - var preference = currentOptionValue.Value; - var userPrefersFileScopedNamespaces = preference == true; - var analyzerDisabled = currentOptionValue.Notification.Severity == ReportDiagnostic.Suppress; + var option = optionSet.GetOption(CSharpCodeStyleOptions.PreferFileScopedNamespace); + var userPrefersFileScopedNamespaces = option.Value; + var analyzerDisabled = option.Notification.Severity == ReportDiagnostic.Suppress; + var forRefactoring = !forAnalyzer; // If the user likes file scoped namespaces, then we offer file scoped namespaces from the diagnostic analyzer. // If the user does not like file scoped namespaces then we offer file scoped namespaces from the refactoring provider. // If the analyzer is disabled completely, the refactoring is enabled in both directions. - var canOffer = userPrefersFileScopedNamespaces == forAnalyzer || (!forAnalyzer && analyzerDisabled); + var canOffer = userPrefersFileScopedNamespaces == forAnalyzer || (forRefactoring && analyzerDisabled); if (!canOffer) return false; @@ -72,10 +72,8 @@ internal static bool CanOfferUseFileScoped(OptionSet optionSet, CompilationUnitS public static async Task ConvertAsync( Document document, BaseNamespaceDeclarationSyntax baseNamespace, CancellationToken cancellationToken) { - var converted = Convert(baseNamespace); var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - - return document.WithSyntaxRoot(root.ReplaceNode(baseNamespace, converted)); + return document.WithSyntaxRoot(root.ReplaceNode(baseNamespace, Convert(baseNamespace))); } public static BaseNamespaceDeclarationSyntax Convert(BaseNamespaceDeclarationSyntax baseNamespace) From eab3b8fcfefad25b82312383f683333989ce9d56 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 21 Jul 2021 23:43:13 -0700 Subject: [PATCH 11/45] Fix --- .../ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs index d86e9add47166..de6a25173be7b 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs @@ -77,7 +77,7 @@ private void AnalyzeNamespace(SyntaxNodeAnalysisContext context) return DiagnosticHelper.Create( descriptor, - GetDiagnosticLocation(declaration, tree, severity), + diagnosticLocation, severity, ImmutableArray.Create(declaration.GetLocation()), ImmutableDictionary.Empty); From 92e56ae3df5a3a0fc0bf7a7a0c2ee46d87fcdcfc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 12:01:37 -0700 Subject: [PATCH 12/45] Add name --- src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs | 1 + .../ConvertNamespace/ConvertNamespaceCodeFixProvider.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs index 7d5d581901350..3c6e8c8d4582c 100644 --- a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs +++ b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs @@ -17,6 +17,7 @@ internal static class PredefinedCodeFixProviderNames public const string ChangeToYield = nameof(ChangeToYield); public const string ConvertToAsync = nameof(ConvertToAsync); public const string ConvertToIterator = nameof(ConvertToIterator); + public const string ConvertNamespace = nameof(ConvertNamespace); public const string CorrectNextControlVariable = nameof(CorrectNextControlVariable); public const string ConvertTypeOfToNameOf = nameof(ConvertTypeOfToNameOf); public const string RemoveDocCommentNode = nameof(RemoveDocCommentNode); diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs index cd5d5de229e77..c4fc6c0d193dc 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace { - [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(ConvertNamespaceCodeFixProvider)), Shared] + [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.ConvertNamespace), Shared] internal class ConvertNamespaceCodeFixProvider : SyntaxEditorBasedCodeFixProvider { [ImportingConstructor] From 69eaa73664477d502e6fb57c04f74840d409da74 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 13:02:48 -0700 Subject: [PATCH 13/45] Code gen file scoped namespaces --- .../CSharpCodeGenerationService.cs | 2 +- .../CodeGeneration/NamespaceGenerator.cs | 47 +++++++++++++------ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpCodeGenerationService.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpCodeGenerationService.cs index f6641b65c8118..109c58a8151da 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpCodeGenerationService.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpCodeGenerationService.cs @@ -646,7 +646,7 @@ public override SyntaxNode CreateNamedTypeDeclaration( public override SyntaxNode CreateNamespaceDeclaration( INamespaceSymbol @namespace, CodeGenerationDestination destination, CodeGenerationOptions options, CancellationToken cancellationToken) { - return NamespaceGenerator.GenerateNamespaceDeclaration(this, @namespace, options, cancellationToken); + return NamespaceGenerator.GenerateNamespaceDeclaration(this, @namespace, destination, options, options.ParseOptions, cancellationToken); } private static TDeclarationNode UpdateDeclarationModifiers(TDeclarationNode declaration, Func computeNewModifiersList) diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs index 20dc341972cf4..36b5345159585 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeGeneration; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Utilities; @@ -28,7 +29,11 @@ public static BaseNamespaceDeclarationSyntax AddNamespaceTo( IList availableIndices, CancellationToken cancellationToken) { - var declaration = GenerateNamespaceDeclaration(service, @namespace, options, cancellationToken); + var declaration = GenerateNamespaceDeclaration( + service, @namespace, + CodeGenerationDestination.Namespace, + options, destination?.SyntaxTree.Options ?? options.ParseOptions, + cancellationToken); if (declaration is not BaseNamespaceDeclarationSyntax namespaceDeclaration) throw new ArgumentException(CSharpWorkspaceResources.Namespace_can_not_be_added_in_this_destination); @@ -44,7 +49,11 @@ public static CompilationUnitSyntax AddNamespaceTo( IList availableIndices, CancellationToken cancellationToken) { - var declaration = GenerateNamespaceDeclaration(service, @namespace, options, cancellationToken); + var declaration = GenerateNamespaceDeclaration( + service, @namespace, + CodeGenerationDestination.CompilationUnit, + options, destination?.SyntaxTree.Options ?? options.ParseOptions, + cancellationToken); if (declaration is not NamespaceDeclarationSyntax namespaceDeclaration) throw new ArgumentException(CSharpWorkspaceResources.Namespace_can_not_be_added_in_this_destination); @@ -55,17 +64,20 @@ public static CompilationUnitSyntax AddNamespaceTo( internal static SyntaxNode GenerateNamespaceDeclaration( ICodeGenerationService service, INamespaceSymbol @namespace, + CodeGenerationDestination destination, CodeGenerationOptions options, + ParseOptions parseOptions, CancellationToken cancellationToken) { options ??= CodeGenerationOptions.Default; GetNameAndInnermostNamespace(@namespace, options, out var name, out var innermostNamespace); - var declaration = GetDeclarationSyntaxWithoutMembers(@namespace, innermostNamespace, name, options); + var declaration = GetDeclarationSyntaxWithoutMembers( + @namespace, innermostNamespace, name, destination, options, parseOptions); declaration = options.GenerateMembers - ? service.AddMembers(declaration, innermostNamespace.GetMembers(), options, cancellationToken) - : declaration; + ? service.AddMembers(declaration, innermostNamespace.GetMembers(), options, cancellationToken) + : declaration; return AddFormatterAndCodeGeneratorAnnotationsTo(declaration); } @@ -83,14 +95,22 @@ public static SyntaxNode UpdateCompilationUnitOrNamespaceDeclaration( } private static SyntaxNode GenerateNamespaceDeclarationWorker( - string name, INamespaceSymbol innermostNamespace) + string name, INamespaceSymbol innermostNamespace, + CodeGenerationDestination destination, + CodeGenerationOptions options, + ParseOptions parseOptions) { var usings = GenerateUsingDirectives(innermostNamespace); // If they're just generating the empty namespace then make that into compilation unit. if (name == string.Empty) - { return SyntaxFactory.CompilationUnit().WithUsings(usings); + + if (destination == CodeGenerationDestination.CompilationUnit && + options.Options.GetOption(CSharpCodeStyleOptions.PreferFileScopedNamespace).Value && + ((CSharpParseOptions)parseOptions)?.LanguageVersion >= LanguageVersion.CSharp10) + { + return SyntaxFactory.FileScopedNamespaceDeclaration(SyntaxFactory.ParseName(name)).WithUsings(usings); } return SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(name)).WithUsings(usings); @@ -100,15 +120,14 @@ private static SyntaxNode GetDeclarationSyntaxWithoutMembers( INamespaceSymbol @namespace, INamespaceSymbol innermostNamespace, string name, - CodeGenerationOptions options) + CodeGenerationDestination destination, + CodeGenerationOptions options, + ParseOptions parseOptions) { var reusableSyntax = GetReuseableSyntaxNodeForSymbol(@namespace, options); - if (reusableSyntax == null) - { - return GenerateNamespaceDeclarationWorker(name, innermostNamespace); - } - - return RemoveAllMembers(reusableSyntax); + return reusableSyntax == null + ? GenerateNamespaceDeclarationWorker(name, innermostNamespace, destination, options, parseOptions) + : RemoveAllMembers(reusableSyntax); } private static SyntaxNode RemoveAllMembers(SyntaxNode declaration) From 275a41529eb6d0a484d9340afc2b89f6b63a3ea7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 13:20:00 -0700 Subject: [PATCH 14/45] Add test --- .../GenerateType/GenerateTypeTests.cs | 32 +++++++++++++++++++ ...ionOrUserDiagnosticTest_TestAddDocument.cs | 3 +- .../CodeGeneration/NamespaceGenerator.cs | 2 +- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs index c7c03aa65be9e..7b4ae435ce3a4 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs @@ -9,6 +9,8 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateType; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Diagnostics; @@ -4878,6 +4880,36 @@ await TestAddDocumentInRegularAndScriptAsync(code, expectedDocumentName: "ClassB.cs"); } + [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateType)] + public async Task TestGenerateTypeInFolderNotDefaultNamespace_0_FileScopedNamespace() + { + var code = @" + + +namespace Namespace1.Namespace2; + +public class ClassA : [|$$ClassB|] +{ +} + + + "; + + var expected = @"namespace Namespace1.Namespace2; + +public class ClassB +{ +}"; + + await TestAddDocumentInRegularAndScriptAsync(code, + expected, + expectedContainers: ImmutableArray.Empty, + expectedDocumentName: "ClassB.cs", + new TestParameters( + parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp10), + options: Option(CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.TrueWithSilentEnforcement))); + } + [WorkItem(932602, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/932602")] [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateType)] public async Task TestGenerateTypeInFolderNotDefaultNamespace_1() diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest_TestAddDocument.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest_TestAddDocument.cs index de44139c7578a..cee75ee9ccb92 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest_TestAddDocument.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest_TestAddDocument.cs @@ -116,7 +116,8 @@ protected static async Task> TestAddDocument( AssertEx.Equal(expectedFolders, addedDocument.Folders); Assert.Equal(expectedDocumentName, addedDocument.Name); - Assert.Equal(expected, (await addedDocument.GetTextAsync()).ToString()); + var actual = (await addedDocument.GetTextAsync()).ToString(); + Assert.Equal(expected, actual); var editHandler = workspace.ExportProvider.GetExportedValue(); if (!hasProjectChange) diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs index 36b5345159585..dfe9729d35524 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs @@ -54,7 +54,7 @@ public static CompilationUnitSyntax AddNamespaceTo( CodeGenerationDestination.CompilationUnit, options, destination?.SyntaxTree.Options ?? options.ParseOptions, cancellationToken); - if (declaration is not NamespaceDeclarationSyntax namespaceDeclaration) + if (declaration is not BaseNamespaceDeclarationSyntax namespaceDeclaration) throw new ArgumentException(CSharpWorkspaceResources.Namespace_can_not_be_added_in_this_destination); var members = Insert(destination.Members, namespaceDeclaration, options, availableIndices); From cac2b649b8b4aaef886fb8acf7fbeafeb4b45018 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 13:48:59 -0700 Subject: [PATCH 15/45] Add metadata as source test --- ...stractMetadataAsSourceTests.TestContext.cs | 15 ++++++----- .../MetadataAsSource/MetadataAsSourceTests.cs | 27 +++++++++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/Test/MetadataAsSource/AbstractMetadataAsSourceTests.TestContext.cs b/src/EditorFeatures/Test/MetadataAsSource/AbstractMetadataAsSourceTests.TestContext.cs index 9e3c98090aa5f..c3cb83e4e3e0a 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/AbstractMetadataAsSourceTests.TestContext.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/AbstractMetadataAsSourceTests.TestContext.cs @@ -26,7 +26,7 @@ public abstract partial class AbstractMetadataAsSourceTests internal class TestContext : IDisposable { - private readonly TestWorkspace _workspace; + public readonly TestWorkspace Workspace; private readonly IMetadataAsSourceFileService _metadataAsSourceService; public static TestContext Create( @@ -46,18 +46,19 @@ public static TestContext Create( var workspace = CreateWorkspace( projectLanguage, metadataSources, includeXmlDocComments, sourceWithSymbolReference, languageVersion, metadataLanguageVersion); + return new TestContext(workspace); } public TestContext(TestWorkspace workspace) { - _workspace = workspace; - _metadataAsSourceService = _workspace.GetService(); + Workspace = workspace; + _metadataAsSourceService = Workspace.GetService(); } public Solution CurrentSolution { - get { return _workspace.CurrentSolution; } + get { return Workspace.CurrentSolution; } } public Project DefaultProject @@ -125,7 +126,7 @@ public void Dispose() } finally { - _workspace.Dispose(); + Workspace.Dispose(); } } @@ -264,8 +265,8 @@ internal Document GetDocument(MetadataAsSourceFile file) internal async Task GetNavigationSymbolAsync() { - var testDocument = _workspace.Documents.Single(d => d.FilePath == "SourceDocument"); - var document = _workspace.CurrentSolution.GetRequiredDocument(testDocument.Id); + var testDocument = Workspace.Documents.Single(d => d.FilePath == "SourceDocument"); + var document = Workspace.CurrentSolution.GetRequiredDocument(testDocument.Id); var syntaxRoot = await document.GetRequiredSyntaxRootAsync(CancellationToken.None); var semanticModel = await document.GetRequiredSemanticModelAsync(CancellationToken.None); diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs index fc2368f442f1f..4dec0ba1030a8 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs @@ -12,6 +12,8 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; using CS = Microsoft.CodeAnalysis.CSharp; using VB = Microsoft.CodeAnalysis.VisualBasic; @@ -398,6 +400,31 @@ End Class End Namespace"); } + [WorkItem(546198, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198")] + [Fact, Trait(Traits.Feature, Traits.Features.MetadataAsSource)] + public async Task TestTypeInFileScopedNamespace() + { + var metadataSource = "namespace N { public class C {} }"; + + using var context = TestContext.Create( + LanguageNames.CSharp, SpecializedCollections.SingletonEnumerable(metadataSource), languageVersion: "10"); + + context.Workspace.SetOptions(context.Workspace.Options.WithChangedOption( + CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.TrueWithSilentEnforcement)); + + await context.GenerateAndVerifySourceAsync("N.C", + $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// {CodeAnalysisResources.InMemoryAssembly} +#endregion + +namespace N; + +public class [|C|] +{{ + public C(); +}}"); + } + [WorkItem(546223, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546223")] [Fact, Trait(Traits.Feature, Traits.Features.MetadataAsSource)] public async Task TestInlineConstant() From ac76a2733693c96499337a6c88b210d288f2d443 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 14:34:56 -0700 Subject: [PATCH 16/45] Add tests --- .../ExtractInterface/ExtractInterfaceTests.cs | 118 ++++++++++++++++++ .../MetadataAsSource/MetadataAsSourceTests.cs | 52 +++++++- .../ExtractInterfaceTestState.cs | 19 ++- 3 files changed, 185 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs b/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs index 32ed303ff7994..47aed6fb8ad50 100644 --- a/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs +++ b/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs @@ -5,16 +5,21 @@ using System.Linq; using System.Threading.Tasks; using System.Xml.Linq; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.Editor.CSharp.ExtractInterface; using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Editor.UnitTests.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.ExtractInterface; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.ExtractInterface; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; using Roslyn.Test.Utilities; using Xunit; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ExtractInterface { @@ -432,6 +437,119 @@ class MyClass await TestExtractInterfaceCommandCSharpAsync(markup, expectedSuccess: true, expectedNamespaceName: "OuterNamespace.InnerNamespace"); } + [WpfFact, Trait(Traits.Feature, Traits.Features.ExtractInterface)] + public async Task ExtractInterface_NamespaceName_NestedNamespaces_FileScopedNamespace1() + { + var markup = @" +using System; +namespace OuterNamespace +{ + namespace InnerNamespace + { + class MyClass + { + $$public void Goo() { } + } + } +}"; + + using var testState = ExtractInterfaceTestState.Create( + markup, LanguageNames.CSharp, + parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp10), + options: new OptionsCollection(LanguageNames.CSharp) + { + { CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.TrueWithSilentEnforcement } + }); + + var result = await testState.ExtractViaCommandAsync(); + + var interfaceDocument = result.UpdatedSolution.GetRequiredDocument(result.NavigationDocumentId); + var interfaceCode = (await interfaceDocument.GetTextAsync()).ToString(); + + Assert.Equal(@"namespace OuterNamespace.InnerNamespace; + +interface IMyClass +{ + void Goo(); +}", interfaceCode); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.ExtractInterface)] + public async Task ExtractInterface_NamespaceName_NestedNamespaces_FileScopedNamespace2() + { + var markup = @" +using System; +namespace OuterNamespace +{ + namespace InnerNamespace + { + class MyClass + { + $$public void Goo() { } + } + } +}"; + + using var testState = ExtractInterfaceTestState.Create( + markup, LanguageNames.CSharp, + parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9), + options: new OptionsCollection(LanguageNames.CSharp) + { + { CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.TrueWithSilentEnforcement } + }); + + var result = await testState.ExtractViaCommandAsync(); + + var interfaceDocument = result.UpdatedSolution.GetRequiredDocument(result.NavigationDocumentId); + var interfaceCode = (await interfaceDocument.GetTextAsync()).ToString(); + + Assert.Equal(@"namespace OuterNamespace.InnerNamespace +{ + interface IMyClass + { + void Goo(); + } +}", interfaceCode); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.ExtractInterface)] + public async Task ExtractInterface_NamespaceName_NestedNamespaces_FileScopedNamespace3() + { + var markup = @" +using System; +namespace OuterNamespace +{ + namespace InnerNamespace + { + class MyClass + { + $$public void Goo() { } + } + } +}"; + + using var testState = ExtractInterfaceTestState.Create( + markup, LanguageNames.CSharp, + parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp10), + options: new OptionsCollection(LanguageNames.CSharp) + { + { CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.FalseWithSilentEnforcement } + }); + + var result = await testState.ExtractViaCommandAsync(); + + var interfaceDocument = result.UpdatedSolution.GetRequiredDocument(result.NavigationDocumentId); + var interfaceCode = (await interfaceDocument.GetTextAsync()).ToString(); + + Assert.Equal(@"namespace OuterNamespace.InnerNamespace +{ + interface IMyClass + { + void Goo(); + } +}", interfaceCode); + } + [WpfFact, Trait(Traits.Feature, Traits.Features.ExtractInterface)] public async Task ExtractInterface_CodeGen_ClassesImplementExtractedInterface() { diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs index 4dec0ba1030a8..2967eb0fef026 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs @@ -402,7 +402,7 @@ End Class [WorkItem(546198, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198")] [Fact, Trait(Traits.Feature, Traits.Features.MetadataAsSource)] - public async Task TestTypeInFileScopedNamespace() + public async Task TestTypeInFileScopedNamespace1() { var metadataSource = "namespace N { public class C {} }"; @@ -419,6 +419,56 @@ await context.GenerateAndVerifySourceAsync("N.C", namespace N; +public class [|C|] +{{ + public C(); +}}"); + } + + [WorkItem(546198, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198")] + [Fact, Trait(Traits.Feature, Traits.Features.MetadataAsSource)] + public async Task TestTypeInFileScopedNamespace2() + { + var metadataSource = "namespace N { public class C {} }"; + + using var context = TestContext.Create( + LanguageNames.CSharp, SpecializedCollections.SingletonEnumerable(metadataSource), languageVersion: "9"); + + context.Workspace.SetOptions(context.Workspace.Options.WithChangedOption( + CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.TrueWithSilentEnforcement)); + + await context.GenerateAndVerifySourceAsync("N.C", + $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// {CodeAnalysisResources.InMemoryAssembly} +#endregion + +namespace N; + +public class [|C|] +{{ + public C(); +}}"); + } + + [WorkItem(546198, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546198")] + [Fact, Trait(Traits.Feature, Traits.Features.MetadataAsSource)] + public async Task TestTypeInFileScopedNamespace3() + { + var metadataSource = "namespace N { public class C {} }"; + + using var context = TestContext.Create( + LanguageNames.CSharp, SpecializedCollections.SingletonEnumerable(metadataSource), languageVersion: "10"); + + context.Workspace.SetOptions(context.Workspace.Options.WithChangedOption( + CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.FalseWithSilentEnforcement)); + + await context.GenerateAndVerifySourceAsync("N.C", + $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// {CodeAnalysisResources.InMemoryAssembly} +#endregion + +namespace N; + public class [|C|] {{ public C(); diff --git a/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs b/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs index 91d084528328b..1fd7def163e10 100644 --- a/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs +++ b/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; namespace Microsoft.CodeAnalysis.Editor.UnitTests.ExtractInterface { @@ -32,11 +33,23 @@ internal class ExtractInterfaceTestState : IDisposable public string ErrorMessage { get; private set; } public NotificationSeverity ErrorSeverity { get; private set; } - public static ExtractInterfaceTestState Create(string markup, string languageName, CompilationOptions compilationOptions) + public static ExtractInterfaceTestState Create( + string markup, + string languageName, + CompilationOptions compilationOptions = null, + ParseOptions parseOptions = null, + OptionsCollection options = null) { var workspace = languageName == LanguageNames.CSharp - ? TestWorkspace.CreateCSharp(markup, composition: Composition, compilationOptions: (CSharpCompilationOptions)compilationOptions) - : TestWorkspace.CreateVisualBasic(markup, composition: Composition, compilationOptions: compilationOptions); + ? TestWorkspace.CreateCSharp(markup, composition: Composition, compilationOptions: compilationOptions, parseOptions: parseOptions) + : TestWorkspace.CreateVisualBasic(markup, composition: Composition, compilationOptions: compilationOptions, parseOptions: parseOptions); + + if (options != null) + { + foreach (var kvp in options) + workspace.SetOptions(workspace.Options.WithChangedOption(kvp.Key, kvp.Value)); + } + return new ExtractInterfaceTestState(workspace); } From 4ddf9bef30518b4e7b1bce20c7673195dd222513 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 14:46:56 -0700 Subject: [PATCH 17/45] Add tests --- .../MetadataAsSource/MetadataAsSourceTests.cs | 18 ++++++++++-------- .../ExtractInterfaceTestState.cs | 4 +--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs index 2967eb0fef026..451e088815855 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs @@ -442,11 +442,12 @@ await context.GenerateAndVerifySourceAsync("N.C", // {CodeAnalysisResources.InMemoryAssembly} #endregion -namespace N; - -public class [|C|] +namespace N {{ - public C(); + public class [|C|] + {{ + public C(); + }} }}"); } @@ -467,11 +468,12 @@ await context.GenerateAndVerifySourceAsync("N.C", // {CodeAnalysisResources.InMemoryAssembly} #endregion -namespace N; - -public class [|C|] +namespace N {{ - public C(); + public class [|C|] + {{ + public C(); + }} }}"); } diff --git a/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs b/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs index 1fd7def163e10..22536037eceae 100644 --- a/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs +++ b/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs @@ -8,15 +8,13 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.ExtractInterface; using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; namespace Microsoft.CodeAnalysis.Editor.UnitTests.ExtractInterface { From 1c48e496345f346a3db733ec94f126bb75ae3ad5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 17:01:42 -0700 Subject: [PATCH 18/45] Ad dtests --- .../ExtractClass/ExtractClassTests.cs | 238 ++++++++++++++---- 1 file changed, 196 insertions(+), 42 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/ExtractClass/ExtractClassTests.cs b/src/EditorFeatures/CSharpTest/ExtractClass/ExtractClassTests.cs index 6e7dae9451939..5558149e0adc2 100644 --- a/src/EditorFeatures/CSharpTest/ExtractClass/ExtractClassTests.cs +++ b/src/EditorFeatures/CSharpTest/ExtractClass/ExtractClassTests.cs @@ -2,17 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; -using System.Xml.Linq; using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.ExtractClass; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.ExtractClass; using Microsoft.CodeAnalysis.PullMemberUp; using Roslyn.Test.Utilities; @@ -22,10 +21,10 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ExtractClass { public class ExtractClassTests : AbstractCSharpCodeActionTest { - private Task TestAsync( + private Task TestExtractClassAsync( string input, - string expected = null, - IEnumerable<(string name, bool makeAbstract)> dialogSelection = null, + string? expected = null, + IEnumerable<(string name, bool makeAbstract)>? dialogSelection = null, bool sameFile = false, bool isClassDeclarationSelection = false, TestParameters testParameters = default) @@ -83,7 +82,7 @@ int Method() "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); } [Fact] @@ -104,7 +103,7 @@ class Test : ErrorBase "; - await TestAsync(input); + await TestExtractClassAsync(input); } [Fact] @@ -126,7 +125,7 @@ class Test "; TestParameters parameters = default; - await TestAsync(input, testParameters: parameters.WithWorkspaceKind(WorkspaceKind.MiscellaneousFiles)); + await TestExtractClassAsync(input, testParameters: parameters.WithWorkspaceKind(WorkspaceKind.MiscellaneousFiles)); } [Fact] @@ -183,7 +182,7 @@ int Method2() "; - await TestAsync( + await TestExtractClassAsync( input, expected, dialogSelection: MakeSelection("Method", "Method2")); @@ -234,7 +233,162 @@ int Method() "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); + } + + [Fact] + public async Task TestInNamespace_FileScopedNamespace1() + { + var input = @" + + + +namespace MyNamespace +{ + class Test + { + int [||]Method() + { + return 1 + 1; + } + } +} + + +"; + + var expected = @" + + + +namespace MyNamespace +{ + class Test : MyBase + { + } +} + + namespace MyNamespace; + +internal class MyBase +{ + int Method() + { + return 1 + 1; + } +} + +"; + + await TestAsync( + input, expected, + CSharpParseOptions.Default, + options: Option(CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.TrueWithSilentEnforcement), + fixProviderData: new TestExtractClassOptionsService()); + } + + [Fact] + public async Task TestInNamespace_FileScopedNamespace2() + { + var input = @" + + + +namespace MyNamespace +{ + class Test + { + int [||]Method() + { + return 1 + 1; + } + } +} + + +"; + + var expected = @" + + + +namespace MyNamespace +{ + class Test : MyBase + { + } +} + + namespace MyNamespace +{ + internal class MyBase + { + int Method() + { + return 1 + 1; + } + } +} + +"; + + await TestAsync( + input, expected, + CSharpParseOptions.Default, + options: Option(CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.TrueWithSilentEnforcement), + fixProviderData: new TestExtractClassOptionsService()); + } + + [Fact] + public async Task TestInNamespace_FileScopedNamespace3() + { + var input = @" + + + +namespace MyNamespace +{ + class Test + { + int [||]Method() + { + return 1 + 1; + } + } +} + + +"; + + var expected = @" + + + +namespace MyNamespace +{ + class Test : MyBase + { + } +} + + namespace MyNamespace +{ + internal class MyBase + { + int Method() + { + return 1 + 1; + } + } +} + +"; + + await TestAsync( + input, expected, + CSharpParseOptions.Default, + options: Option(CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.FalseWithSilentEnforcement), + fixProviderData: new TestExtractClassOptionsService()); } [Fact] @@ -273,7 +427,7 @@ int Method() "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); } [Fact] @@ -310,7 +464,7 @@ class Test : MyBase "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); } [Fact] @@ -343,7 +497,7 @@ class Test : MyBase "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); } [Fact] @@ -376,7 +530,7 @@ class Test : MyBase "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); } [Fact] @@ -422,7 +576,7 @@ int Method() "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); } [Fact] @@ -471,7 +625,7 @@ int Method() "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); } [ConditionalFact(AlwaysSkip = "https://github.com/dotnet/roslyn/issues/45977")] @@ -521,7 +675,7 @@ int Method() "; - await TestAsync( + await TestExtractClassAsync( input, expected, dialogSelection: MakeSelection("Method")); @@ -564,7 +718,7 @@ override int Method() "; - await TestAsync( + await TestExtractClassAsync( input, expected, dialogSelection: MakeAbstractSelection("Method")); @@ -615,7 +769,7 @@ override int Method() "; - await TestAsync( + await TestExtractClassAsync( input, expected, dialogSelection: MakeAbstractSelection("Method", "Method2", "Method3")); @@ -661,7 +815,7 @@ int Method() "; - await TestAsync( + await TestExtractClassAsync( input, expected, dialogSelection: MakeSelection("Method", "Method2")); @@ -707,7 +861,7 @@ int Method() "; - await TestAsync( + await TestExtractClassAsync( input, expected, dialogSelection: MakeSelection("Method2")); @@ -755,7 +909,7 @@ int Method() "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); } [Fact] @@ -800,7 +954,7 @@ int Method() "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); } [Fact] @@ -845,7 +999,7 @@ int Method() "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); } [Fact] @@ -890,7 +1044,7 @@ int Method() "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); } [Fact] @@ -945,7 +1099,7 @@ int Method() "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); } [Fact] @@ -1004,7 +1158,7 @@ int Method() "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); } [ConditionalFact(AlwaysSkip = "https://github.com/dotnet/roslyn/issues/45987")] @@ -1062,7 +1216,7 @@ int Method() "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); } [ConditionalFact(AlwaysSkip = "https://github.com/dotnet/roslyn/issues/45987")] @@ -1120,7 +1274,7 @@ int Method() "; - await TestAsync(input, expected); + await TestExtractClassAsync(input, expected); } [Fact] @@ -1156,7 +1310,7 @@ class Test : MyBase "; - await TestAsync(input, expected, sameFile: true); + await TestExtractClassAsync(input, expected, sameFile: true); } [Fact] @@ -1195,7 +1349,7 @@ int Method() "; - await TestAsync(input, expected, isClassDeclarationSelection: true); + await TestExtractClassAsync(input, expected, isClassDeclarationSelection: true); } [Fact] @@ -1234,7 +1388,7 @@ int Method() "; - await TestAsync(input, expected, isClassDeclarationSelection: true); + await TestExtractClassAsync(input, expected, isClassDeclarationSelection: true); } [Fact] @@ -1273,7 +1427,7 @@ int Method() "; - await TestAsync(input, expected, isClassDeclarationSelection: true); + await TestExtractClassAsync(input, expected, isClassDeclarationSelection: true); } [Fact] @@ -1312,7 +1466,7 @@ int Method() "; - await TestAsync(input, expected, isClassDeclarationSelection: true); + await TestExtractClassAsync(input, expected, isClassDeclarationSelection: true); } [Fact] @@ -1361,7 +1515,7 @@ int Method() "; - await TestAsync(input, expected, isClassDeclarationSelection: true); + await TestExtractClassAsync(input, expected, isClassDeclarationSelection: true); } [Fact] @@ -1410,7 +1564,7 @@ int Method() "; - await TestAsync(input, expected, isClassDeclarationSelection: true); + await TestExtractClassAsync(input, expected, isClassDeclarationSelection: true); } [Fact] @@ -1459,7 +1613,7 @@ int Method() "; - await TestAsync(input, expected, isClassDeclarationSelection: true); + await TestExtractClassAsync(input, expected, isClassDeclarationSelection: true); } [Fact] @@ -1509,7 +1663,7 @@ int Method() "; - await TestAsync(input, expected, isClassDeclarationSelection: true); + await TestExtractClassAsync(input, expected, isClassDeclarationSelection: true); } [Fact] @@ -1548,7 +1702,7 @@ int Method() "; - await TestAsync(input, expected, isClassDeclarationSelection: true); + await TestExtractClassAsync(input, expected, isClassDeclarationSelection: true); } [Fact] @@ -1587,7 +1741,7 @@ int Method() "; - await TestAsync(input, expected, isClassDeclarationSelection: true); + await TestExtractClassAsync(input, expected, isClassDeclarationSelection: true); } private static IEnumerable<(string name, bool makeAbstract)> MakeAbstractSelection(params string[] memberNames) From 23594c91692116f783799354b4777925f8a10a89 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 17:21:10 -0700 Subject: [PATCH 19/45] use equiv key --- .../ConvertNamespace/ConvertNamespaceCodeFixProvider.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs index c4fc6c0d193dc..30989f9dfc20c 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs @@ -38,9 +38,12 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) var title = diagnostic.Id == IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId ? CSharpFeaturesResources.Convert_to_file_scoped_namespace : CSharpFeaturesResources.Convert_to_regular_namespace; + var equivalenceKey = diagnostic.Id == IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId + ? nameof(CSharpFeaturesResources.Convert_to_file_scoped_namespace) + : nameof(CSharpFeaturesResources.Convert_to_regular_namespace); context.RegisterCodeFix( - new MyCodeAction(title, c => FixAsync(context.Document, diagnostic, c)), + new MyCodeAction(title, c => FixAsync(context.Document, diagnostic, c), equivalenceKey), context.Diagnostics); return Task.CompletedTask; @@ -61,8 +64,8 @@ protected override Task FixAllAsync( private class MyCodeAction : CodeAction.DocumentChangeAction { - public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument, title) + public MyCodeAction(string title, Func> createChangedDocument, string equivalenceKey) + : base(title, createChangedDocument, equivalenceKey) { } } From 8283983fd16010bf048e24a2ff7d510d5f67853d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 17:57:08 -0700 Subject: [PATCH 20/45] Move to an enum --- .../MetadataAsSource/MetadataAsSourceTests.cs | 2 +- .../ConvertNamespaceDiagnosticAnalyzer.cs | 8 ++-- .../ConvertNamespaceHelper.cs | 12 ++++-- .../CSharp/Impl/CSharpVSResources.resx | 6 +++ .../Impl/Options/Formatting/StyleViewModel.cs | 20 +++++++++- .../CSharp/Impl/xlf/CSharpVSResources.cs.xlf | 10 +++++ .../CSharp/Impl/xlf/CSharpVSResources.de.xlf | 10 +++++ .../CSharp/Impl/xlf/CSharpVSResources.es.xlf | 10 +++++ .../CSharp/Impl/xlf/CSharpVSResources.fr.xlf | 10 +++++ .../CSharp/Impl/xlf/CSharpVSResources.it.xlf | 10 +++++ .../CSharp/Impl/xlf/CSharpVSResources.ja.xlf | 10 +++++ .../CSharp/Impl/xlf/CSharpVSResources.ko.xlf | 10 +++++ .../CSharp/Impl/xlf/CSharpVSResources.pl.xlf | 10 +++++ .../Impl/xlf/CSharpVSResources.pt-BR.xlf | 10 +++++ .../CSharp/Impl/xlf/CSharpVSResources.ru.xlf | 10 +++++ .../CSharp/Impl/xlf/CSharpVSResources.tr.xlf | 10 +++++ .../Impl/xlf/CSharpVSResources.zh-Hans.xlf | 10 +++++ .../Impl/xlf/CSharpVSResources.zh-Hant.xlf | 10 +++++ .../Core/Def/ServicesVSResources.resx | 4 +- .../Core/Def/xlf/ServicesVSResources.cs.xlf | 10 ++--- .../Core/Def/xlf/ServicesVSResources.de.xlf | 10 ++--- .../Core/Def/xlf/ServicesVSResources.es.xlf | 10 ++--- .../Core/Def/xlf/ServicesVSResources.fr.xlf | 10 ++--- .../Core/Def/xlf/ServicesVSResources.it.xlf | 10 ++--- .../Core/Def/xlf/ServicesVSResources.ja.xlf | 10 ++--- .../Core/Def/xlf/ServicesVSResources.ko.xlf | 10 ++--- .../Core/Def/xlf/ServicesVSResources.pl.xlf | 10 ++--- .../Def/xlf/ServicesVSResources.pt-BR.xlf | 10 ++--- .../Core/Def/xlf/ServicesVSResources.ru.xlf | 10 ++--- .../Core/Def/xlf/ServicesVSResources.tr.xlf | 10 ++--- .../Def/xlf/ServicesVSResources.zh-Hans.xlf | 10 ++--- .../Def/xlf/ServicesVSResources.zh-Hant.xlf | 10 ++--- .../CodeGeneration/NamespaceGenerator.cs | 3 +- .../CodeStyle/CSharpCodeStyleOptions.cs | 38 ++++++++++++------- .../CSharpCodeStyleOptions_Parsing.cs | 30 ++++++++++++++- .../NamespaceDeclarationPreference.cs | 18 +++++++++ .../Core/CompilerExtensions.projitems | 1 + 37 files changed, 309 insertions(+), 93 deletions(-) create mode 100644 src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/NamespaceDeclarationPreference.cs diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs index 451e088815855..8f23c353c2b7b 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs @@ -410,7 +410,7 @@ public async Task TestTypeInFileScopedNamespace1() LanguageNames.CSharp, SpecializedCollections.SingletonEnumerable(metadataSource), languageVersion: "10"); context.Workspace.SetOptions(context.Workspace.Options.WithChangedOption( - CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.TrueWithSilentEnforcement)); + CSharpCodeStyleOptions.NamespaceDeclarations, CodeStyleOptions2.TrueWithSilentEnforcement)); await context.GenerateAndVerifySourceAsync("N.C", $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs index de6a25173be7b..86853a1916340 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs @@ -27,8 +27,8 @@ internal class ConvertNamespaceDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiag private static readonly ImmutableDictionary s_descriptors = ImmutableDictionary.Empty - .Add(s_useRegularNamespaceDescriptor, CSharpCodeStyleOptions.PreferFileScopedNamespace) - .Add(s_useFileScopedNamespaceDescriptor, CSharpCodeStyleOptions.PreferFileScopedNamespace); + .Add(s_useRegularNamespaceDescriptor, CSharpCodeStyleOptions.NamespaceDeclarations) + .Add(s_useFileScopedNamespaceDescriptor, CSharpCodeStyleOptions.NamespaceDeclarations); public ConvertNamespaceDiagnosticAnalyzer() : base(s_descriptors, LanguageNames.CSharp) @@ -60,8 +60,7 @@ private void AnalyzeNamespace(SyntaxNodeAnalysisContext context) private static Diagnostic? AnalyzeNamespace(OptionSet optionSet, CompilationUnitSyntax root, BaseNamespaceDeclarationSyntax declaration) { var tree = declaration.SyntaxTree; - var preferFileScopedNamespace = optionSet.GetOption(CSharpCodeStyleOptions.PreferFileScopedNamespace); - var severity = preferFileScopedNamespace.Notification.Severity; + var option = optionSet.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations); var descriptor = ConvertNamespaceHelper.CanOfferUseRegular(optionSet, declaration, forAnalyzer: true) ? s_useRegularNamespaceDescriptor : @@ -71,6 +70,7 @@ private void AnalyzeNamespace(SyntaxNodeAnalysisContext context) // if the diagnostic is hidden, show it anywhere from the `namespace` keyword through the name. // otherwise, if it's not hidden, just squiggle the name. + var severity = option.Notification.Severity; var diagnosticLocation = severity.WithDefaultSeverity(DiagnosticSeverity.Hidden) == ReportDiagnostic.Hidden ? tree.GetLocation(TextSpan.FromBounds(declaration.SpanStart, declaration.Name.Span.End)) : declaration.Name.GetLocation(); diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs index 03c1f4974a4e0..0355da9ae14b4 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; @@ -22,8 +23,8 @@ internal static bool CanOfferUseRegular(OptionSet optionSet, BaseNamespaceDeclar if (declaration is not FileScopedNamespaceDeclarationSyntax) return false; - var option = optionSet.GetOption(CSharpCodeStyleOptions.PreferFileScopedNamespace); - var userPrefersRegularNamespaces = !option.Value; + var option = optionSet.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations); + var userPrefersRegularNamespaces = option.Value == NamespaceDeclarationPreference.BlockScoped; var analyzerDisabled = option.Notification.Severity == ReportDiagnostic.Suppress; var forRefactoring = !forAnalyzer; @@ -42,8 +43,11 @@ internal static bool CanOfferUseFileScoped(OptionSet optionSet, CompilationUnitS if (namespaceDeclaration.OpenBraceToken.IsMissing) return false; - var option = optionSet.GetOption(CSharpCodeStyleOptions.PreferFileScopedNamespace); - var userPrefersFileScopedNamespaces = option.Value; + if (((CSharpParseOptions)root.SyntaxTree.Options).LanguageVersion < LanguageVersion.CSharp10) + return false; + + var option = optionSet.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations); + var userPrefersFileScopedNamespaces = option.Value == NamespaceDeclarationPreference.FileScoped; var analyzerDisabled = option.Notification.Severity == ReportDiagnostic.Suppress; var forRefactoring = !forAnalyzer; diff --git a/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx b/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx index 9199149dd86ac..ac658678d6ddd 100644 --- a/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx +++ b/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx @@ -643,4 +643,10 @@ Prefer 'null' check over type check + + Block scoped + + + File scoped + \ No newline at end of file diff --git a/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs b/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs index 460f3e5f6129e..f3d5c13a80fa6 100644 --- a/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs +++ b/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs @@ -2038,7 +2038,7 @@ internal StyleViewModel(OptionStore optionStore, IServiceProvider serviceProvide // Code block AddBracesOptions(optionStore, codeBlockPreferencesGroupTitle); - CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferFileScopedNamespace, ServicesVSResources.Prefer_file_scoped_namespace, s_preferFileScopedNamespace, s_preferBlockNamespace, this, optionStore, codeBlockPreferencesGroupTitle)); + AddNamespaceDeclarationsOptions(optionStore, codeBlockPreferencesGroupTitle); CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CodeStyleOptions2.PreferAutoProperties, ServicesVSResources.analyzer_Prefer_auto_properties, s_preferAutoProperties, s_preferAutoProperties, this, optionStore, codeBlockPreferencesGroupTitle)); CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferSimpleUsingStatement, ServicesVSResources.Prefer_simple_using_statement, s_preferSimpleUsingStatement, s_preferSimpleUsingStatement, this, optionStore, codeBlockPreferencesGroupTitle)); CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CodeStyleOptions2.PreferSystemHashCode, ServicesVSResources.Prefer_System_HashCode_in_GetHashCode, s_preferSystemHashCode, s_preferSystemHashCode, this, optionStore, codeBlockPreferencesGroupTitle)); @@ -2151,6 +2151,24 @@ private void AddBracesOptions(OptionStore optionStore, string bracesPreferenceGr this, optionStore, bracesPreferenceGroupTitle, bracesPreferences)); } + private void AddNamespaceDeclarationsOptions(OptionStore optionStore, string group) + { + var preferences = new List + { + new CodeStylePreference(CSharpVSResources.Block_scoped, isChecked: false), + new CodeStylePreference(CSharpVSResources.File_scoped, isChecked: false), + }; + + var enumValues = new[] { NamespaceDeclarationPreference.BlockScoped, NamespaceDeclarationPreference.FileScoped }; + + CodeStyleItems.Add(new EnumCodeStyleOptionViewModel( + CSharpCodeStyleOptions.NamespaceDeclarations, + ServicesVSResources.Namespace_declarations, + enumValues, + new[] { s_preferBlockNamespace, s_preferFileScopedNamespace }, + this, optionStore, group, preferences)); + } + private void AddExpressionBodyOptions(OptionStore optionStore, string expressionPreferencesGroupTitle) { var expressionBodyPreferences = new List diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf index d6c965cc620ff..ad7dd3c561eba 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf @@ -37,6 +37,11 @@ Automaticky zobrazovat seznam dokončení v seznamech argumentů + + Block scoped + Block scoped + + C# C# @@ -67,6 +72,11 @@ Upravit barevné schéma + + File scoped + File scoped + + Format Document Settings (Experiment) Nastavení formátování dokumentu (experiment) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf index c9444c135d8eb..2f864a802cafd 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf @@ -37,6 +37,11 @@ Vervollständigungsliste in Argumentlisten automatisch anzeigen + + Block scoped + Block scoped + + C# C# @@ -67,6 +72,11 @@ Farbschema bearbeiten + + File scoped + File scoped + + Format Document Settings (Experiment) Einstellungen zur Dokumentformatierung (experimentell) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf index 60b4ae0cd42f8..86a788218919b 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf @@ -37,6 +37,11 @@ Mostrar automáticamente la lista de finalización en las listas de argumentos + + Block scoped + Block scoped + + C# C# @@ -67,6 +72,11 @@ Editar esquema de color + + File scoped + File scoped + + Format Document Settings (Experiment) Configuración de “Dar formato al documento” (experimento) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf index 46d4e596b2dd4..6e3206db8c38a 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf @@ -37,6 +37,11 @@ Afficher automatiquement la liste de complétion dans les listes d'arguments + + Block scoped + Block scoped + + C# C# @@ -67,6 +72,11 @@ Modifier le modèle de couleurs + + File scoped + File scoped + + Format Document Settings (Experiment) Paramètres de mise en forme du document (essai) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf index 11b64a1b14483..18c4cc0a825fb 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf @@ -37,6 +37,11 @@ Mostra automaticamente l'elenco di completamento negli elenchi di argomenti + + Block scoped + Block scoped + + C# C# @@ -67,6 +72,11 @@ Modifica combinazione colori + + File scoped + File scoped + + Format Document Settings (Experiment) Impostazioni formattazione documento (sperimentale) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf index cecf7812a142f..5d5cd015f4727 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf @@ -37,6 +37,11 @@ 引数リストに入力候補一覧を自動的に表示する + + Block scoped + Block scoped + + C# C# @@ -67,6 +72,11 @@ 配色の編集 + + File scoped + File scoped + + Format Document Settings (Experiment) ドキュメントのフォーマットの設定 (実験) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf index 4a568dd1e004b..abea55dff9438 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf @@ -37,6 +37,11 @@ 인수 목록에 자동으로 완성 목록 표시 + + Block scoped + Block scoped + + C# C# @@ -67,6 +72,11 @@ 색 구성표 편집 + + File scoped + File scoped + + Format Document Settings (Experiment) 문서 서식 설정(실험) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf index 079f151946fd7..a8a9280d33d5d 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf @@ -37,6 +37,11 @@ Automatycznie pokaż listę uzupełniania na listach argumentów + + Block scoped + Block scoped + + C# C# @@ -67,6 +72,11 @@ Edytuj schemat kolorów + + File scoped + File scoped + + Format Document Settings (Experiment) Ustawienia dokumentu formatu (eksperyment) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf index afb037c984b51..f52fefb767553 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf @@ -37,6 +37,11 @@ Mostrar automaticamente a lista de conclusão nas listas de argumentos + + Block scoped + Block scoped + + C# C# @@ -67,6 +72,11 @@ Editar o esquema de cores + + File scoped + File scoped + + Format Document Settings (Experiment) Formatar Configurações do Documento (Experimento) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf index 5b284d87e3beb..ee61874789ede 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf @@ -37,6 +37,11 @@ Автоматически показывать список завершения в списках аргументов + + Block scoped + Block scoped + + C# C# @@ -67,6 +72,11 @@ Изменить цветовую схему + + File scoped + File scoped + + Format Document Settings (Experiment) Параметры форматирования документа (эксперимент) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf index f36be771b8a70..612d928a430a8 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf @@ -37,6 +37,11 @@ Bağımsız değişken listelerinde tamamlama listesini otomatik olarak göster + + Block scoped + Block scoped + + C# C# @@ -67,6 +72,11 @@ Renk düzenini düzenle + + File scoped + File scoped + + Format Document Settings (Experiment) Belge Biçimlendirme Ayarları (Deneme) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf index 7d200d4f2e050..47b143e74d031 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf @@ -37,6 +37,11 @@ 自动显示参数列表中的补全列表 + + Block scoped + Block scoped + + C# C# @@ -67,6 +72,11 @@ 编辑配色方案 + + File scoped + File scoped + + Format Document Settings (Experiment) 设定文档设置的格式(试验) diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf index 656e68e1a6e94..7ba6b834bcd71 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf @@ -37,6 +37,11 @@ 自動在引數清單中顯示自動完成清單 + + Block scoped + Block scoped + + C# C# @@ -67,6 +72,11 @@ 編輯色彩配置 + + File scoped + File scoped + + Format Document Settings (Experiment) 格式化文件設定 (實驗) diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx index b87663df6a4e7..cc091ac718615 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.resx +++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx @@ -1773,7 +1773,7 @@ I agree to all of the foregoing: Select an appropriate symbol to start value tracking - - Prefer file scoped namespace + + Namespace declarations \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf index 983b412b64713..5755473678de6 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf @@ -567,6 +567,11 @@ Obor názvů: {0} + + Namespace declarations + Namespace declarations + + class class @@ -852,11 +857,6 @@ Preferovat složená přiřazení - - Prefer file scoped namespace - Prefer file scoped namespace - - Prefer index operator Preferovat operátor indexu diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf index 30f204e1ac5fe..2a81729c6343c 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf @@ -567,6 +567,11 @@ Namespace: {0} + + Namespace declarations + Namespace declarations + + class class @@ -852,11 +857,6 @@ Zusammengesetzte Zuweisungen bevorzugen - - Prefer file scoped namespace - Prefer file scoped namespace - - Prefer index operator Indexoperator bevorzugen diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf index a2067090ded69..2f02dcbd1ce85 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf @@ -567,6 +567,11 @@ Espacio de nombres: "{0}" + + Namespace declarations + Namespace declarations + + class class @@ -852,11 +857,6 @@ Preferir asignaciones compuestas - - Prefer file scoped namespace - Prefer file scoped namespace - - Prefer index operator Preferir operador de índice diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf index a83a384e345e3..297a0de093fd0 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf @@ -567,6 +567,11 @@ Espace de noms : '{0}' + + Namespace declarations + Namespace declarations + + class class @@ -852,11 +857,6 @@ Préférer les affectations composées - - Prefer file scoped namespace - Prefer file scoped namespace - - Prefer index operator Préférer l'opérateur d'index diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf index a54a4bc68ce26..ee18dc376c805 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf @@ -567,6 +567,11 @@ Spazio dei nomi: '{0}' + + Namespace declarations + Namespace declarations + + class class @@ -852,11 +857,6 @@ Preferisci assegnazioni composte - - Prefer file scoped namespace - Prefer file scoped namespace - - Prefer index operator Preferisci operatore di indice diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf index ad552a217787f..05a3fb75345bc 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf @@ -567,6 +567,11 @@ 名前空間: '{0}' + + Namespace declarations + Namespace declarations + + class class @@ -852,11 +857,6 @@ 複合代入を優先 - - Prefer file scoped namespace - Prefer file scoped namespace - - Prefer index operator インデックス演算子を優先 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf index d9b671716a0a9..4505b455cf656 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf @@ -567,6 +567,11 @@ 네임스페이스: '{0}' + + Namespace declarations + Namespace declarations + + class class @@ -852,11 +857,6 @@ 복합 대입 선호 - - Prefer file scoped namespace - Prefer file scoped namespace - - Prefer index operator 인덱스 연산자 선호 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf index 879ed0a1b3487..20c6a6a169861 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf @@ -567,6 +567,11 @@ Przestrzeń nazw: „{0}” + + Namespace declarations + Namespace declarations + + class class @@ -852,11 +857,6 @@ Preferuj przypisania złożone - - Prefer file scoped namespace - Prefer file scoped namespace - - Prefer index operator Preferuj operator indeksowania diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf index d0b3666e850ea..40e8970554694 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf @@ -567,6 +567,11 @@ Namespace: '{0}' + + Namespace declarations + Namespace declarations + + class class @@ -852,11 +857,6 @@ Preferir atribuições de compostos - - Prefer file scoped namespace - Prefer file scoped namespace - - Prefer index operator Preferir operador de índice diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf index 9685cd62101e7..a4c678bf38fc8 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf @@ -567,6 +567,11 @@ Пространство имен: "{0}" + + Namespace declarations + Namespace declarations + + class class @@ -852,11 +857,6 @@ Предпочитать составные назначения - - Prefer file scoped namespace - Prefer file scoped namespace - - Prefer index operator Предпочитать оператор index diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf index 988193d97fe58..502ee11efc9ae 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf @@ -567,6 +567,11 @@ Ad alanı: '{0}' + + Namespace declarations + Namespace declarations + + class class @@ -852,11 +857,6 @@ Bileşik atamaları tercih et - - Prefer file scoped namespace - Prefer file scoped namespace - - Prefer index operator Dizin işlecini tercih et diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf index e3db8c68d12c3..569269df7feb9 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf @@ -567,6 +567,11 @@ 命名空间:“{0}” + + Namespace declarations + Namespace declarations + + class class @@ -852,11 +857,6 @@ 首选复合赋值 - - Prefer file scoped namespace - Prefer file scoped namespace - - Prefer index operator 首选索引运算符 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf index f604da633e660..4055d7ff62e0f 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf @@ -567,6 +567,11 @@ 命名空間: '{0}' + + Namespace declarations + Namespace declarations + + class class @@ -852,11 +857,6 @@ 優先使用複合指派 - - Prefer file scoped namespace - Prefer file scoped namespace - - Prefer index operator 優先使用索引運算子 diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs index dfe9729d35524..218b8047d774b 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeGeneration; +using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -107,7 +108,7 @@ private static SyntaxNode GenerateNamespaceDeclarationWorker( return SyntaxFactory.CompilationUnit().WithUsings(usings); if (destination == CodeGenerationDestination.CompilationUnit && - options.Options.GetOption(CSharpCodeStyleOptions.PreferFileScopedNamespace).Value && + options.Options.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations).Value == NamespaceDeclarationPreference.FileScoped && ((CSharpParseOptions)parseOptions)?.LanguageVersion >= LanguageVersion.CSharp10) { return SyntaxFactory.FileScopedNamespaceDeclaration(SyntaxFactory.ParseName(name)).WithUsings(usings); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs index 59a8751c952f7..23dbe9ddd82e9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs @@ -232,15 +232,15 @@ private static Option2> CreatePreferBra new(AddImportPlacement.OutsideNamespace, NotificationOption2.Silent); private static Option2> CreateUsingDirectivePlacementOption(string optionName, CodeStyleOption2 defaultValue, string editorconfigKeyName) - => CreateOption( - CSharpCodeStyleOptionGroups.UsingDirectivePreferences, optionName, - defaultValue, - storageLocations: new OptionStorageLocation2[] { - new EditorConfigStorageLocation>( - editorconfigKeyName, - s => ParseUsingDirectivesPlacement(s, defaultValue), - v => GetUsingDirectivesPlacementEditorConfigString(v, defaultValue)), - new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{optionName}")}); + => CreateOption( + CSharpCodeStyleOptionGroups.UsingDirectivePreferences, optionName, + defaultValue, + storageLocations: new OptionStorageLocation2[] { + new EditorConfigStorageLocation>( + editorconfigKeyName, + s => ParseUsingDirectivesPlacement(s, defaultValue), + v => GetUsingDirectivesPlacementEditorConfigString(v, defaultValue)), + new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{optionName}")}); public static readonly Option2> PreferredUsingDirectivePlacement = CreateUsingDirectivePlacementOption( nameof(PreferredUsingDirectivePlacement), defaultValue: PreferOutsidePlacementWithSilentEnforcement, "csharp_using_directive_placement"); @@ -295,11 +295,21 @@ private static Option2> CreateUsingDirectiv EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental", CodeStyleOptions2.TrueWithSilentEnforcement), new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(AllowBlankLineAfterColonInConstructorInitializer)}")}); - public static readonly Option2> PreferFileScopedNamespace = CreateOption( - CSharpCodeStyleOptionGroups.CodeBlockPreferences, nameof(PreferFileScopedNamespace), - defaultValue: CodeStyleOptions2.FalseWithSilentEnforcement, - "csharp_style_prefer_file_scoped_namespace", - $"TextEditor.CSharp.Specific.{nameof(PreferFileScopedNamespace)}"); + private static Option2> CreateNamespaceDeclarationOption(string optionName, CodeStyleOption2 defaultValue, string editorconfigKeyName) + => CreateOption( + CSharpCodeStyleOptionGroups.UsingDirectivePreferences, optionName, + defaultValue, + storageLocations: new OptionStorageLocation2[] { + new EditorConfigStorageLocation>( + editorconfigKeyName, + s => ParseNamespaceDeclaration(s, defaultValue), + v => GetNamespaceDeclarationEditorConfigString(v, defaultValue)), + new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{optionName}")}); + + public static readonly Option2> NamespaceDeclarations = CreateNamespaceDeclarationOption( + nameof(NamespaceDeclarations), + new(NamespaceDeclarationPreference.BlockScoped, NotificationOption2.Silent), + "csharp_style_namespace_declarations"); #if false diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions_Parsing.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions_Parsing.cs index ec1169e3cc556..c809d144cb365 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions_Parsing.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions_Parsing.cs @@ -50,7 +50,7 @@ public static CodeStyleOption2 ParseUsingDirectivesPlacement string optionString, CodeStyleOption2 @default) { if (CodeStyleHelpers.TryGetCodeStyleValueAndOptionalNotification( - optionString, @default.Notification, out var value, out var notification)) + optionString, @default.Notification, out var value, out var notification)) { return value switch { @@ -74,6 +74,34 @@ public static string GetUsingDirectivesPlacementEditorConfigString(CodeStyleOpti }; } + public static CodeStyleOption2 ParseNamespaceDeclaration( + string optionString, CodeStyleOption2 @default) + { + if (CodeStyleHelpers.TryGetCodeStyleValueAndOptionalNotification( + optionString, @default.Notification, out var value, out var notification)) + { + return value switch + { + "block_scoped" => new(NamespaceDeclarationPreference.BlockScoped, notification), + "file_scoped" => new(NamespaceDeclarationPreference.FileScoped, notification), + _ => throw new NotSupportedException(), + }; + } + + return @default; + } + + public static string GetNamespaceDeclarationEditorConfigString(CodeStyleOption2 value, CodeStyleOption2 defaultValue) + { + var notificationString = CodeStyleHelpers.GetEditorConfigStringNotificationPart(value, defaultValue); + return value.Value switch + { + NamespaceDeclarationPreference.BlockScoped => $"block_scoped{notificationString}", + NamespaceDeclarationPreference.FileScoped => $"file_scoped{notificationString}", + _ => throw new NotSupportedException(), + }; + } + private static CodeStyleOption2 ParsePreferBracesPreference( string optionString, CodeStyleOption2 defaultValue) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/NamespaceDeclarationPreference.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/NamespaceDeclarationPreference.cs new file mode 100644 index 0000000000000..a1c2581d70bc2 --- /dev/null +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/NamespaceDeclarationPreference.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.CodeStyle +{ + internal enum NamespaceDeclarationPreference + { + /// + /// Prefer namespace N { } + /// + BlockScoped, + /// + /// Prefer namespace N; + /// + FileScoped, + } +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index ac9929602d103..435f0de091df0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -182,6 +182,7 @@ + From 4c7b9dbd2bafcdbf3e1b1fc076e7dfc5ab0e933e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 21:22:51 -0700 Subject: [PATCH 21/45] switch to an enum --- .../Diagnostics/GenerateType/GenerateTypeTests.cs | 2 +- .../CSharpTest/ExtractClass/ExtractClassTests.cs | 14 +++++++------- .../ExtractInterface/ExtractInterfaceTests.cs | 6 +++--- .../Test/MetadataAsSource/MetadataAsSourceTests.cs | 9 ++++++--- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs index 7b4ae435ce3a4..0a6e961cae651 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs @@ -4907,7 +4907,7 @@ await TestAddDocumentInRegularAndScriptAsync(code, expectedDocumentName: "ClassB.cs", new TestParameters( parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp10), - options: Option(CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.TrueWithSilentEnforcement))); + options: Option(CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped, NotificationOption2.Silent))); } [WorkItem(932602, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/932602")] diff --git a/src/EditorFeatures/CSharpTest/ExtractClass/ExtractClassTests.cs b/src/EditorFeatures/CSharpTest/ExtractClass/ExtractClassTests.cs index 5558149e0adc2..60c277389b942 100644 --- a/src/EditorFeatures/CSharpTest/ExtractClass/ExtractClassTests.cs +++ b/src/EditorFeatures/CSharpTest/ExtractClass/ExtractClassTests.cs @@ -283,7 +283,7 @@ int Method() await TestAsync( input, expected, CSharpParseOptions.Default, - options: Option(CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.TrueWithSilentEnforcement), + options: Option(CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped, NotificationOption2.Silent), fixProviderData: new TestExtractClassOptionsService()); } @@ -335,7 +335,7 @@ int Method() await TestAsync( input, expected, CSharpParseOptions.Default, - options: Option(CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.TrueWithSilentEnforcement), + options: Option(CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped, NotificationOption2.Silent), fixProviderData: new TestExtractClassOptionsService()); } @@ -387,7 +387,7 @@ int Method() await TestAsync( input, expected, CSharpParseOptions.Default, - options: Option(CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.FalseWithSilentEnforcement), + options: Option(CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped, NotificationOption2.Silent), fixProviderData: new TestExtractClassOptionsService()); } @@ -1752,11 +1752,11 @@ int Method() private class TestExtractClassOptionsService : IExtractClassOptionsService { - private readonly IEnumerable<(string name, bool makeAbstract)> _dialogSelection; + private readonly IEnumerable<(string name, bool makeAbstract)>? _dialogSelection; private readonly bool _sameFile; private readonly bool isClassDeclarationSelection; - public TestExtractClassOptionsService(IEnumerable<(string name, bool makeAbstract)> dialogSelection = null, bool sameFile = false, bool isClassDeclarationSelection = false) + public TestExtractClassOptionsService(IEnumerable<(string name, bool makeAbstract)>? dialogSelection = null, bool sameFile = false, bool isClassDeclarationSelection = false) { _dialogSelection = dialogSelection; _sameFile = sameFile; @@ -1766,7 +1766,7 @@ public TestExtractClassOptionsService(IEnumerable<(string name, bool makeAbstrac public string FileName { get; set; } = "MyBase.cs"; public string BaseName { get; set; } = "MyBase"; - public Task GetExtractClassOptionsAsync(Document document, INamedTypeSymbol originalSymbol, ISymbol selectedMember) + public Task GetExtractClassOptionsAsync(Document document, INamedTypeSymbol originalSymbol, ISymbol? selectedMember) { var availableMembers = originalSymbol.GetMembers().Where(member => MemberAndDestinationValidator.IsMemberValid(member)); @@ -1796,7 +1796,7 @@ public Task GetExtractClassOptionsAsync(Document document, s.makeAbstract)) .ToImmutableArray(); - return Task.FromResult(new ExtractClassOptions(FileName, BaseName, _sameFile, memberAnalysis)); + return Task.FromResult(new ExtractClassOptions(FileName, BaseName, _sameFile, memberAnalysis)); } } } diff --git a/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs b/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs index 47aed6fb8ad50..77afb177537a0 100644 --- a/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs +++ b/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs @@ -458,7 +458,7 @@ class MyClass parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp10), options: new OptionsCollection(LanguageNames.CSharp) { - { CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.TrueWithSilentEnforcement } + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped, NotificationOption2.Silent } }); var result = await testState.ExtractViaCommandAsync(); @@ -495,7 +495,7 @@ class MyClass parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9), options: new OptionsCollection(LanguageNames.CSharp) { - { CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.TrueWithSilentEnforcement } + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped, NotificationOption2.Silent } }); var result = await testState.ExtractViaCommandAsync(); @@ -533,7 +533,7 @@ class MyClass parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp10), options: new OptionsCollection(LanguageNames.CSharp) { - { CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.FalseWithSilentEnforcement } + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped, NotificationOption2.Silent } }); var result = await testState.ExtractViaCommandAsync(); diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs index 8f23c353c2b7b..7a3e752e1bb40 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs @@ -410,7 +410,8 @@ public async Task TestTypeInFileScopedNamespace1() LanguageNames.CSharp, SpecializedCollections.SingletonEnumerable(metadataSource), languageVersion: "10"); context.Workspace.SetOptions(context.Workspace.Options.WithChangedOption( - CSharpCodeStyleOptions.NamespaceDeclarations, CodeStyleOptions2.TrueWithSilentEnforcement)); + CSharpCodeStyleOptions.NamespaceDeclarations, + new CodeStyleOption2(NamespaceDeclarationPreference.FileScoped, NotificationOption2.Silent))); await context.GenerateAndVerifySourceAsync("N.C", $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null @@ -435,7 +436,8 @@ public async Task TestTypeInFileScopedNamespace2() LanguageNames.CSharp, SpecializedCollections.SingletonEnumerable(metadataSource), languageVersion: "9"); context.Workspace.SetOptions(context.Workspace.Options.WithChangedOption( - CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.TrueWithSilentEnforcement)); + CSharpCodeStyleOptions.NamespaceDeclarations, + new CodeStyleOption2(NamespaceDeclarationPreference.FileScoped, NotificationOption2.Silent))); await context.GenerateAndVerifySourceAsync("N.C", $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null @@ -461,7 +463,8 @@ public async Task TestTypeInFileScopedNamespace3() LanguageNames.CSharp, SpecializedCollections.SingletonEnumerable(metadataSource), languageVersion: "10"); context.Workspace.SetOptions(context.Workspace.Options.WithChangedOption( - CSharpCodeStyleOptions.PreferFileScopedNamespace, CodeStyleOptions2.FalseWithSilentEnforcement)); + CSharpCodeStyleOptions.NamespaceDeclarations, + new CodeStyleOption2(NamespaceDeclarationPreference.BlockScoped, NotificationOption2.Silent))); await context.GenerateAndVerifySourceAsync("N.C", $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null From 6f7881716a7033ff29eb9771d5599d6114b3ff71 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 22:15:21 -0700 Subject: [PATCH 22/45] Add tests --- .../ConvertNamespaceRefactoringTests.cs | 399 ++++++++++++++++++ 1 file changed, 399 insertions(+) create mode 100644 src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs new file mode 100644 index 0000000000000..6052bd079d3c9 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs @@ -0,0 +1,399 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.ConvertNamespace; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Testing; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertNamespace +{ + using VerifyCS = CSharpCodeRefactoringVerifier; + + public class ConvertNamespaceRefactoringTests + { + [Fact] + public async Task TestNoConvertToFileScopedInCSharp9() + { + var code = @" +namespace $$N +{ +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp9, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNoConvertToFileScopedInCSharp10WithFileScopedPreference() + { + var code = @" +namespace $$N +{ +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedInCSharp10WithBlockScopedPreference() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N +{ +} +", + FixedCode = @" +namespace $$N; +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNoConvertWithMultipleNamespaces() + { + var code = @" +namespace $$N +{ +} + +namespace N2 +{ +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNoConvertWithNestedNamespaces1() + { + var code = @" +namespace $$N +{ + namespace N2 + { + } +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNoConvertWithNestedNamespaces2() + { + var code = @" +namespace N +{ + namespace $$N2 + { + } +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNoConvertWithTopLevelStatement1() + { + var code = @" +int i = 0; + +namespace $$N +{ +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + ExpectedDiagnostics = + { + // /0/Test0.cs(2,1): error CS8805: Program using top-level statements must be an executable. + DiagnosticResult.CompilerError("CS8805").WithSpan(2, 1, 2, 11), + }, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNoConvertWithTopLevelStatement2() + { + var code = @" +namespace $$N +{ +} + +int i = 0; +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + ExpectedDiagnostics = + { + // /0/Test0.cs(6,1): error CS8803: Top-level statements must precede namespace and type declarations. + DiagnosticResult.CompilerError("CS8803").WithSpan(6, 1, 6, 11), + // /0/Test0.cs(6,1): error CS8805: Program using top-level statements must be an executable. + DiagnosticResult.CompilerError("CS8805").WithSpan(6, 1, 6, 11), + }, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedWithUsing1() + { + await new VerifyCS.Test + { + TestCode = @" +using System; + +namespace $$N +{ +} +", + FixedCode = @" +using System; + +namespace $$N; +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedWithUsing2() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N +{ + using System; +} +", + FixedCode = @" +namespace $$N; + +using System; +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedWithClass() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N +{ + class C + { + } +} +", + FixedCode = @" +namespace $$N; + +class C +{ +} +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedWithClassWithDocComment() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N +{ + /// + class C + { + } +} +", + FixedCode = @" +namespace $$N; + +/// +class C +{ +} +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedWithMissingCloseBrace() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N +{ + /// + class C + { + }{|CS1513:|}", + FixedCode = @" +namespace N; + +/// +class C +{ +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedWithCommentOnOpenCurly() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N +{ // comment + class C + { + } +} +", + FixedCode = @" +namespace $$N; // comment + +class C +{ +} +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedWithLeadingComment() + { + await new VerifyCS.Test + { + TestCode = @" +// copyright +namespace $$N +{ + class C + { + } +} +", + FixedCode = @" +// copyright +namespace $$N; + +class C +{ +} +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + } +} From 95b9cd5a0b2b0340a320ff2def255761c8c80acd Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 22:25:21 -0700 Subject: [PATCH 23/45] Update formatter --- .../Rules/ElasticTriviaFormattingRule.cs | 60 +++++++++++++------ 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs index 9b12761f852e6..a28d646d54218 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Extensions; @@ -126,20 +127,18 @@ private static void AddInitializerSuppressOperations(List lis // if operation is already forced, return as it is. if (operation.Option == AdjustNewLinesOption.ForceLines) - { return operation; - } if (!CommonFormattingHelpers.HasAnyWhitespaceElasticTrivia(previousToken, currentToken)) - { return operation; - } + + var afterFileScopedNamespaceOperation = GetAdjustNewLinesOperationAfterFileScopedNamespace(previousToken, currentToken); + if (afterFileScopedNamespaceOperation != null) + return afterFileScopedNamespaceOperation; var betweenMemberOperation = GetAdjustNewLinesOperationBetweenMembers(previousToken, currentToken); if (betweenMemberOperation != null) - { return betweenMemberOperation; - } var line = Math.Max(LineBreaksAfter(previousToken, currentToken), operation.Line); if (line == 0) @@ -150,6 +149,23 @@ private static void AddInitializerSuppressOperations(List lis return CreateAdjustNewLinesOperation(line, AdjustNewLinesOption.ForceLines); } + private static AdjustNewLinesOperation? GetAdjustNewLinesOperationAfterFileScopedNamespace(SyntaxToken previousToken, SyntaxToken currentToken) + { + if (previousToken.Kind() != SyntaxKind.SemicolonToken) + return null; + + if (currentToken.Kind() == SyntaxKind.EndOfFileToken) + return null; + + if (previousToken.Parent is not FileScopedNamespaceDeclarationSyntax) + return null; + + if (TryGetOperationBeforeDocComment(currentToken, out var operation)) + return operation; + + return FormattingOperations.CreateAdjustNewLinesOperation(2, AdjustNewLinesOption.ForceLines); + } + private static AdjustNewLinesOperation? GetAdjustNewLinesOperationBetweenMembers(SyntaxToken previousToken, SyntaxToken currentToken) { if (!FormattingRangeHelper.InBetweenTwoMembers(previousToken, currentToken)) @@ -164,17 +180,8 @@ private static void AddInitializerSuppressOperations(List lis return null; } - // see whether first non whitespace trivia after before the current member is a comment or not - var triviaList = currentToken.LeadingTrivia; - var firstNonWhitespaceTrivia = triviaList.FirstOrDefault(trivia => !IsWhitespace(trivia)); - if (firstNonWhitespaceTrivia.IsRegularOrDocComment()) - { - // the first one is a comment, add two more lines than existing number of lines - var numberOfLines = GetNumberOfLines(triviaList); - var numberOfLinesBeforeComment = GetNumberOfLines(triviaList.Take(triviaList.IndexOf(firstNonWhitespaceTrivia))); - var addedLines = (numberOfLinesBeforeComment < 1) ? 2 : 1; - return CreateAdjustNewLinesOperation(numberOfLines + addedLines, AdjustNewLinesOption.ForceLines); - } + if (TryGetOperationBeforeDocComment(currentToken, out var operation)) + return operation; // If we have two members of the same kind, we won't insert a blank line if both members // have any content (e.g. accessors bodies, non-empty method bodies, etc.). @@ -216,6 +223,25 @@ private static void AddInitializerSuppressOperations(List lis return FormattingOperations.CreateAdjustNewLinesOperation(2 /* +1 for member itself and +1 for a blank line*/, AdjustNewLinesOption.ForceLines); } + private static bool TryGetOperationBeforeDocComment(SyntaxToken currentToken, [NotNullWhen(true)] out AdjustNewLinesOperation? operation) + { + // see whether first non whitespace trivia after before the current member is a comment or not + var triviaList = currentToken.LeadingTrivia; + var firstNonWhitespaceTrivia = triviaList.FirstOrDefault(trivia => !IsWhitespace(trivia)); + if (!firstNonWhitespaceTrivia.IsRegularOrDocComment()) + { + operation = null; + return false; + } + + // the first one is a comment, add two more lines than existing number of lines + var numberOfLines = GetNumberOfLines(triviaList); + var numberOfLinesBeforeComment = GetNumberOfLines(triviaList.Take(triviaList.IndexOf(firstNonWhitespaceTrivia))); + var addedLines = numberOfLinesBeforeComment < 1 ? 2 : 1; + operation = CreateAdjustNewLinesOperation(numberOfLines + addedLines, AdjustNewLinesOption.ForceLines); + return true; + } + public override AdjustSpacesOperation? GetAdjustSpacesOperation(in SyntaxToken previousToken, in SyntaxToken currentToken, in NextGetAdjustSpacesOperation nextOperation) { var operation = nextOperation.Invoke(in previousToken, in currentToken); From 08ec0a730adcb792b98700bd8b59fbc4acc6bd7b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 22:29:26 -0700 Subject: [PATCH 24/45] tests --- .../ConvertNamespaceRefactoringTests.cs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs index 6052bd079d3c9..fc1ff2c1203f9 100644 --- a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs +++ b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs @@ -17,6 +17,8 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertNamespace public class ConvertNamespaceRefactoringTests { + #region Convert To File Scoped + [Fact] public async Task TestNoConvertToFileScopedInCSharp9() { @@ -78,6 +80,68 @@ namespace $$N; }.RunAsync(); } + [Fact] + public async Task TestOnNamespaceToken() + { + await new VerifyCS.Test + { + TestCode = @" +$$namespace N +{ +} +", + FixedCode = @" +namespace N; +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNotBeforeNamespaceToken() + { + var code = @" +$$ +namespace N +{ +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNotOnOpenBrace() + { + var code = @" +namespace N +$${ +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + [Fact] public async Task TestNoConvertWithMultipleNamespaces() { @@ -395,5 +459,7 @@ class C } }.RunAsync(); } + + #endregion } } From 10cecf51f8dd645b2705979c60b806b300a4309e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 22:52:51 -0700 Subject: [PATCH 25/45] Tests --- .../ConvertNamespaceRefactoringTests.cs | 463 ++++++++++++++++++ 1 file changed, 463 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs index fc1ff2c1203f9..17fc21106c2d3 100644 --- a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs +++ b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs @@ -461,5 +461,468 @@ class C } #endregion + + #region Convert To Block Scoped + + [Fact] + public async Task TestConvertToBlockScopedInCSharp9() + { + await new VerifyCS.Test + { + TestCode = @" +{|CS8773:namespace|} $$N; +", + FixedCode = @" +namespace $$N +{ +}", + LanguageVersion = LanguageVersion.CSharp9, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNoConvertToBlockScopedInCSharp10WithBlockScopedPreference() + { + var code = @" +namespace $$N; +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedInCSharp10WithFileScopedPreference() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; +", + FixedCode = @" +namespace $$N +{ +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockOnNamespaceToken2() + { + await new VerifyCS.Test + { + TestCode = @" +$$namespace N; +", + FixedCode = @" +namespace N +{ +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockNotBeforeNamespaceToken2() + { + var code = @" +$$ +namespace N; +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockNotAfterSemicolon() + { + var code = @" +namespace N; +$$ +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockAfterSemicolon() + { + var code = @" +namespace N; $$ +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockWithMultipleNamespaces() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; + +namespace {|CS8955:N2|} +{ +} +", + FixedCode = @" +namespace $$N +{ + namespace N2 + { + } +} +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockWithNestedNamespaces1() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; + +namespace {|CS8954:N2|}; +", + FixedCode = @" +namespace $$N +{ + namespace N2; +} +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockWithNestedNamespaces2() + { + await new VerifyCS.Test + { + TestCode = @" +namespace N +{ + namespace $${|CS8955:N2|}; +} +", + FixedCode = @" +namespace N +{ + namespace $$N2 + { + } +} +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockWithTopLevelStatement1() + { + await new VerifyCS.Test + { + TestCode = @" +{|CS8805:int i = 0;|} + +namespace $${|CS8956:N|}; +", + FixedCode = @" +int i = 0; + +namespace $$N +{ +} +", + LanguageVersion = LanguageVersion.CSharp10, + ExpectedDiagnostics = + { + // /0/Test0.cs(2,1): error CS8805: Program using top-level statements must be an executable. + DiagnosticResult.CompilerError("CS8805").WithSpan(2, 1, 2, 11), + }, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockWithTopLevelStatement2() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; + +int i = 0; +", + FixedCode = @" +namespace $$N +{ + {|CS0116:i|}nt i = 0; +} +", + LanguageVersion = LanguageVersion.CSharp10, + ExpectedDiagnostics = + { + // /0/Test0.cs(6,1): error CS8803: Top-level statements must precede namespace and type declarations. + DiagnosticResult.CompilerError("CS8803").WithSpan(6, 1, 6, 11), + // /0/Test0.cs(6,1): error CS8805: Program using top-level statements must be an executable. + DiagnosticResult.CompilerError("CS8805").WithSpan(6, 1, 6, 11), + }, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithUsing1() + { + await new VerifyCS.Test + { + TestCode = @" +using System; + +namespace $$N; +", + FixedCode = @" +using System; + +namespace $$N +{ +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithUsing2() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; + +using System; +", + FixedCode = @" +namespace $$N +{ + using System; +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithClass() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; + +class C +{ +} +", + FixedCode = @" +namespace $$N +{ + class C + { + } +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithClassWithDocComment() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; + +/// +class C +{ +} +", + FixedCode = @" +namespace $$N +{ + /// + class C + { + } +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithMissingCloseBrace() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; + +/// +class C +{{|CS1513:|}", + FixedCode = @" +namespace N; +{ + /// + class C + { + }", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithCommentOnSemicolon() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; // comment + +class C +{ +} +", + FixedCode = @" +namespace $$N +{ // comment + class C + { + } +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithLeadingComment() + { + await new VerifyCS.Test + { + TestCode = @" +// copyright +namespace $$N; + +class C +{ +} +", + FixedCode = @" +// copyright +namespace $$N +{ + class C + { + } +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + #endregion } } From d6e325c6322c4c81a47a3b402d05faf28cd28443 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 22:56:11 -0700 Subject: [PATCH 26/45] Add tests --- .../ConvertNamespaceRefactoringTests.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs index 17fc21106c2d3..b4b8867e6ee1b 100644 --- a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs +++ b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs @@ -616,8 +616,7 @@ namespace $$N namespace N2 { } -} -", +}", LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -639,7 +638,7 @@ namespace {|CS8954:N2|}; FixedCode = @" namespace $$N { - namespace N2; + namespace {|CS8955:N2|}; } ", LanguageVersion = LanguageVersion.CSharp10, @@ -688,7 +687,7 @@ public async Task TestConvertToBlockWithTopLevelStatement1() namespace $${|CS8956:N|}; ", FixedCode = @" -int i = 0; +{|CS8805:int i = 0;|} namespace $$N { @@ -715,7 +714,7 @@ public async Task TestConvertToBlockWithTopLevelStatement2() TestCode = @" namespace $$N; -int i = 0; +int {|CS0116:i|} = 0; ", FixedCode = @" namespace $$N @@ -853,12 +852,12 @@ namespace $$N; class C {{|CS1513:|}", FixedCode = @" -namespace N; +namespace N { /// class C { - }", + }{|CS1513:|}", LanguageVersion = LanguageVersion.CSharp10, Options = { From fb592a0574719e058478dc1f6acd1286abba310b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 23:03:43 -0700 Subject: [PATCH 27/45] tests --- .../ConvertNamespaceRefactoringTests.cs | 24 ++++--------------- .../Rules/ElasticTriviaFormattingRule.cs | 3 +++ 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs index b4b8867e6ee1b..546a33549ad47 100644 --- a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs +++ b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs @@ -633,14 +633,12 @@ public async Task TestConvertToBlockWithNestedNamespaces1() TestCode = @" namespace $$N; -namespace {|CS8954:N2|}; -", +namespace {|CS8954:N2|};", FixedCode = @" namespace $$N { namespace {|CS8955:N2|}; -} -", +}", LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -694,11 +692,6 @@ namespace $$N } ", LanguageVersion = LanguageVersion.CSharp10, - ExpectedDiagnostics = - { - // /0/Test0.cs(2,1): error CS8805: Program using top-level statements must be an executable. - DiagnosticResult.CompilerError("CS8805").WithSpan(2, 1, 2, 11), - }, Options = { { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } @@ -719,17 +712,9 @@ namespace $$N; FixedCode = @" namespace $$N { - {|CS0116:i|}nt i = 0; -} -", + int {|CS0116:i|} = 0; +}", LanguageVersion = LanguageVersion.CSharp10, - ExpectedDiagnostics = - { - // /0/Test0.cs(6,1): error CS8803: Top-level statements must precede namespace and type declarations. - DiagnosticResult.CompilerError("CS8803").WithSpan(6, 1, 6, 11), - // /0/Test0.cs(6,1): error CS8805: Program using top-level statements must be an executable. - DiagnosticResult.CompilerError("CS8805").WithSpan(6, 1, 6, 11), - }, Options = { { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } @@ -859,6 +844,7 @@ class C { }{|CS1513:|}", LanguageVersion = LanguageVersion.CSharp10, + CodeActionValidationMode = CodeActionValidationMode.None, Options = { { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs index a28d646d54218..0ef2fe32373c4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs @@ -157,6 +157,9 @@ private static void AddInitializerSuppressOperations(List lis if (currentToken.Kind() == SyntaxKind.EndOfFileToken) return null; + if (currentToken.Kind() == SyntaxKind.CloseBraceToken) + return null; + if (previousToken.Parent is not FileScopedNamespaceDeclarationSyntax) return null; From 7291263d1c92dc42ed4cc18ef87f33a5350952f7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 22 Jul 2021 23:05:20 -0700 Subject: [PATCH 28/45] Tests --- .../ConvertNamespace/ConvertNamespaceRefactoringTests.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs index 546a33549ad47..9e4b18b2c547d 100644 --- a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs +++ b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs @@ -689,8 +689,7 @@ namespace $${|CS8956:N|}; namespace $$N { -} -", +}", LanguageVersion = LanguageVersion.CSharp10, Options = { @@ -841,8 +840,7 @@ namespace N { /// class C - { - }{|CS1513:|}", + {}{|CS1513:|}", LanguageVersion = LanguageVersion.CSharp10, CodeActionValidationMode = CodeActionValidationMode.None, Options = From 3a6d0429fae15bfd72fb99ea874ff59c5efb65a1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 23 Jul 2021 16:07:35 -0700 Subject: [PATCH 29/45] add tests --- .../ConvertNamespaceAnalyzerTests.cs | 913 ++++++++++++++++++ 1 file changed, 913 insertions(+) create mode 100644 src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceAnalyzerTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceAnalyzerTests.cs new file mode 100644 index 0000000000000..955e5ef173aff --- /dev/null +++ b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceAnalyzerTests.cs @@ -0,0 +1,913 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.ConvertNamespace; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Testing; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertNamespace +{ + using VerifyCS = CSharpCodeFixVerifier; + + public class ConvertNamespaceAnalyzerTests + { + #region Convert To File Scoped + + [Fact] + public async Task TestNoConvertToFileScopedInCSharp9() + { + var code = @" +namespace $$N +{ +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp9, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNoConvertToFileScopedInCSharp10WithFileScopedPreference() + { + var code = @" +namespace $$N +{ +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedInCSharp10WithBlockScopedPreference() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N +{ +} +", + FixedCode = @" +namespace $$N; +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestOnNamespaceToken() + { + await new VerifyCS.Test + { + TestCode = @" +$$namespace N +{ +} +", + FixedCode = @" +namespace N; +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNotBeforeNamespaceToken() + { + var code = @" +$$ +namespace N +{ +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNotOnOpenBrace() + { + var code = @" +namespace N +$${ +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNoConvertWithMultipleNamespaces() + { + var code = @" +namespace $$N +{ +} + +namespace N2 +{ +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNoConvertWithNestedNamespaces1() + { + var code = @" +namespace $$N +{ + namespace N2 + { + } +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNoConvertWithNestedNamespaces2() + { + var code = @" +namespace N +{ + namespace $$N2 + { + } +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNoConvertWithTopLevelStatement1() + { + var code = @" +int i = 0; + +namespace $$N +{ +} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + ExpectedDiagnostics = + { + // /0/Test0.cs(2,1): error CS8805: Program using top-level statements must be an executable. + DiagnosticResult.CompilerError("CS8805").WithSpan(2, 1, 2, 11), + }, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNoConvertWithTopLevelStatement2() + { + var code = @" +namespace $$N +{ +} + +int i = 0; +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + ExpectedDiagnostics = + { + // /0/Test0.cs(6,1): error CS8803: Top-level statements must precede namespace and type declarations. + DiagnosticResult.CompilerError("CS8803").WithSpan(6, 1, 6, 11), + // /0/Test0.cs(6,1): error CS8805: Program using top-level statements must be an executable. + DiagnosticResult.CompilerError("CS8805").WithSpan(6, 1, 6, 11), + }, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedWithUsing1() + { + await new VerifyCS.Test + { + TestCode = @" +using System; + +namespace $$N +{ +} +", + FixedCode = @" +using System; + +namespace $$N; +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedWithUsing2() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N +{ + using System; +} +", + FixedCode = @" +namespace $$N; + +using System; +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedWithClass() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N +{ + class C + { + } +} +", + FixedCode = @" +namespace $$N; + +class C +{ +} +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedWithClassWithDocComment() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N +{ + /// + class C + { + } +} +", + FixedCode = @" +namespace $$N; + +/// +class C +{ +} +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedWithMissingCloseBrace() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N +{ + /// + class C + { + }{|CS1513:|}", + FixedCode = @" +namespace N; + +/// +class C +{ +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedWithCommentOnOpenCurly() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N +{ // comment + class C + { + } +} +", + FixedCode = @" +namespace $$N; // comment + +class C +{ +} +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToFileScopedWithLeadingComment() + { + await new VerifyCS.Test + { + TestCode = @" +// copyright +namespace $$N +{ + class C + { + } +} +", + FixedCode = @" +// copyright +namespace $$N; + +class C +{ +} +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + #endregion + + #region Convert To Block Scoped + + [Fact] + public async Task TestConvertToBlockScopedInCSharp9() + { + await new VerifyCS.Test + { + TestCode = @" +{|CS8773:namespace|} $$N; +", + FixedCode = @" +namespace $$N +{ +}", + LanguageVersion = LanguageVersion.CSharp9, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNoConvertToBlockScopedInCSharp10WithBlockScopedPreference() + { + var code = @" +namespace $$N; +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedInCSharp10WithFileScopedPreference() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; +", + FixedCode = @" +namespace $$N +{ +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockOnNamespaceToken2() + { + await new VerifyCS.Test + { + TestCode = @" +$$namespace N; +", + FixedCode = @" +namespace N +{ +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockNotBeforeNamespaceToken2() + { + var code = @" +$$ +namespace N; +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockNotAfterSemicolon() + { + var code = @" +namespace N; +$$ +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockAfterSemicolon() + { + await new VerifyCS.Test + { + TestCode = @" +namespace {|IDE0160:N|}; +", + FixedCode = @" +namespace N +{ +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockWithMultipleNamespaces() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; + +namespace {|CS8955:N2|} +{ +} +", + FixedCode = @" +namespace $$N +{ + namespace N2 + { + } +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockWithNestedNamespaces1() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; + +namespace {|CS8954:N2|};", + FixedCode = @" +namespace $$N +{ + namespace {|CS8955:N2|}; +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockWithNestedNamespaces2() + { + await new VerifyCS.Test + { + TestCode = @" +namespace N +{ + namespace $${|CS8955:N2|}; +} +", + FixedCode = @" +namespace N +{ + namespace $$N2 + { + } +} +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockWithTopLevelStatement1() + { + await new VerifyCS.Test + { + TestCode = @" +{|CS8805:int i = 0;|} + +namespace $${|CS8956:N|}; +", + FixedCode = @" +{|CS8805:int i = 0;|} + +namespace $$N +{ +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockWithTopLevelStatement2() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; + +int {|CS0116:i|} = 0; +", + FixedCode = @" +namespace $$N +{ + int {|CS0116:i|} = 0; +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithUsing1() + { + await new VerifyCS.Test + { + TestCode = @" +using System; + +namespace $$N; +", + FixedCode = @" +using System; + +namespace $$N +{ +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithUsing2() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; + +using System; +", + FixedCode = @" +namespace $$N +{ + using System; +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithClass() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; + +class C +{ +} +", + FixedCode = @" +namespace $$N +{ + class C + { + } +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithClassWithDocComment() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; + +/// +class C +{ +} +", + FixedCode = @" +namespace $$N +{ + /// + class C + { + } +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithMissingCloseBrace() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; + +/// +class C +{{|CS1513:|}", + FixedCode = @" +namespace N +{ + /// + class C + {}{|CS1513:|}", + LanguageVersion = LanguageVersion.CSharp10, + CodeActionValidationMode = CodeActionValidationMode.None, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithCommentOnSemicolon() + { + await new VerifyCS.Test + { + TestCode = @" +namespace $$N; // comment + +class C +{ +} +", + FixedCode = @" +namespace $$N +{ // comment + class C + { + } +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithLeadingComment() + { + await new VerifyCS.Test + { + TestCode = @" +// copyright +namespace $$N; + +class C +{ +} +", + FixedCode = @" +// copyright +namespace $$N +{ + class C + { + } +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + #endregion + } +} From 594d1befdc239b1f568b85901c3b4079aa3cfe6b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 24 Jul 2021 12:12:40 -0700 Subject: [PATCH 30/45] Fix test --- .../ConvertNamespaceAnalyzerTests.cs | 2 +- .../ConvertNamespaceDiagnosticAnalyzer.cs | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceAnalyzerTests.cs index 955e5ef173aff..a74c386cbd1b5 100644 --- a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceAnalyzerTests.cs @@ -586,7 +586,7 @@ public async Task TestConvertToBlockAfterSemicolon() await new VerifyCS.Test { TestCode = @" -namespace {|IDE0160:N|}; +{|IDE0160:namespace N;|} ", FixedCode = @" namespace N diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs index 86853a1916340..6e4a3dcdb541c 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs @@ -71,9 +71,7 @@ private void AnalyzeNamespace(SyntaxNodeAnalysisContext context) // if the diagnostic is hidden, show it anywhere from the `namespace` keyword through the name. // otherwise, if it's not hidden, just squiggle the name. var severity = option.Notification.Severity; - var diagnosticLocation = severity.WithDefaultSeverity(DiagnosticSeverity.Hidden) == ReportDiagnostic.Hidden - ? tree.GetLocation(TextSpan.FromBounds(declaration.SpanStart, declaration.Name.Span.End)) - : declaration.Name.GetLocation(); + var diagnosticLocation = GetDiagnosticLocation(declaration, tree, severity); return DiagnosticHelper.Create( descriptor, @@ -81,6 +79,18 @@ private void AnalyzeNamespace(SyntaxNodeAnalysisContext context) severity, ImmutableArray.Create(declaration.GetLocation()), ImmutableDictionary.Empty); + + static Location GetDiagnosticLocation(BaseNamespaceDeclarationSyntax declaration, SyntaxTree tree, ReportDiagnostic severity) + { + if (severity.WithDefaultSeverity(DiagnosticSeverity.Hidden) != ReportDiagnostic.Hidden) + return declaration.Name.GetLocation(); + + var end = declaration is FileScopedNamespaceDeclarationSyntax fileScopedNamespace + ? fileScopedNamespace.SemicolonToken.Span.End + : declaration.Name.Span.End; + + return tree.GetLocation(TextSpan.FromBounds(declaration.SpanStart, end)); + } } } } From 1656aa79e807ccf1acd73b080bd74537b521687a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 24 Jul 2021 12:18:17 -0700 Subject: [PATCH 31/45] Before split --- .../ConvertNamespace/ConvertNamespaceAnalyzerTests.cs | 8 ++++---- src/Features/CSharp/Portable/CSharpFeaturesResources.resx | 4 ++-- .../ConvertNamespaceDiagnosticAnalyzer.cs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceAnalyzerTests.cs index a74c386cbd1b5..22e94737f50db 100644 --- a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceAnalyzerTests.cs @@ -508,7 +508,7 @@ public async Task TestConvertToBlockScopedInCSharp10WithFileScopedPreference() await new VerifyCS.Test { TestCode = @" -namespace $$N; +{|IDE0160:namespace $$N;|} ", FixedCode = @" namespace $$N @@ -528,7 +528,7 @@ public async Task TestConvertToBlockOnNamespaceToken2() await new VerifyCS.Test { TestCode = @" -$$namespace N; +$${|IDE0160:namespace N;|} ", FixedCode = @" namespace N @@ -547,7 +547,7 @@ public async Task TestConvertToBlockNotBeforeNamespaceToken2() { var code = @" $$ -namespace N; +{|IDE0160:namespace N;|} "; await new VerifyCS.Test { @@ -565,7 +565,7 @@ namespace N; public async Task TestConvertToBlockNotAfterSemicolon() { var code = @" -namespace N; +{|IDE0160:namespace N;|} $$ "; await new VerifyCS.Test diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx index cdc3214538a68..6d29265190951 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx @@ -632,8 +632,8 @@ Convert to file-scoped namespace {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - - Convert to regular namespace + + Convert to block scoped namespace {Locked="namespace"} "namespace" is a C# keyword and should not be localized. \ No newline at end of file diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs index 6e4a3dcdb541c..5df960ac77dd9 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs @@ -18,7 +18,7 @@ internal class ConvertNamespaceDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiag private static readonly DiagnosticDescriptor s_useRegularNamespaceDescriptor = CreateDescriptorWithId( IDEDiagnosticIds.UseRegularNamespaceDiagnosticId, EnforceOnBuildValues.UseRegularNamespace, - new LocalizableResourceString(nameof(CSharpFeaturesResources.Convert_to_regular_namespace), CSharpFeaturesResources.ResourceManager, typeof(CSharpFeaturesResources))); + new LocalizableResourceString(nameof(CSharpFeaturesResources.Convert_to_block_scoped_namespace), CSharpFeaturesResources.ResourceManager, typeof(CSharpFeaturesResources))); private static readonly DiagnosticDescriptor s_useFileScopedNamespaceDescriptor = CreateDescriptorWithId( IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId, From fe53780a8d06472489be1002b4181a1885785382 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 24 Jul 2021 13:00:15 -0700 Subject: [PATCH 32/45] Update tests --- .../Core/Analyzers/EnforceOnBuildValues.cs | 2 +- .../Core/Analyzers/IDEDiagnosticIds.cs | 2 +- ...vertToBlockScopedNamespaceAnalyzerTests.cs | 413 ++++++++++++++++ ...vertToFileScopedNamespaceAnalyzerTests.cs} | 452 +----------------- .../ConvertNamespaceCodeFixProvider.cs | 6 +- ...ConvertNamespaceCodeRefactoringProvider.cs | 2 +- .../ConvertNamespaceDiagnosticAnalyzer.cs | 96 ---- .../ConvertNamespaceHelper.cs | 2 +- ...oBlockScopedNamespaceDiagnosticAnalyzer.cs | 70 +++ ...ToFileScopedNamespaceDiagnosticAnalyzer.cs | 72 +++ .../xlf/CSharpFeaturesResources.cs.xlf | 10 +- .../xlf/CSharpFeaturesResources.de.xlf | 10 +- .../xlf/CSharpFeaturesResources.es.xlf | 10 +- .../xlf/CSharpFeaturesResources.fr.xlf | 10 +- .../xlf/CSharpFeaturesResources.it.xlf | 10 +- .../xlf/CSharpFeaturesResources.ja.xlf | 10 +- .../xlf/CSharpFeaturesResources.ko.xlf | 10 +- .../xlf/CSharpFeaturesResources.pl.xlf | 10 +- .../xlf/CSharpFeaturesResources.pt-BR.xlf | 10 +- .../xlf/CSharpFeaturesResources.ru.xlf | 10 +- .../xlf/CSharpFeaturesResources.tr.xlf | 10 +- .../xlf/CSharpFeaturesResources.zh-Hans.xlf | 10 +- .../xlf/CSharpFeaturesResources.zh-Hant.xlf | 10 +- 23 files changed, 629 insertions(+), 618 deletions(-) create mode 100644 src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertToBlockScopedNamespaceAnalyzerTests.cs rename src/EditorFeatures/CSharpTest/ConvertNamespace/{ConvertNamespaceAnalyzerTests.cs => ConvertToFileScopedNamespaceAnalyzerTests.cs} (50%) delete mode 100644 src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs create mode 100644 src/Features/CSharp/Portable/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs create mode 100644 src/Features/CSharp/Portable/ConvertNamespace/ConvertToFileScopedNamespaceDiagnosticAnalyzer.cs diff --git a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs index 2b46f1504eb50..65b676107f306 100644 --- a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs +++ b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs @@ -26,7 +26,7 @@ internal static class EnforceOnBuildValues public const EnforceOnBuild InvalidSuppressMessageAttribute = /*IDE0076*/ EnforceOnBuild.HighlyRecommended; public const EnforceOnBuild LegacyFormatSuppressMessageAttribute = /*IDE0077*/ EnforceOnBuild.HighlyRecommended; public const EnforceOnBuild RemoveConfusingSuppressionForIsExpression = /*IDE0080*/ EnforceOnBuild.HighlyRecommended; - public const EnforceOnBuild UseRegularNamespace = /*IDE0160*/ EnforceOnBuild.HighlyRecommended; + public const EnforceOnBuild UseBlockScopedNamespace = /*IDE0160*/ EnforceOnBuild.HighlyRecommended; public const EnforceOnBuild UseFileScopedNamespace = /*IDE0161*/ EnforceOnBuild.HighlyRecommended; /* EnforceOnBuild.Recommended */ diff --git a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs index 0095c798b4bf5..551c36c740958 100644 --- a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs +++ b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs @@ -162,7 +162,7 @@ internal static class IDEDiagnosticIds public const string UseNullCheckOverTypeCheckDiagnosticId = "IDE0150"; - public const string UseRegularNamespaceDiagnosticId = "IDE0160"; + public const string UseBlockScopedNamespaceDiagnosticId = "IDE0160"; public const string UseFileScopedNamespaceDiagnosticId = "IDE0161"; // Analyzer error Ids diff --git a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertToBlockScopedNamespaceAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertToBlockScopedNamespaceAnalyzerTests.cs new file mode 100644 index 0000000000000..8475cc0ecfa66 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertToBlockScopedNamespaceAnalyzerTests.cs @@ -0,0 +1,413 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.ConvertNamespace; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Testing; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertNamespace +{ + using VerifyCS = CSharpCodeFixVerifier; + + public class ConvertToBlockScopedNamespaceAnalyzerTests + { + #region Convert To Block Scoped + + [Fact] + public async Task TestConvertToBlockScopedInCSharp9() + { + await new VerifyCS.Test + { + TestCode = @" +[|{|CS8773:namespace|} N;|] +", + FixedCode = @" +namespace N +{ +}", + LanguageVersion = LanguageVersion.CSharp9, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNoConvertToBlockScopedInCSharp10WithBlockScopedPreference() + { + var code = @" +namespace N {} +"; + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedInCSharp10WithFileScopedPreference() + { + await new VerifyCS.Test + { + TestCode = @" +[|namespace N;|] +", + FixedCode = @" +namespace N +{ +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockSpan() + { + await new VerifyCS.Test + { + TestCode = @" +[|namespace N;|] +", + FixedCode = @" +namespace N +{ +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockWithMultipleNamespaces() + { + await new VerifyCS.Test + { + TestCode = @" +[|namespace N;|] + +namespace {|CS8955:N2|} +{ +} +", + FixedCode = @" +namespace N +{ + namespace N2 + { + } +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockWithNestedNamespaces1() + { + await new VerifyCS.Test + { + TestCode = @" +[|namespace N;|] + +[|namespace {|CS8954:N2|};|]", + FixedCode = @" +namespace N +{ + namespace N2 + { + } +}", + LanguageVersion = LanguageVersion.CSharp10, + NumberOfFixAllIterations = 2, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockWithNestedNamespaces2() + { + await new VerifyCS.Test + { + TestCode = @" +namespace N +{ + [|namespace {|CS8955:N2|};|] +} +", + FixedCode = @" +namespace N +{ + namespace $$N2 + { + } +} +", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockWithTopLevelStatement1() + { + await new VerifyCS.Test + { + TestCode = @" +{|CS8805:int i = 0;|} + +[|namespace {|CS8956:N|};|] +", + FixedCode = @" +{|CS8805:int i = 0;|} + +namespace N +{ +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockWithTopLevelStatement2() + { + await new VerifyCS.Test + { + TestCode = @" +[|namespace N;|] + +int {|CS0116:i|} = 0; +", + FixedCode = @" +namespace N +{ + int {|CS0116:i|} = 0; +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithUsing1() + { + await new VerifyCS.Test + { + TestCode = @" +using System; + +[|namespace N;|] +", + FixedCode = @" +using System; + +namespace N +{ +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithUsing2() + { + await new VerifyCS.Test + { + TestCode = @" +[|namespace N;|] + +using System; +", + FixedCode = @" +namespace $$N +{ + using System; +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithClass() + { + await new VerifyCS.Test + { + TestCode = @" +[|namespace N;|] + +class C +{ +} +", + FixedCode = @" +namespace $$N +{ + class C + { + } +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithClassWithDocComment() + { + await new VerifyCS.Test + { + TestCode = @" +[|namespace N;|] + +/// +class C +{ +} +", + FixedCode = @" +namespace N +{ + /// + class C + { + } +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithMissingCloseBrace() + { + await new VerifyCS.Test + { + TestCode = @" +[|namespace N;|] + +/// +class C +{{|CS1513:|}", + FixedCode = @" +namespace N +{ + /// + class C + {}{|CS1513:|}", + LanguageVersion = LanguageVersion.CSharp10, + CodeActionValidationMode = CodeActionValidationMode.None, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithCommentOnSemicolon() + { + await new VerifyCS.Test + { + TestCode = @" +[|namespace N;|] // comment + +class C +{ +} +", + FixedCode = @" +namespace N +{ // comment + class C + { + } +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + [Fact] + public async Task TestConvertToBlockScopedWithLeadingComment() + { + await new VerifyCS.Test + { + TestCode = @" +// copyright +[|namespace N;|] + +class C +{ +} +", + FixedCode = @" +// copyright +namespace N +{ + class C + { + } +}", + LanguageVersion = LanguageVersion.CSharp10, + Options = + { + { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } + } + }.RunAsync(); + } + + #endregion + } +} diff --git a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs similarity index 50% rename from src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceAnalyzerTests.cs rename to src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs index 22e94737f50db..8e89823c2f9f4 100644 --- a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs @@ -13,9 +13,9 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertNamespace { - using VerifyCS = CSharpCodeFixVerifier; + using VerifyCS = CSharpCodeFixVerifier; - public class ConvertNamespaceAnalyzerTests + public class ConvertToFileScopedNamespaceAnalyzerTests { #region Convert To File Scoped @@ -461,453 +461,5 @@ class C } #endregion - - #region Convert To Block Scoped - - [Fact] - public async Task TestConvertToBlockScopedInCSharp9() - { - await new VerifyCS.Test - { - TestCode = @" -{|CS8773:namespace|} $$N; -", - FixedCode = @" -namespace $$N -{ -}", - LanguageVersion = LanguageVersion.CSharp9, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestNoConvertToBlockScopedInCSharp10WithBlockScopedPreference() - { - var code = @" -namespace $$N; -"; - await new VerifyCS.Test - { - TestCode = code, - FixedCode = code, - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockScopedInCSharp10WithFileScopedPreference() - { - await new VerifyCS.Test - { - TestCode = @" -{|IDE0160:namespace $$N;|} -", - FixedCode = @" -namespace $$N -{ -}", - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockOnNamespaceToken2() - { - await new VerifyCS.Test - { - TestCode = @" -$${|IDE0160:namespace N;|} -", - FixedCode = @" -namespace N -{ -}", - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockNotBeforeNamespaceToken2() - { - var code = @" -$$ -{|IDE0160:namespace N;|} -"; - await new VerifyCS.Test - { - TestCode = code, - FixedCode = code, - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockNotAfterSemicolon() - { - var code = @" -{|IDE0160:namespace N;|} -$$ -"; - await new VerifyCS.Test - { - TestCode = code, - FixedCode = code, - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockAfterSemicolon() - { - await new VerifyCS.Test - { - TestCode = @" -{|IDE0160:namespace N;|} -", - FixedCode = @" -namespace N -{ -}", - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockWithMultipleNamespaces() - { - await new VerifyCS.Test - { - TestCode = @" -namespace $$N; - -namespace {|CS8955:N2|} -{ -} -", - FixedCode = @" -namespace $$N -{ - namespace N2 - { - } -}", - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockWithNestedNamespaces1() - { - await new VerifyCS.Test - { - TestCode = @" -namespace $$N; - -namespace {|CS8954:N2|};", - FixedCode = @" -namespace $$N -{ - namespace {|CS8955:N2|}; -}", - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockWithNestedNamespaces2() - { - await new VerifyCS.Test - { - TestCode = @" -namespace N -{ - namespace $${|CS8955:N2|}; -} -", - FixedCode = @" -namespace N -{ - namespace $$N2 - { - } -} -", - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockWithTopLevelStatement1() - { - await new VerifyCS.Test - { - TestCode = @" -{|CS8805:int i = 0;|} - -namespace $${|CS8956:N|}; -", - FixedCode = @" -{|CS8805:int i = 0;|} - -namespace $$N -{ -}", - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockWithTopLevelStatement2() - { - await new VerifyCS.Test - { - TestCode = @" -namespace $$N; - -int {|CS0116:i|} = 0; -", - FixedCode = @" -namespace $$N -{ - int {|CS0116:i|} = 0; -}", - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockScopedWithUsing1() - { - await new VerifyCS.Test - { - TestCode = @" -using System; - -namespace $$N; -", - FixedCode = @" -using System; - -namespace $$N -{ -}", - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockScopedWithUsing2() - { - await new VerifyCS.Test - { - TestCode = @" -namespace $$N; - -using System; -", - FixedCode = @" -namespace $$N -{ - using System; -}", - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockScopedWithClass() - { - await new VerifyCS.Test - { - TestCode = @" -namespace $$N; - -class C -{ -} -", - FixedCode = @" -namespace $$N -{ - class C - { - } -}", - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockScopedWithClassWithDocComment() - { - await new VerifyCS.Test - { - TestCode = @" -namespace $$N; - -/// -class C -{ -} -", - FixedCode = @" -namespace $$N -{ - /// - class C - { - } -}", - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockScopedWithMissingCloseBrace() - { - await new VerifyCS.Test - { - TestCode = @" -namespace $$N; - -/// -class C -{{|CS1513:|}", - FixedCode = @" -namespace N -{ - /// - class C - {}{|CS1513:|}", - LanguageVersion = LanguageVersion.CSharp10, - CodeActionValidationMode = CodeActionValidationMode.None, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockScopedWithCommentOnSemicolon() - { - await new VerifyCS.Test - { - TestCode = @" -namespace $$N; // comment - -class C -{ -} -", - FixedCode = @" -namespace $$N -{ // comment - class C - { - } -}", - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestConvertToBlockScopedWithLeadingComment() - { - await new VerifyCS.Test - { - TestCode = @" -// copyright -namespace $$N; - -class C -{ -} -", - FixedCode = @" -// copyright -namespace $$N -{ - class C - { - } -}", - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.BlockScoped } - } - }.RunAsync(); - } - - #endregion } } diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs index 30989f9dfc20c..8eae4f6253595 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs @@ -29,7 +29,7 @@ public ConvertNamespaceCodeFixProvider() internal override CodeFixCategory CodeFixCategory => CodeFixCategory.CodeStyle; public override ImmutableArray FixableDiagnosticIds - => ImmutableArray.Create(IDEDiagnosticIds.UseRegularNamespaceDiagnosticId, IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId); + => ImmutableArray.Create(IDEDiagnosticIds.UseBlockScopedNamespaceDiagnosticId, IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId); public override Task RegisterCodeFixesAsync(CodeFixContext context) { @@ -37,10 +37,10 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) var title = diagnostic.Id == IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId ? CSharpFeaturesResources.Convert_to_file_scoped_namespace - : CSharpFeaturesResources.Convert_to_regular_namespace; + : CSharpFeaturesResources.Convert_to_block_scoped_namespace; var equivalenceKey = diagnostic.Id == IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId ? nameof(CSharpFeaturesResources.Convert_to_file_scoped_namespace) - : nameof(CSharpFeaturesResources.Convert_to_regular_namespace); + : nameof(CSharpFeaturesResources.Convert_to_block_scoped_namespace); context.RegisterCodeFix( new MyCodeAction(title, c => FixAsync(context.Document, diagnostic, c), equivalenceKey), diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs index 2d310a0167f5f..0b58cf5d568bc 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs @@ -42,7 +42,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var title = - ConvertNamespaceHelper.CanOfferUseRegular(optionSet, namespaceDecl, forAnalyzer: false) ? CSharpFeaturesResources.Convert_to_regular_namespace : + ConvertNamespaceHelper.CanOfferUseBlockScoped(optionSet, namespaceDecl, forAnalyzer: false) ? CSharpFeaturesResources.Convert_to_block_scoped_namespace : ConvertNamespaceHelper.CanOfferUseFileScoped(optionSet, root, namespaceDecl, forAnalyzer: false) ? CSharpFeaturesResources.Convert_to_file_scoped_namespace : null; if (title == null) return; diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs deleted file mode 100644 index 5df960ac77dd9..0000000000000 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceDiagnosticAnalyzer.cs +++ /dev/null @@ -1,96 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - internal class ConvertNamespaceDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer - { - private static readonly DiagnosticDescriptor s_useRegularNamespaceDescriptor = CreateDescriptorWithId( - IDEDiagnosticIds.UseRegularNamespaceDiagnosticId, - EnforceOnBuildValues.UseRegularNamespace, - new LocalizableResourceString(nameof(CSharpFeaturesResources.Convert_to_block_scoped_namespace), CSharpFeaturesResources.ResourceManager, typeof(CSharpFeaturesResources))); - - private static readonly DiagnosticDescriptor s_useFileScopedNamespaceDescriptor = CreateDescriptorWithId( - IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId, - EnforceOnBuildValues.UseFileScopedNamespace, - new LocalizableResourceString(nameof(CSharpFeaturesResources.Convert_to_file_scoped_namespace), CSharpFeaturesResources.ResourceManager, typeof(CSharpFeaturesResources))); - - private static readonly ImmutableDictionary s_descriptors = - ImmutableDictionary.Empty - .Add(s_useRegularNamespaceDescriptor, CSharpCodeStyleOptions.NamespaceDeclarations) - .Add(s_useFileScopedNamespaceDescriptor, CSharpCodeStyleOptions.NamespaceDeclarations); - - public ConvertNamespaceDiagnosticAnalyzer() - : base(s_descriptors, LanguageNames.CSharp) - { - } - - public sealed override DiagnosticAnalyzerCategory GetAnalyzerCategory() - => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; - - protected override void InitializeWorker(AnalysisContext context) - => context.RegisterSyntaxNodeAction(AnalyzeNamespace, SyntaxKind.NamespaceDeclaration, SyntaxKind.FileScopedNamespaceDeclaration); - - private void AnalyzeNamespace(SyntaxNodeAnalysisContext context) - { - var options = context.Options; - var namespaceDeclaration = (BaseNamespaceDeclarationSyntax)context.Node; - var syntaxTree = namespaceDeclaration.SyntaxTree; - - var cancellationToken = context.CancellationToken; - var root = (CompilationUnitSyntax)syntaxTree.GetRoot(cancellationToken); - - var optionSet = options.GetAnalyzerOptionSet(syntaxTree, cancellationToken); - - var diagnostic = AnalyzeNamespace(optionSet, root, namespaceDeclaration); - if (diagnostic != null) - context.ReportDiagnostic(diagnostic); - } - - private static Diagnostic? AnalyzeNamespace(OptionSet optionSet, CompilationUnitSyntax root, BaseNamespaceDeclarationSyntax declaration) - { - var tree = declaration.SyntaxTree; - var option = optionSet.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations); - - var descriptor = - ConvertNamespaceHelper.CanOfferUseRegular(optionSet, declaration, forAnalyzer: true) ? s_useRegularNamespaceDescriptor : - ConvertNamespaceHelper.CanOfferUseFileScoped(optionSet, root, declaration, forAnalyzer: true) ? s_useFileScopedNamespaceDescriptor : null; - if (descriptor == null) - return null; - - // if the diagnostic is hidden, show it anywhere from the `namespace` keyword through the name. - // otherwise, if it's not hidden, just squiggle the name. - var severity = option.Notification.Severity; - var diagnosticLocation = GetDiagnosticLocation(declaration, tree, severity); - - return DiagnosticHelper.Create( - descriptor, - diagnosticLocation, - severity, - ImmutableArray.Create(declaration.GetLocation()), - ImmutableDictionary.Empty); - - static Location GetDiagnosticLocation(BaseNamespaceDeclarationSyntax declaration, SyntaxTree tree, ReportDiagnostic severity) - { - if (severity.WithDefaultSeverity(DiagnosticSeverity.Hidden) != ReportDiagnostic.Hidden) - return declaration.Name.GetLocation(); - - var end = declaration is FileScopedNamespaceDeclarationSyntax fileScopedNamespace - ? fileScopedNamespace.SemicolonToken.Span.End - : declaration.Name.Span.End; - - return tree.GetLocation(TextSpan.FromBounds(declaration.SpanStart, end)); - } - } - } -} diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs index 0355da9ae14b4..e0068bb4030d8 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace { internal static class ConvertNamespaceHelper { - internal static bool CanOfferUseRegular(OptionSet optionSet, BaseNamespaceDeclarationSyntax declaration, bool forAnalyzer) + internal static bool CanOfferUseBlockScoped(OptionSet optionSet, BaseNamespaceDeclarationSyntax declaration, bool forAnalyzer) { if (declaration is not FileScopedNamespaceDeclarationSyntax) return false; diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..4ac8d2ce6f740 --- /dev/null +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal class ConvertToBlockScopedNamespaceDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer + { + public ConvertToBlockScopedNamespaceDiagnosticAnalyzer() + : base(IDEDiagnosticIds.UseBlockScopedNamespaceDiagnosticId, + EnforceOnBuildValues.UseBlockScopedNamespace, + CSharpCodeStyleOptions.NamespaceDeclarations, + LanguageNames.CSharp, + new LocalizableResourceString(nameof(CSharpFeaturesResources.Convert_to_block_scoped_namespace), CSharpFeaturesResources.ResourceManager, typeof(CSharpFeaturesResources))) + { + } + + public sealed override DiagnosticAnalyzerCategory GetAnalyzerCategory() + => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; + + protected override void InitializeWorker(AnalysisContext context) + => context.RegisterSyntaxNodeAction(AnalyzeNamespace, SyntaxKind.FileScopedNamespaceDeclaration); + + private void AnalyzeNamespace(SyntaxNodeAnalysisContext context) + { + var options = context.Options; + var namespaceDeclaration = (FileScopedNamespaceDeclarationSyntax)context.Node; + var syntaxTree = namespaceDeclaration.SyntaxTree; + + var cancellationToken = context.CancellationToken; + var optionSet = options.GetAnalyzerOptionSet(syntaxTree, cancellationToken); + + var diagnostic = AnalyzeNamespace(optionSet, namespaceDeclaration); + if (diagnostic != null) + context.ReportDiagnostic(diagnostic); + } + + private Diagnostic? AnalyzeNamespace(OptionSet optionSet, FileScopedNamespaceDeclarationSyntax declaration) + { + var tree = declaration.SyntaxTree; + var option = optionSet.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations); + + if (!ConvertNamespaceHelper.CanOfferUseBlockScoped(optionSet, declaration, forAnalyzer: true)) + return null; + + // if the diagnostic is hidden, show it anywhere from the `namespace` keyword through the name. + // otherwise, if it's not hidden, just squiggle the name. + var severity = option.Notification.Severity; + var diagnosticLocation = severity.WithDefaultSeverity(DiagnosticSeverity.Hidden) != ReportDiagnostic.Hidden + ? declaration.Name.GetLocation() + : tree.GetLocation(TextSpan.FromBounds(declaration.SpanStart, declaration.SemicolonToken.Span.End)); + + return DiagnosticHelper.Create( + this.Descriptor, + diagnosticLocation, + severity, + ImmutableArray.Create(declaration.GetLocation()), + ImmutableDictionary.Empty); + } + } +} diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertToFileScopedNamespaceDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertToFileScopedNamespaceDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..d75024e6238b9 --- /dev/null +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertToFileScopedNamespaceDiagnosticAnalyzer.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal class ConvertToFileScopedNamespaceDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer + { + public ConvertToFileScopedNamespaceDiagnosticAnalyzer() + : base(IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId, + EnforceOnBuildValues.UseFileScopedNamespace, + CSharpCodeStyleOptions.NamespaceDeclarations, + LanguageNames.CSharp, + new LocalizableResourceString(nameof(CSharpFeaturesResources.Convert_to_file_scoped_namespace), CSharpFeaturesResources.ResourceManager, typeof(CSharpFeaturesResources))) + { + } + + public sealed override DiagnosticAnalyzerCategory GetAnalyzerCategory() + => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; + + protected override void InitializeWorker(AnalysisContext context) + => context.RegisterSyntaxNodeAction(AnalyzeNamespace, SyntaxKind.NamespaceDeclaration); + + private void AnalyzeNamespace(SyntaxNodeAnalysisContext context) + { + var options = context.Options; + var namespaceDeclaration = (NamespaceDeclarationSyntax)context.Node; + var syntaxTree = namespaceDeclaration.SyntaxTree; + + var cancellationToken = context.CancellationToken; + var root = (CompilationUnitSyntax)syntaxTree.GetRoot(cancellationToken); + + var optionSet = options.GetAnalyzerOptionSet(syntaxTree, cancellationToken); + + var diagnostic = AnalyzeNamespace(optionSet, root, namespaceDeclaration); + if (diagnostic != null) + context.ReportDiagnostic(diagnostic); + } + + private Diagnostic? AnalyzeNamespace(OptionSet optionSet, CompilationUnitSyntax root, BaseNamespaceDeclarationSyntax declaration) + { + var tree = declaration.SyntaxTree; + var option = optionSet.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations); + + if (!ConvertNamespaceHelper.CanOfferUseFileScoped(optionSet, root, declaration, forAnalyzer: true)) + return null; + + // if the diagnostic is hidden, show it anywhere from the `namespace` keyword through the name. + // otherwise, if it's not hidden, just squiggle the name. + var severity = option.Notification.Severity; + var diagnosticLocation = severity.WithDefaultSeverity(DiagnosticSeverity.Hidden) != ReportDiagnostic.Hidden + ? declaration.Name.GetLocation() + : tree.GetLocation(TextSpan.FromBounds(declaration.SpanStart, declaration.Name.Span.End)); + + return DiagnosticHelper.Create( + this.Descriptor, + diagnosticLocation, + severity, + ImmutableArray.Create(declaration.GetLocation()), + ImmutableDictionary.Empty); + } + } +} diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf index a14640e92f5a5..a46682811de62 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf @@ -92,6 +92,11 @@ Porovnat s {0} + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to file-scoped namespace Convert to file-scoped namespace @@ -102,11 +107,6 @@ Převést na metodu - - Convert to regular namespace - Convert to regular namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to regular string Převést na běžný řetězec diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf index 7011c19c4abaf..f84d08a947283 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf @@ -92,6 +92,11 @@ Mit "{0}" vergleichen + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to file-scoped namespace Convert to file-scoped namespace @@ -102,11 +107,6 @@ In Methode konvertieren - - Convert to regular namespace - Convert to regular namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to regular string In reguläre Zeichenfolge konvertieren diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf index a326f9104e627..f87445627502b 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf @@ -92,6 +92,11 @@ Comparar con "{0}" + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to file-scoped namespace Convert to file-scoped namespace @@ -102,11 +107,6 @@ Convertir al método - - Convert to regular namespace - Convert to regular namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to regular string Convertir en cadena regular diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf index bb1ab5fcce5ed..4eaa2c930bd0a 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf @@ -92,6 +92,11 @@ Comparer à '{0}' + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to file-scoped namespace Convert to file-scoped namespace @@ -102,11 +107,6 @@ Convertir en méthode - - Convert to regular namespace - Convert to regular namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to regular string Convertir en chaîne classique diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf index fff81d4617522..2c674cb495c0d 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf @@ -92,6 +92,11 @@ Confronta con '{0}' + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to file-scoped namespace Convert to file-scoped namespace @@ -102,11 +107,6 @@ Converti in metodo - - Convert to regular namespace - Convert to regular namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to regular string Converti in stringa normale diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf index 99ecca3c1887f..318cd40b4f73f 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf @@ -92,6 +92,11 @@ "{0}" と比較 + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to file-scoped namespace Convert to file-scoped namespace @@ -102,11 +107,6 @@ メソッドに変換 - - Convert to regular namespace - Convert to regular namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to regular string 正規文字列に変換する diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf index 0c23ea60b0c43..d92db785de1cd 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf @@ -92,6 +92,11 @@ '{0}'과(와) 비교 + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to file-scoped namespace Convert to file-scoped namespace @@ -102,11 +107,6 @@ 메서드로 변환 - - Convert to regular namespace - Convert to regular namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to regular string 일반 문자열로 변환 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf index 7a08cb94153bb..665eacd5cf0e7 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf @@ -92,6 +92,11 @@ Porównaj z „{0}” + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to file-scoped namespace Convert to file-scoped namespace @@ -102,11 +107,6 @@ Konwertuj na metodę - - Convert to regular namespace - Convert to regular namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to regular string Konwertuj na zwykły ciąg diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf index 14a27ef06add7..5dfaf26cb5413 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf @@ -92,6 +92,11 @@ Comparar a '{0}' + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to file-scoped namespace Convert to file-scoped namespace @@ -102,11 +107,6 @@ Converter em método - - Convert to regular namespace - Convert to regular namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to regular string Converter para cadeia de caracteres regular diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf index 0999abc405198..2097ab2d59449 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf @@ -92,6 +92,11 @@ Сравнить с "{0}" + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to file-scoped namespace Convert to file-scoped namespace @@ -102,11 +107,6 @@ Преобразовать в метод - - Convert to regular namespace - Convert to regular namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to regular string Преобразовать в обычную строку diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf index ce0049dfe19b6..27056008aea38 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf @@ -92,6 +92,11 @@ '{0}' ile karşılaştır + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to file-scoped namespace Convert to file-scoped namespace @@ -102,11 +107,6 @@ Yönteme dönüştür - - Convert to regular namespace - Convert to regular namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to regular string Normal dizeye dönüştür diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf index f13ed4dfa2d5f..7ad9566d983b4 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf @@ -92,6 +92,11 @@ 与 "{0}" 比较 + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to file-scoped namespace Convert to file-scoped namespace @@ -102,11 +107,6 @@ 转换为方法 - - Convert to regular namespace - Convert to regular namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to regular string 转换为正则字符串 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf index 0678a71cfddda..5f327745bdd28 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf @@ -92,6 +92,11 @@ 與 '{0}' 比較 + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Convert to file-scoped namespace Convert to file-scoped namespace @@ -102,11 +107,6 @@ 轉換為方法 - - Convert to regular namespace - Convert to regular namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to regular string 轉換為一般字串 From 1186364f420a85e57f90379d77ed6211aaea1543 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 24 Jul 2021 19:48:31 -0700 Subject: [PATCH 33/45] Tests --- ...nvertToFileScopedNamespaceAnalyzerTests.cs | 122 +++--------------- 1 file changed, 16 insertions(+), 106 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs index 8e89823c2f9f4..cfae0310380ec 100644 --- a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertToFileScopedNamespaceAnalyzerTests.cs @@ -23,7 +23,7 @@ public class ConvertToFileScopedNamespaceAnalyzerTests public async Task TestNoConvertToFileScopedInCSharp9() { var code = @" -namespace $$N +namespace N { } "; @@ -40,10 +40,10 @@ namespace $$N } [Fact] - public async Task TestNoConvertToFileScopedInCSharp10WithFileScopedPreference() + public async Task TestNoConvertToFileScopedInCSharp10WithBlockScopedPreference() { var code = @" -namespace $$N +namespace N { } "; @@ -65,7 +65,7 @@ public async Task TestConvertToFileScopedInCSharp10WithBlockScopedPreference() await new VerifyCS.Test { TestCode = @" -namespace $$N +[|namespace N|] { } ", @@ -80,73 +80,11 @@ namespace $$N; }.RunAsync(); } - [Fact] - public async Task TestOnNamespaceToken() - { - await new VerifyCS.Test - { - TestCode = @" -$$namespace N -{ -} -", - FixedCode = @" -namespace N; -", - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestNotBeforeNamespaceToken() - { - var code = @" -$$ -namespace N -{ -} -"; - await new VerifyCS.Test - { - TestCode = code, - FixedCode = code, - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestNotOnOpenBrace() - { - var code = @" -namespace N -$${ -} -"; - await new VerifyCS.Test - { - TestCode = code, - FixedCode = code, - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } - } - }.RunAsync(); - } - [Fact] public async Task TestNoConvertWithMultipleNamespaces() { var code = @" -namespace $$N +namespace N { } @@ -170,32 +108,9 @@ namespace N2 public async Task TestNoConvertWithNestedNamespaces1() { var code = @" -namespace $$N -{ - namespace N2 - { - } -} -"; - await new VerifyCS.Test - { - TestCode = code, - FixedCode = code, - LanguageVersion = LanguageVersion.CSharp10, - Options = - { - { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } - } - }.RunAsync(); - } - - [Fact] - public async Task TestNoConvertWithNestedNamespaces2() - { - var code = @" namespace N { - namespace $$N2 + namespace N2 { } } @@ -216,9 +131,9 @@ namespace $$N2 public async Task TestNoConvertWithTopLevelStatement1() { var code = @" -int i = 0; +{|CS8805:int i = 0;|} -namespace $$N +namespace N { } "; @@ -227,11 +142,6 @@ namespace $$N TestCode = code, FixedCode = code, LanguageVersion = LanguageVersion.CSharp10, - ExpectedDiagnostics = - { - // /0/Test0.cs(2,1): error CS8805: Program using top-level statements must be an executable. - DiagnosticResult.CompilerError("CS8805").WithSpan(2, 1, 2, 11), - }, Options = { { CSharpCodeStyleOptions.NamespaceDeclarations, NamespaceDeclarationPreference.FileScoped } @@ -243,7 +153,7 @@ namespace $$N public async Task TestNoConvertWithTopLevelStatement2() { var code = @" -namespace $$N +namespace N { } @@ -276,7 +186,7 @@ public async Task TestConvertToFileScopedWithUsing1() TestCode = @" using System; -namespace $$N +[|namespace N|] { } ", @@ -299,7 +209,7 @@ public async Task TestConvertToFileScopedWithUsing2() await new VerifyCS.Test { TestCode = @" -namespace $$N +[|namespace N|] { using System; } @@ -323,7 +233,7 @@ public async Task TestConvertToFileScopedWithClass() await new VerifyCS.Test { TestCode = @" -namespace $$N +[|namespace N|] { class C { @@ -351,7 +261,7 @@ public async Task TestConvertToFileScopedWithClassWithDocComment() await new VerifyCS.Test { TestCode = @" -namespace $$N +[|namespace N|] { /// class C @@ -381,7 +291,7 @@ public async Task TestConvertToFileScopedWithMissingCloseBrace() await new VerifyCS.Test { TestCode = @" -namespace $$N +[|namespace N|] { /// class C @@ -408,7 +318,7 @@ public async Task TestConvertToFileScopedWithCommentOnOpenCurly() await new VerifyCS.Test { TestCode = @" -namespace $$N +[|namespace N|] { // comment class C { @@ -437,7 +347,7 @@ public async Task TestConvertToFileScopedWithLeadingComment() { TestCode = @" // copyright -namespace $$N +[|namespace N|] { class C { From a0e35ea51ebe287704c3973d20643629a94139fb Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Sat, 24 Jul 2021 21:45:39 -0700 Subject: [PATCH 34/45] Adjust test baseline for `CSharpAddMissingUsingsOnPaste.VerifyDisabledWithNull` --- .../IntegrationTests/CSharp/CSharpAddMissingUsingsOnPaste.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpAddMissingUsingsOnPaste.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpAddMissingUsingsOnPaste.cs index c989728db5b4d..375446a46d1f5 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpAddMissingUsingsOnPaste.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpAddMissingUsingsOnPaste.cs @@ -85,6 +85,7 @@ static void Main(string[] args) VisualStudio.Editor.Verify.TextContains(@" using System; +using System.Threading.Tasks; class Program { From 13533a0ff1ed8c164342a189c4df5619a8a341ba Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 24 Jul 2021 22:33:56 -0700 Subject: [PATCH 35/45] Add name --- .../ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs | 2 +- .../CodeRefactorings/PredefinedCodeRefactoringProviderNames.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs index 0b58cf5d568bc..e5db057896599 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace { - [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] + [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.ConvertNamespace), Shared] internal class ConvertNamespaceCodeRefactoringProvider : CodeRefactoringProvider { [ImportingConstructor] diff --git a/src/Features/Core/Portable/CodeRefactorings/PredefinedCodeRefactoringProviderNames.cs b/src/Features/Core/Portable/CodeRefactorings/PredefinedCodeRefactoringProviderNames.cs index 8b785e7a95c59..ea00e9e4a6aaf 100644 --- a/src/Features/Core/Portable/CodeRefactorings/PredefinedCodeRefactoringProviderNames.cs +++ b/src/Features/Core/Portable/CodeRefactorings/PredefinedCodeRefactoringProviderNames.cs @@ -15,6 +15,7 @@ internal static class PredefinedCodeRefactoringProviderNames public const string ChangeSignature = "Change Signature Code Action Provider"; public const string ConvertAnonymousTypeToClass = "Convert Anonymous Type to Class Code Action Provider"; public const string ConvertDirectCastToTryCast = "Convert Direct Cast to Try Cast"; + public const string ConvertNamespace = "Convert Namespace"; public const string ConvertTryCastToDirectCast = "Convert Try Cast to Direct Cast"; public const string ConvertToInterpolatedString = "Convert To Interpolated String Code Action Provider"; public const string ConvertTupleToStruct = "Convert Tuple to Struct Code Action Provider"; From a72e35e6bc07fd4ba90892be6cac2308d824c7cf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 24 Jul 2021 22:37:42 -0700 Subject: [PATCH 36/45] Update tests --- .../Diagnostics/IDEDiagnosticIDConfigurationTests.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs index 936471ea40450..58502cba89a3c 100644 --- a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs @@ -420,6 +420,12 @@ public void CSharp_VerifyIDEDiagnosticSeveritiesAreConfigurable() # IDE0130 dotnet_diagnostic.IDE0130.severity = %value% +# IDE0160 +dotnet_diagnostic.IDE0160.severity = %value% + +# IDE0161 +dotnet_diagnostic.IDE0161.severity = %value% + # IDE2000 dotnet_diagnostic.IDE2000.severity = %value% @@ -995,6 +1001,12 @@ No editorconfig based code style option # IDE0150 No editorconfig based code style option +# IDE0160, NamespaceDeclarations +csharp_style_namespace_declarations = block_scoped + +# IDE0161, NamespaceDeclarations +csharp_style_namespace_declarations = block_scoped + # IDE1005, PreferConditionalDelegateCall csharp_style_conditional_delegate_call = true From 34541ed2c2246000050e495ee5c6ec4807a2a01f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 25 Jul 2021 17:32:26 -0700 Subject: [PATCH 37/45] Share code --- .../ConvertNamespaceCodeFixProvider.cs | 15 +++++++++------ .../ConvertNamespaceCodeRefactoringProvider.cs | 18 +++++++++++------- .../ConvertNamespace/ConvertNamespaceHelper.cs | 10 +++++++++- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs index 8eae4f6253595..db5b27f373354 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs @@ -10,11 +10,13 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace { @@ -35,12 +37,13 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) { var diagnostic = context.Diagnostics.First(); - var title = diagnostic.Id == IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId - ? CSharpFeaturesResources.Convert_to_file_scoped_namespace - : CSharpFeaturesResources.Convert_to_block_scoped_namespace; - var equivalenceKey = diagnostic.Id == IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId - ? nameof(CSharpFeaturesResources.Convert_to_file_scoped_namespace) - : nameof(CSharpFeaturesResources.Convert_to_block_scoped_namespace); + var (title, equivalenceKey) = ConvertNamespaceHelper.GetInfo( + diagnostic.Id switch + { + IDEDiagnosticIds.UseBlockScopedNamespaceDiagnosticId => NamespaceDeclarationPreference.BlockScoped, + IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId => NamespaceDeclarationPreference.FileScoped, + _ => throw ExceptionUtilities.UnexpectedValue(diagnostic.Id), + }); context.RegisterCodeFix( new MyCodeAction(title, c => FixAsync(context.Document, diagnostic, c), equivalenceKey), diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs index e5db057896599..46512db23e763 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -15,6 +16,8 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace { + using static ConvertNamespaceHelper; + [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.ConvertNamespace), Shared] internal class ConvertNamespaceCodeRefactoringProvider : CodeRefactoringProvider { @@ -41,14 +44,15 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte return; var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - var title = - ConvertNamespaceHelper.CanOfferUseBlockScoped(optionSet, namespaceDecl, forAnalyzer: false) ? CSharpFeaturesResources.Convert_to_block_scoped_namespace : - ConvertNamespaceHelper.CanOfferUseFileScoped(optionSet, root, namespaceDecl, forAnalyzer: false) ? CSharpFeaturesResources.Convert_to_file_scoped_namespace : null; - if (title == null) + var info = + CanOfferUseBlockScoped(optionSet, namespaceDecl, forAnalyzer: false) ? GetInfo(NamespaceDeclarationPreference.BlockScoped) : + CanOfferUseFileScoped(optionSet, root, namespaceDecl, forAnalyzer: false) ? GetInfo(NamespaceDeclarationPreference.FileScoped) : + ((string title, string equivalenceKey)?)null; + if (info == null) return; context.RegisterRefactoring(new MyCodeAction( - title, c => ConvertNamespaceHelper.ConvertAsync(document, namespaceDecl, c))); + info.Value.title, c => ConvertNamespaceHelper.ConvertAsync(document, namespaceDecl, c), info.Value.equivalenceKey)); } private static bool IsValidPosition(BaseNamespaceDeclarationSyntax baseDeclaration, int position) @@ -67,8 +71,8 @@ private static bool IsValidPosition(BaseNamespaceDeclarationSyntax baseDeclarati private class MyCodeAction : CodeAction.DocumentChangeAction { - public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument, title) + public MyCodeAction(string title, Func> createChangedDocument, string equivalenceKey) + : base(title, createChangedDocument, equivalenceKey) { } } diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs index e0068bb4030d8..7fd984ac947af 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs @@ -18,7 +18,15 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace { internal static class ConvertNamespaceHelper { - internal static bool CanOfferUseBlockScoped(OptionSet optionSet, BaseNamespaceDeclarationSyntax declaration, bool forAnalyzer) + public static (string title, string equivalenceKey) GetInfo(NamespaceDeclarationPreference preference) + => preference switch + { + NamespaceDeclarationPreference.BlockScoped => (CSharpFeaturesResources.Convert_to_block_scoped_namespace, nameof(CSharpFeaturesResources.Convert_to_block_scoped_namespace)), + NamespaceDeclarationPreference.FileScoped => (CSharpFeaturesResources.Convert_to_file_scoped_namespace, nameof(CSharpFeaturesResources.Convert_to_file_scoped_namespace)), + _ => throw ExceptionUtilities.UnexpectedValue(preference), + }; + + public static bool CanOfferUseBlockScoped(OptionSet optionSet, BaseNamespaceDeclarationSyntax declaration, bool forAnalyzer) { if (declaration is not FileScopedNamespaceDeclarationSyntax) return false; From 91cd1719e63c01365d973dcdd17f663513008a06 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 25 Jul 2021 17:33:43 -0700 Subject: [PATCH 38/45] Simplify --- .../ConvertNamespace/ConvertNamespaceCodeFixProvider.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs index db5b27f373354..c4748d9fc7506 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs @@ -20,6 +20,8 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace { + using static ConvertNamespaceHelper; + [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.ConvertNamespace), Shared] internal class ConvertNamespaceCodeFixProvider : SyntaxEditorBasedCodeFixProvider { @@ -37,7 +39,7 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) { var diagnostic = context.Diagnostics.First(); - var (title, equivalenceKey) = ConvertNamespaceHelper.GetInfo( + var (title, equivalenceKey) = GetInfo( diagnostic.Id switch { IDEDiagnosticIds.UseBlockScopedNamespaceDiagnosticId => NamespaceDeclarationPreference.BlockScoped, @@ -59,7 +61,7 @@ protected override Task FixAllAsync( var diagnostic = diagnostics.First(); var namespaceDecl = (BaseNamespaceDeclarationSyntax)diagnostic.Location.FindNode(cancellationToken); - var converted = ConvertNamespaceHelper.Convert(namespaceDecl); + var converted = Convert(namespaceDecl); editor.ReplaceNode(namespaceDecl, converted); return Task.CompletedTask; From 3cc0cc910ce87cc5d81811832ce0055ad7335211 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 25 Jul 2021 21:35:55 -0700 Subject: [PATCH 39/45] NRT --- ...oBlockScopedNamespaceDiagnosticAnalyzer.cs | 2 +- .../CSharpCodeGenerationService.cs | 2 +- .../CodeGeneration/NamespaceGenerator.cs | 29 +++++++------------ 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs index 4ac8d2ce6f740..51136fd8ca97b 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs @@ -57,7 +57,7 @@ private void AnalyzeNamespace(SyntaxNodeAnalysisContext context) var severity = option.Notification.Severity; var diagnosticLocation = severity.WithDefaultSeverity(DiagnosticSeverity.Hidden) != ReportDiagnostic.Hidden ? declaration.Name.GetLocation() - : tree.GetLocation(TextSpan.FromBounds(declaration.SpanStart, declaration.SemicolonToken.Span.End)); + : tree.GetLocation(TextSpan.FromBounds(declaration.SpanStart, declaration.SemicolonToken.Span.End)); return DiagnosticHelper.Create( this.Descriptor, diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpCodeGenerationService.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpCodeGenerationService.cs index 109c58a8151da..2854b52358779 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpCodeGenerationService.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpCodeGenerationService.cs @@ -646,7 +646,7 @@ public override SyntaxNode CreateNamedTypeDeclaration( public override SyntaxNode CreateNamespaceDeclaration( INamespaceSymbol @namespace, CodeGenerationDestination destination, CodeGenerationOptions options, CancellationToken cancellationToken) { - return NamespaceGenerator.GenerateNamespaceDeclaration(this, @namespace, destination, options, options.ParseOptions, cancellationToken); + return NamespaceGenerator.GenerateNamespaceDeclaration(this, @namespace, destination, options, options?.ParseOptions, cancellationToken); } private static TDeclarationNode UpdateDeclarationModifiers(TDeclarationNode declaration, Func computeNewModifiersList) diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs index 218b8047d774b..c2fdc3aec3802 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Generic; using System.Linq; @@ -33,7 +31,7 @@ public static BaseNamespaceDeclarationSyntax AddNamespaceTo( var declaration = GenerateNamespaceDeclaration( service, @namespace, CodeGenerationDestination.Namespace, - options, destination?.SyntaxTree.Options ?? options.ParseOptions, + options, destination.SyntaxTree.Options ?? options.ParseOptions, cancellationToken); if (declaration is not BaseNamespaceDeclarationSyntax namespaceDeclaration) throw new ArgumentException(CSharpWorkspaceResources.Namespace_can_not_be_added_in_this_destination); @@ -53,7 +51,7 @@ public static CompilationUnitSyntax AddNamespaceTo( var declaration = GenerateNamespaceDeclaration( service, @namespace, CodeGenerationDestination.CompilationUnit, - options, destination?.SyntaxTree.Options ?? options.ParseOptions, + options, destination.SyntaxTree.Options ?? options.ParseOptions, cancellationToken); if (declaration is not BaseNamespaceDeclarationSyntax namespaceDeclaration) throw new ArgumentException(CSharpWorkspaceResources.Namespace_can_not_be_added_in_this_destination); @@ -67,7 +65,7 @@ internal static SyntaxNode GenerateNamespaceDeclaration( INamespaceSymbol @namespace, CodeGenerationDestination destination, CodeGenerationOptions options, - ParseOptions parseOptions, + ParseOptions? parseOptions, CancellationToken cancellationToken) { options ??= CodeGenerationOptions.Default; @@ -99,7 +97,7 @@ private static SyntaxNode GenerateNamespaceDeclarationWorker( string name, INamespaceSymbol innermostNamespace, CodeGenerationDestination destination, CodeGenerationOptions options, - ParseOptions parseOptions) + ParseOptions? parseOptions) { var usings = GenerateUsingDirectives(innermostNamespace); @@ -108,8 +106,8 @@ private static SyntaxNode GenerateNamespaceDeclarationWorker( return SyntaxFactory.CompilationUnit().WithUsings(usings); if (destination == CodeGenerationDestination.CompilationUnit && - options.Options.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations).Value == NamespaceDeclarationPreference.FileScoped && - ((CSharpParseOptions)parseOptions)?.LanguageVersion >= LanguageVersion.CSharp10) + options.Options!.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations).Value == NamespaceDeclarationPreference.FileScoped && + ((CSharpParseOptions?)parseOptions)?.LanguageVersion >= LanguageVersion.CSharp10) { return SyntaxFactory.FileScopedNamespaceDeclaration(SyntaxFactory.ParseName(name)).WithUsings(usings); } @@ -123,7 +121,7 @@ private static SyntaxNode GetDeclarationSyntaxWithoutMembers( string name, CodeGenerationDestination destination, CodeGenerationOptions options, - ParseOptions parseOptions) + ParseOptions? parseOptions) { var reusableSyntax = GetReuseableSyntaxNodeForSymbol(@namespace, options); return reusableSyntax == null @@ -150,7 +148,7 @@ private static SyntaxList GenerateUsingDirectives(INamespa return usingDirectives.ToSyntaxList(); } - private static UsingDirectiveSyntax GenerateUsingDirective(ISymbol symbol) + private static UsingDirectiveSyntax? GenerateUsingDirective(ISymbol symbol) { if (symbol is IAliasSymbol alias) { @@ -176,14 +174,9 @@ private static UsingDirectiveSyntax GenerateUsingDirective(ISymbol symbol) private static NameSyntax GenerateName(INamespaceOrTypeSymbol symbol) { - if (symbol is ITypeSymbol type) - { - return type.GenerateTypeSyntax() as NameSyntax; - } - else - { - return SyntaxFactory.ParseName(symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); - } + return symbol is ITypeSymbol type + ? type.GenerateNameSyntax() + : SyntaxFactory.ParseName(symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); } } } From 403ac976f514d269db3410c443eb73b369071985 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 25 Jul 2021 21:43:35 -0700 Subject: [PATCH 40/45] tests --- .../CSharp/Portable/CodeGeneration/NamespaceGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs index c2fdc3aec3802..acda791c41932 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/NamespaceGenerator.cs @@ -106,7 +106,7 @@ private static SyntaxNode GenerateNamespaceDeclarationWorker( return SyntaxFactory.CompilationUnit().WithUsings(usings); if (destination == CodeGenerationDestination.CompilationUnit && - options.Options!.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations).Value == NamespaceDeclarationPreference.FileScoped && + options.Options?.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations).Value == NamespaceDeclarationPreference.FileScoped && ((CSharpParseOptions?)parseOptions)?.LanguageVersion >= LanguageVersion.CSharp10) { return SyntaxFactory.FileScopedNamespaceDeclaration(SyntaxFactory.ParseName(name)).WithUsings(usings); From 4411dab9771a9e94053d723d34a20d68b518d67e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 25 Jul 2021 22:40:53 -0700 Subject: [PATCH 41/45] Fix formatting --- .../ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs index 51136fd8ca97b..4ac8d2ce6f740 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs @@ -57,7 +57,7 @@ private void AnalyzeNamespace(SyntaxNodeAnalysisContext context) var severity = option.Notification.Severity; var diagnosticLocation = severity.WithDefaultSeverity(DiagnosticSeverity.Hidden) != ReportDiagnostic.Hidden ? declaration.Name.GetLocation() - : tree.GetLocation(TextSpan.FromBounds(declaration.SpanStart, declaration.SemicolonToken.Span.End)); + : tree.GetLocation(TextSpan.FromBounds(declaration.SpanStart, declaration.SemicolonToken.Span.End)); return DiagnosticHelper.Create( this.Descriptor, From 30971521c740edb18a603e7fa6bfed5d58a95e5c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 25 Jul 2021 22:46:54 -0700 Subject: [PATCH 42/45] Fix tests --- .../Core/Test/Options/CSharpEditorConfigGeneratorTests.vb | 2 ++ .../Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb index 2cf6212cbe614..1aa5c71c4a149 100644 --- a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb +++ b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb @@ -125,6 +125,7 @@ csharp_preferred_modifier_order = public,private,protected,internal,static,exter # Code-block preferences csharp_prefer_braces = true csharp_prefer_simple_using_statement = true +csharp_style_namespace_declarations = block_scoped # Expression-level preferences csharp_prefer_simple_default_expression = true @@ -355,6 +356,7 @@ csharp_preferred_modifier_order = public,private,protected,internal,static,exter # Code-block preferences csharp_prefer_braces = true csharp_prefer_simple_using_statement = true +csharp_style_namespace_declarations = block_scoped # Expression-level preferences csharp_prefer_simple_default_expression = true diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs index 23dbe9ddd82e9..6b7b171b5c7dc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs @@ -297,7 +297,7 @@ private static Option2> CreateUsingDirectiv private static Option2> CreateNamespaceDeclarationOption(string optionName, CodeStyleOption2 defaultValue, string editorconfigKeyName) => CreateOption( - CSharpCodeStyleOptionGroups.UsingDirectivePreferences, optionName, + CSharpCodeStyleOptionGroups.CodeBlockPreferences, optionName, defaultValue, storageLocations: new OptionStorageLocation2[] { new EditorConfigStorageLocation>( From fd48f8ef7cc27126e4e7f048128c9213c8214497 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 25 Jul 2021 23:34:17 -0700 Subject: [PATCH 43/45] Move to analyzer lib --- .../Analyzers/CSharpAnalyzers.projitems | 3 + .../Analyzers/CSharpAnalyzersResources.resx | 8 ++ .../ConvertNamespaceAnalysis.cs | 88 +++++++++++++++++++ ...oBlockScopedNamespaceDiagnosticAnalyzer.cs | 8 +- ...ToFileScopedNamespaceDiagnosticAnalyzer.cs | 8 +- .../xlf/CSharpAnalyzersResources.cs.xlf | 10 +++ .../xlf/CSharpAnalyzersResources.de.xlf | 10 +++ .../xlf/CSharpAnalyzersResources.es.xlf | 10 +++ .../xlf/CSharpAnalyzersResources.fr.xlf | 10 +++ .../xlf/CSharpAnalyzersResources.it.xlf | 10 +++ .../xlf/CSharpAnalyzersResources.ja.xlf | 10 +++ .../xlf/CSharpAnalyzersResources.ko.xlf | 10 +++ .../xlf/CSharpAnalyzersResources.pl.xlf | 10 +++ .../xlf/CSharpAnalyzersResources.pt-BR.xlf | 10 +++ .../xlf/CSharpAnalyzersResources.ru.xlf | 10 +++ .../xlf/CSharpAnalyzersResources.tr.xlf | 10 +++ .../xlf/CSharpAnalyzersResources.zh-Hans.xlf | 10 +++ .../xlf/CSharpAnalyzersResources.zh-Hant.xlf | 10 +++ .../CodeFixes/CSharpCodeFixes.projitems | 2 + .../ConvertNamespaceCodeFixProvider.cs | 5 +- .../ConvertNamespaceTransform.cs} | 69 ++------------- .../Portable/CSharpFeaturesResources.resx | 8 -- ...ConvertNamespaceCodeRefactoringProvider.cs | 5 +- .../xlf/CSharpFeaturesResources.cs.xlf | 10 --- .../xlf/CSharpFeaturesResources.de.xlf | 10 --- .../xlf/CSharpFeaturesResources.es.xlf | 10 --- .../xlf/CSharpFeaturesResources.fr.xlf | 10 --- .../xlf/CSharpFeaturesResources.it.xlf | 10 --- .../xlf/CSharpFeaturesResources.ja.xlf | 10 --- .../xlf/CSharpFeaturesResources.ko.xlf | 10 --- .../xlf/CSharpFeaturesResources.pl.xlf | 10 --- .../xlf/CSharpFeaturesResources.pt-BR.xlf | 10 --- .../xlf/CSharpFeaturesResources.ru.xlf | 10 --- .../xlf/CSharpFeaturesResources.tr.xlf | 10 --- .../xlf/CSharpFeaturesResources.zh-Hans.xlf | 10 --- .../xlf/CSharpFeaturesResources.zh-Hant.xlf | 10 --- 36 files changed, 254 insertions(+), 210 deletions(-) create mode 100644 src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertNamespaceAnalysis.cs rename src/{Features/CSharp/Portable => Analyzers/CSharp/Analyzers}/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs (88%) rename src/{Features/CSharp/Portable => Analyzers/CSharp/Analyzers}/ConvertNamespace/ConvertToFileScopedNamespaceDiagnosticAnalyzer.cs (88%) rename src/{Features/CSharp/Portable => Analyzers/CSharp/CodeFixes}/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs (94%) rename src/{Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs => Analyzers/CSharp/CodeFixes/ConvertNamespace/ConvertNamespaceTransform.cs} (57%) diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems index 4a14e4a023573..d9f77a57bdb2e 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems @@ -18,6 +18,9 @@ + + + diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx index 72f20bb5febc9..2584c5bab384b 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx @@ -320,4 +320,12 @@ Null check can be clarified + + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + + + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + \ No newline at end of file diff --git a/src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertNamespaceAnalysis.cs b/src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertNamespaceAnalysis.cs new file mode 100644 index 0000000000000..82a7218f49b26 --- /dev/null +++ b/src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertNamespaceAnalysis.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +#if CODE_STYLE +using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions; +#endif + +namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace +{ + internal static class ConvertNamespaceAnalysis + { + public static (string title, string equivalenceKey) GetInfo(NamespaceDeclarationPreference preference) + => preference switch + { + NamespaceDeclarationPreference.BlockScoped => (CSharpAnalyzersResources.Convert_to_block_scoped_namespace, nameof(CSharpAnalyzersResources.Convert_to_block_scoped_namespace)), + NamespaceDeclarationPreference.FileScoped => (CSharpAnalyzersResources.Convert_to_file_scoped_namespace, nameof(CSharpAnalyzersResources.Convert_to_file_scoped_namespace)), + _ => throw ExceptionUtilities.UnexpectedValue(preference), + }; + + public static bool CanOfferUseBlockScoped(OptionSet optionSet, BaseNamespaceDeclarationSyntax declaration, bool forAnalyzer) + { + if (declaration is not FileScopedNamespaceDeclarationSyntax) + return false; + + var option = optionSet.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations); + var userPrefersRegularNamespaces = option.Value == NamespaceDeclarationPreference.BlockScoped; + var analyzerDisabled = option.Notification.Severity == ReportDiagnostic.Suppress; + var forRefactoring = !forAnalyzer; + + // If the user likes regular namespaces, then we offer regular namespaces from the diagnostic analyzer. + // If the user does not like regular namespaces then we offer regular namespaces bodies from the refactoring provider. + // If the analyzer is disabled completely, the refactoring is enabled in both directions. + var canOffer = userPrefersRegularNamespaces == forAnalyzer || (forRefactoring && analyzerDisabled); + return canOffer; + } + + internal static bool CanOfferUseFileScoped(OptionSet optionSet, CompilationUnitSyntax root, BaseNamespaceDeclarationSyntax declaration, bool forAnalyzer) + { + if (declaration is not NamespaceDeclarationSyntax namespaceDeclaration) + return false; + + if (namespaceDeclaration.OpenBraceToken.IsMissing) + return false; + + if (((CSharpParseOptions)root.SyntaxTree.Options).LanguageVersion < LanguageVersion.CSharp10) + return false; + + var option = optionSet.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations); + var userPrefersFileScopedNamespaces = option.Value == NamespaceDeclarationPreference.FileScoped; + var analyzerDisabled = option.Notification.Severity == ReportDiagnostic.Suppress; + var forRefactoring = !forAnalyzer; + + // If the user likes file scoped namespaces, then we offer file scoped namespaces from the diagnostic analyzer. + // If the user does not like file scoped namespaces then we offer file scoped namespaces from the refactoring provider. + // If the analyzer is disabled completely, the refactoring is enabled in both directions. + var canOffer = userPrefersFileScopedNamespaces == forAnalyzer || (forRefactoring && analyzerDisabled); + if (!canOffer) + return false; + + // even if we could offer this here, we have to make sure it would be legal. A file scoped namespace is + // only legal if it's the only namespace in the file and there are no top level statements. + var tooManyNamespaces = root.DescendantNodesAndSelf(n => n is CompilationUnitSyntax || n is BaseNamespaceDeclarationSyntax) + .OfType() + .Take(2) + .Count() != 1; + if (tooManyNamespaces) + return false; + + if (root.Members.Any(m => m is GlobalStatementSyntax)) + return false; + + return true; + } + } +} diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs similarity index 88% rename from src/Features/CSharp/Portable/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs rename to src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs index 4ac8d2ce6f740..1d5539926ec85 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs @@ -10,6 +10,10 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; +#if CODE_STYLE +using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions; +#endif + namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace { [DiagnosticAnalyzer(LanguageNames.CSharp)] @@ -20,7 +24,7 @@ public ConvertToBlockScopedNamespaceDiagnosticAnalyzer() EnforceOnBuildValues.UseBlockScopedNamespace, CSharpCodeStyleOptions.NamespaceDeclarations, LanguageNames.CSharp, - new LocalizableResourceString(nameof(CSharpFeaturesResources.Convert_to_block_scoped_namespace), CSharpFeaturesResources.ResourceManager, typeof(CSharpFeaturesResources))) + new LocalizableResourceString(nameof(CSharpAnalyzersResources.Convert_to_block_scoped_namespace), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) { } @@ -49,7 +53,7 @@ private void AnalyzeNamespace(SyntaxNodeAnalysisContext context) var tree = declaration.SyntaxTree; var option = optionSet.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations); - if (!ConvertNamespaceHelper.CanOfferUseBlockScoped(optionSet, declaration, forAnalyzer: true)) + if (!ConvertNamespaceAnalysis.CanOfferUseBlockScoped(optionSet, declaration, forAnalyzer: true)) return null; // if the diagnostic is hidden, show it anywhere from the `namespace` keyword through the name. diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertToFileScopedNamespaceDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertToFileScopedNamespaceDiagnosticAnalyzer.cs similarity index 88% rename from src/Features/CSharp/Portable/ConvertNamespace/ConvertToFileScopedNamespaceDiagnosticAnalyzer.cs rename to src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertToFileScopedNamespaceDiagnosticAnalyzer.cs index d75024e6238b9..d5a62dd630240 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertToFileScopedNamespaceDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertToFileScopedNamespaceDiagnosticAnalyzer.cs @@ -10,6 +10,10 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; +#if CODE_STYLE +using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions; +#endif + namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace { [DiagnosticAnalyzer(LanguageNames.CSharp)] @@ -20,7 +24,7 @@ public ConvertToFileScopedNamespaceDiagnosticAnalyzer() EnforceOnBuildValues.UseFileScopedNamespace, CSharpCodeStyleOptions.NamespaceDeclarations, LanguageNames.CSharp, - new LocalizableResourceString(nameof(CSharpFeaturesResources.Convert_to_file_scoped_namespace), CSharpFeaturesResources.ResourceManager, typeof(CSharpFeaturesResources))) + new LocalizableResourceString(nameof(CSharpAnalyzersResources.Convert_to_file_scoped_namespace), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) { } @@ -51,7 +55,7 @@ private void AnalyzeNamespace(SyntaxNodeAnalysisContext context) var tree = declaration.SyntaxTree; var option = optionSet.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations); - if (!ConvertNamespaceHelper.CanOfferUseFileScoped(optionSet, root, declaration, forAnalyzer: true)) + if (!ConvertNamespaceAnalysis.CanOfferUseFileScoped(optionSet, root, declaration, forAnalyzer: true)) return null; // if the diagnostic is hidden, show it anywhere from the `namespace` keyword through the name. diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf index e4a4b3b0c87c9..86ae16c551a9c 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf @@ -27,6 +27,16 @@ Převést příkaz switch na výraz + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Deconstruct variable declaration Dekonstruovat deklaraci proměnné diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf index 3731ce7769969..531be86f50816 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf @@ -27,6 +27,16 @@ Switch-Anweisung in Ausdruck konvertieren + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Deconstruct variable declaration Variablendeklaration dekonstruieren diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf index ee3229c585db9..133ff92b10b78 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf @@ -27,6 +27,16 @@ Convertir una instrucción switch en expresión + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Deconstruct variable declaration Desconstruir la declaración de variable diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf index 2d0fae02013aa..1a4eabfabc6db 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf @@ -27,6 +27,16 @@ Convertir l'instruction switch en expression + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Deconstruct variable declaration Déconstruire la déclaration de variable diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf index db60776e3a5d8..2ee3ea80f034e 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf @@ -27,6 +27,16 @@ Converti l'istruzione switch in espressione + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Deconstruct variable declaration Decostruisci la dichiarazione di variabile diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf index 04f5a5d755680..671d548aeff02 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf @@ -27,6 +27,16 @@ switch ステートメントを式に変換します + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Deconstruct variable declaration 変数の宣言を分解 diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf index 362167157ae14..b9aa0872b8cc2 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf @@ -27,6 +27,16 @@ switch 문을 식으로 변환 + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Deconstruct variable declaration 변수 선언 분해 diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf index 746df4ba10a11..e5ba07c68088d 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf @@ -27,6 +27,16 @@ Konwertuj instrukcję switch na wyrażenie + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Deconstruct variable declaration Zdekonstruuj deklarację zmiennej diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf index 8940740c6310a..c2b165d9212a4 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf @@ -27,6 +27,16 @@ Converter a instrução switch em expressão + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Deconstruct variable declaration Desconstruir declaração de variável diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf index 01c46cb10fe8f..ca241df87489e 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf @@ -27,6 +27,16 @@ Преобразовать оператор switch в выражение + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Deconstruct variable declaration Деконструировать объявление переменной diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf index 988ca9b6f5961..7e9c5ca5bfd12 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf @@ -27,6 +27,16 @@ Switch deyimini ifadeye dönüştür + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Deconstruct variable declaration Değişken bildirimini ayrıştır diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf index 2f00f1542a2e2..3d4dc0fb5a5f8 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf @@ -27,6 +27,16 @@ 将 switch 语句转换为表达式 + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Deconstruct variable declaration 析构变量声明 diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf index 96a0ed65e0d1f..12128baa011c0 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf @@ -27,6 +27,16 @@ 將 switch 陳述式轉換為運算式 + + Convert to block scoped namespace + Convert to block scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + + + Convert to file-scoped namespace + Convert to file-scoped namespace + {Locked="namespace"} "namespace" is a C# keyword and should not be localized. + Deconstruct variable declaration 解構變數宣告 diff --git a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems index 5bfb9d4a7801f..d9a2923277073 100644 --- a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems +++ b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems @@ -16,6 +16,8 @@ + + diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs similarity index 94% rename from src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs rename to src/Analyzers/CSharp/CodeFixes/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs index c4748d9fc7506..618fa8a4edbc0 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs @@ -20,7 +20,8 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace { - using static ConvertNamespaceHelper; + using static ConvertNamespaceAnalysis; + using static ConvertNamespaceTransform; [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.ConvertNamespace), Shared] internal class ConvertNamespaceCodeFixProvider : SyntaxEditorBasedCodeFixProvider @@ -67,7 +68,7 @@ protected override Task FixAllAsync( return Task.CompletedTask; } - private class MyCodeAction : CodeAction.DocumentChangeAction + private class MyCodeAction : CustomCodeActions.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument, string equivalenceKey) : base(title, createChangedDocument, equivalenceKey) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs b/src/Analyzers/CSharp/CodeFixes/ConvertNamespace/ConvertNamespaceTransform.cs similarity index 57% rename from src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs rename to src/Analyzers/CSharp/CodeFixes/ConvertNamespace/ConvertNamespaceTransform.cs index 7fd984ac947af..7b81590b1ea10 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceHelper.cs +++ b/src/Analyzers/CSharp/CodeFixes/ConvertNamespace/ConvertNamespaceTransform.cs @@ -14,73 +14,14 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; +#if CODE_STYLE +using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions; +#endif + namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace { - internal static class ConvertNamespaceHelper + internal static class ConvertNamespaceTransform { - public static (string title, string equivalenceKey) GetInfo(NamespaceDeclarationPreference preference) - => preference switch - { - NamespaceDeclarationPreference.BlockScoped => (CSharpFeaturesResources.Convert_to_block_scoped_namespace, nameof(CSharpFeaturesResources.Convert_to_block_scoped_namespace)), - NamespaceDeclarationPreference.FileScoped => (CSharpFeaturesResources.Convert_to_file_scoped_namespace, nameof(CSharpFeaturesResources.Convert_to_file_scoped_namespace)), - _ => throw ExceptionUtilities.UnexpectedValue(preference), - }; - - public static bool CanOfferUseBlockScoped(OptionSet optionSet, BaseNamespaceDeclarationSyntax declaration, bool forAnalyzer) - { - if (declaration is not FileScopedNamespaceDeclarationSyntax) - return false; - - var option = optionSet.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations); - var userPrefersRegularNamespaces = option.Value == NamespaceDeclarationPreference.BlockScoped; - var analyzerDisabled = option.Notification.Severity == ReportDiagnostic.Suppress; - var forRefactoring = !forAnalyzer; - - // If the user likes regular namespaces, then we offer regular namespaces from the diagnostic analyzer. - // If the user does not like regular namespaces then we offer regular namespaces bodies from the refactoring provider. - // If the analyzer is disabled completely, the refactoring is enabled in both directions. - var canOffer = userPrefersRegularNamespaces == forAnalyzer || (forRefactoring && analyzerDisabled); - return canOffer; - } - - internal static bool CanOfferUseFileScoped(OptionSet optionSet, CompilationUnitSyntax root, BaseNamespaceDeclarationSyntax declaration, bool forAnalyzer) - { - if (declaration is not NamespaceDeclarationSyntax namespaceDeclaration) - return false; - - if (namespaceDeclaration.OpenBraceToken.IsMissing) - return false; - - if (((CSharpParseOptions)root.SyntaxTree.Options).LanguageVersion < LanguageVersion.CSharp10) - return false; - - var option = optionSet.GetOption(CSharpCodeStyleOptions.NamespaceDeclarations); - var userPrefersFileScopedNamespaces = option.Value == NamespaceDeclarationPreference.FileScoped; - var analyzerDisabled = option.Notification.Severity == ReportDiagnostic.Suppress; - var forRefactoring = !forAnalyzer; - - // If the user likes file scoped namespaces, then we offer file scoped namespaces from the diagnostic analyzer. - // If the user does not like file scoped namespaces then we offer file scoped namespaces from the refactoring provider. - // If the analyzer is disabled completely, the refactoring is enabled in both directions. - var canOffer = userPrefersFileScopedNamespaces == forAnalyzer || (forRefactoring && analyzerDisabled); - if (!canOffer) - return false; - - // even if we could offer this here, we have to make sure it would be legal. A file scoped namespace is - // only legal if it's the only namespace in the file and there are no top level statements. - var tooManyNamespaces = root.DescendantNodesAndSelf(n => n is CompilationUnitSyntax || n is BaseNamespaceDeclarationSyntax) - .OfType() - .Take(2) - .Count() != 1; - if (tooManyNamespaces) - return false; - - if (root.Members.Any(m => m is GlobalStatementSyntax)) - return false; - - return true; - } - public static async Task ConvertAsync( Document document, BaseNamespaceDeclarationSyntax baseNamespace, CancellationToken cancellationToken) { diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx index 6d29265190951..38cffbfe26c88 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx @@ -628,12 +628,4 @@ record struct - - Convert to file-scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - - - Convert to block scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - \ No newline at end of file diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs index 46512db23e763..d6a30423155f0 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs @@ -16,7 +16,8 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace { - using static ConvertNamespaceHelper; + using static ConvertNamespaceAnalysis; + using static ConvertNamespaceTransform; [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.ConvertNamespace), Shared] internal class ConvertNamespaceCodeRefactoringProvider : CodeRefactoringProvider @@ -52,7 +53,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte return; context.RegisterRefactoring(new MyCodeAction( - info.Value.title, c => ConvertNamespaceHelper.ConvertAsync(document, namespaceDecl, c), info.Value.equivalenceKey)); + info.Value.title, c => ConvertAsync(document, namespaceDecl, c), info.Value.equivalenceKey)); } private static bool IsValidPosition(BaseNamespaceDeclarationSyntax baseDeclaration, int position) diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf index a46682811de62..45a27fb7e9b9e 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf @@ -92,16 +92,6 @@ Porovnat s {0} - - Convert to block scoped namespace - Convert to block scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - - - Convert to file-scoped namespace - Convert to file-scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to method Převést na metodu diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf index f84d08a947283..837f34974766b 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf @@ -92,16 +92,6 @@ Mit "{0}" vergleichen - - Convert to block scoped namespace - Convert to block scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - - - Convert to file-scoped namespace - Convert to file-scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to method In Methode konvertieren diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf index f87445627502b..97bdbf70cdbca 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf @@ -92,16 +92,6 @@ Comparar con "{0}" - - Convert to block scoped namespace - Convert to block scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - - - Convert to file-scoped namespace - Convert to file-scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to method Convertir al método diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf index 4eaa2c930bd0a..bf6ca47d26a47 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf @@ -92,16 +92,6 @@ Comparer à '{0}' - - Convert to block scoped namespace - Convert to block scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - - - Convert to file-scoped namespace - Convert to file-scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to method Convertir en méthode diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf index 2c674cb495c0d..0447635cd07e5 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf @@ -92,16 +92,6 @@ Confronta con '{0}' - - Convert to block scoped namespace - Convert to block scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - - - Convert to file-scoped namespace - Convert to file-scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to method Converti in metodo diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf index 318cd40b4f73f..8717f718be560 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf @@ -92,16 +92,6 @@ "{0}" と比較 - - Convert to block scoped namespace - Convert to block scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - - - Convert to file-scoped namespace - Convert to file-scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to method メソッドに変換 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf index d92db785de1cd..8e0702f33c52a 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf @@ -92,16 +92,6 @@ '{0}'과(와) 비교 - - Convert to block scoped namespace - Convert to block scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - - - Convert to file-scoped namespace - Convert to file-scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to method 메서드로 변환 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf index 665eacd5cf0e7..930c28f5d4e9d 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf @@ -92,16 +92,6 @@ Porównaj z „{0}” - - Convert to block scoped namespace - Convert to block scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - - - Convert to file-scoped namespace - Convert to file-scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to method Konwertuj na metodę diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf index 5dfaf26cb5413..5a1b3b7449225 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf @@ -92,16 +92,6 @@ Comparar a '{0}' - - Convert to block scoped namespace - Convert to block scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - - - Convert to file-scoped namespace - Convert to file-scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to method Converter em método diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf index 2097ab2d59449..a003e9df7cb61 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf @@ -92,16 +92,6 @@ Сравнить с "{0}" - - Convert to block scoped namespace - Convert to block scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - - - Convert to file-scoped namespace - Convert to file-scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to method Преобразовать в метод diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf index 27056008aea38..5ab4cd6e4f8b2 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf @@ -92,16 +92,6 @@ '{0}' ile karşılaştır - - Convert to block scoped namespace - Convert to block scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - - - Convert to file-scoped namespace - Convert to file-scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to method Yönteme dönüştür diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf index 7ad9566d983b4..11ca99c89a76e 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf @@ -92,16 +92,6 @@ 与 "{0}" 比较 - - Convert to block scoped namespace - Convert to block scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - - - Convert to file-scoped namespace - Convert to file-scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to method 转换为方法 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf index 5f327745bdd28..3333bd8b7eed5 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf @@ -92,16 +92,6 @@ 與 '{0}' 比較 - - Convert to block scoped namespace - Convert to block scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - - - Convert to file-scoped namespace - Convert to file-scoped namespace - {Locked="namespace"} "namespace" is a C# keyword and should not be localized. - Convert to method 轉換為方法 From 45e7a9082207aca58117cb062ee1d05c93ec4e59 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 25 Jul 2021 23:44:24 -0700 Subject: [PATCH 44/45] Update automation object --- .../Impl/Options/AutomationObject/AutomationObject.Style.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Style.cs b/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Style.cs index def7d40b58687..9428e2814886a 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Style.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Style.cs @@ -398,5 +398,11 @@ public string Style_AllowBlankLineAfterColonInConstructorInitializer get { return GetXmlOption(CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer); } set { SetXmlOption(CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer, value); } } + + public string Style_NamespaceDeclarations + { + get { return GetXmlOption(CSharpCodeStyleOptions.NamespaceDeclarations); } + set { SetXmlOption(CSharpCodeStyleOptions.NamespaceDeclarations, value); } + } } } From 42261eb7f06ad00f91286923e998b92f4bf1e382 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sun, 25 Jul 2021 23:45:14 -0700 Subject: [PATCH 45/45] Update src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems Co-authored-by: Youssef Victor --- src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems index d9f77a57bdb2e..e9940207cf13d 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems @@ -18,7 +18,7 @@ - + @@ -119,4 +119,4 @@ - \ No newline at end of file +