From 246cbebaa5d34637a49ff3f16fc10213bb83602b Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 13 Apr 2024 15:05:49 +0300 Subject: [PATCH 01/15] Add 'Make anonymous function static' feature --- .../Analyzers/CSharpAnalyzers.projitems | 1 + .../Analyzers/CSharpAnalyzersResources.resx | 6 + .../CSharpAnalyzerOptionsProvider.cs | 1 + ...onymousFunctionStaticDiagnosticAnalyzer.cs | 71 ++++ .../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 | 1 + ...eAnonymousFunctionStaticCodeFixProvider.cs | 48 +++ .../Tests/CSharpAnalyzers.UnitTests.projitems | 1 + .../MakeAnonymousFunctionStaticTests.cs | 321 ++++++++++++++++++ .../Core/Analyzers/EnforceOnBuildValues.cs | 1 + .../Core/Analyzers/IDEDiagnosticIds.cs | 2 + .../PredefinedCodeFixProviderNames.cs | 1 + src/Compilers/Test/Core/Traits/Traits.cs | 1 + .../Impl/Options/Formatting/StyleViewModel.cs | 18 + .../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 +- .../CodeStyle/CSharpIdeCodeStyleOptions.cs | 2 +- 42 files changed, 677 insertions(+), 2 deletions(-) create mode 100644 src/Analyzers/CSharp/Analyzers/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticDiagnosticAnalyzer.cs create mode 100644 src/Analyzers/CSharp/CodeFixes/MakeAnonymousFunctionStatic/CSharpMakeAnonymousFunctionStaticCodeFixProvider.cs create mode 100644 src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems index c17c11daeeec8..985079e04d290 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems @@ -29,6 +29,7 @@ + diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx index a0493c14e1860..fe85b481cfb79 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx @@ -412,4 +412,10 @@ Use primary constructor + + Anonymous function can be made static + + + Make anonymous function static + \ No newline at end of file diff --git a/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs b/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs index a5d8035dd759d..f10a26fc8ad69 100644 --- a/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs +++ b/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs @@ -92,6 +92,7 @@ internal CSharpCodeGenerationOptions GetCodeGenerationOptions() public CodeStyleOption2 PreferReadOnlyStruct => GetOption(CSharpCodeStyleOptions.PreferReadOnlyStruct, FallbackCodeStyleOptions.PreferReadOnlyStruct); public CodeStyleOption2 PreferReadOnlyStructMember => GetOption(CSharpCodeStyleOptions.PreferReadOnlyStructMember, FallbackCodeStyleOptions.PreferReadOnlyStructMember); public CodeStyleOption2 PreferStaticLocalFunction => GetOption(CSharpCodeStyleOptions.PreferStaticLocalFunction, FallbackCodeStyleOptions.PreferStaticLocalFunction); + public CodeStyleOption2 PreferStaticAnonymousFunction => GetOption(CSharpCodeStyleOptions.PreferStaticAnonymousFunction, FallbackCodeStyleOptions.PreferStaticAnonymousFunction); private TValue GetOption(Option2 option, TValue defaultValue) => _options.GetOption(option, defaultValue); diff --git a/src/Analyzers/CSharp/Analyzers/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..737781ec44eeb --- /dev/null +++ b/src/Analyzers/CSharp/Analyzers/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticDiagnosticAnalyzer.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.Collections.Immutable; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.MakeAnonymousFunctionStatic; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +internal sealed class MakeAnonymousFunctionStaticDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer +{ + public MakeAnonymousFunctionStaticDiagnosticAnalyzer() + : base(IDEDiagnosticIds.MakeAnonymousFunctionStaticDiagnosticId, + EnforceOnBuildValues.MakeAnonymousFunctionStatic, + CSharpCodeStyleOptions.PreferStaticAnonymousFunction, + new LocalizableResourceString(nameof(CSharpAnalyzersResources.Make_anonymous_function_static), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)), + new LocalizableResourceString(nameof(CSharpAnalyzersResources.Anonymous_function_can_be_made_static), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) + { + } + + public override DiagnosticAnalyzerCategory GetAnalyzerCategory() + => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; + + protected override void InitializeWorker(AnalysisContext context) + { + context.RegisterCompilationStartAction(context => + { + if (context.Compilation.LanguageVersion() < LanguageVersion.CSharp9) + return; + + context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.SimpleLambdaExpression, SyntaxKind.ParenthesizedLambdaExpression, SyntaxKind.AnonymousMethodExpression); + }); + } + + private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + var option = context.GetCSharpAnalyzerOptions().PreferStaticAnonymousFunction; + if (!option.Value || ShouldSkipAnalysis(context, option.Notification)) + return; + + var anonymousFunction = (AnonymousFunctionExpressionSyntax)context.Node; + if (anonymousFunction.Modifiers.Any(SyntaxKind.StaticKeyword)) + return; + + if (TryGetCaptures(anonymousFunction, context.SemanticModel, out var captures) && captures.IsEmpty) + { + context.ReportDiagnostic( + Diagnostic.Create( + Descriptor, + anonymousFunction.GetLocation())); + } + } + + private static bool TryGetCaptures(AnonymousFunctionExpressionSyntax anonymousFunction, SemanticModel semanticModel, out ImmutableArray captures) + { + var dataFlow = semanticModel.AnalyzeDataFlow(anonymousFunction); + if (dataFlow is null) + { + captures = default; + return false; + } + + captures = dataFlow.Captured; + return dataFlow.Succeeded; + } +} diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf index b20cc6f8ba857..48421f1d1df92 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf @@ -12,6 +12,11 @@ Přidat složené závorky do příkazu {0}. + + Anonymous function can be made static + Anonymous function can be made static + + Blank line not allowed after arrow expression clause token Za tokenem klauzule výrazu šipky není povolen prázdný řádek. @@ -92,6 +97,11 @@ Výraz lambda je možné odebrat + + Make anonymous function static + Make anonymous function static + + Make member 'readonly' Nastavit člena jako readonly diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf index 5af1d97c90635..2172b9847a4a9 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf @@ -12,6 +12,11 @@ Der Anweisung "{0}" geschweifte Klammern hinzufügen + + Anonymous function can be made static + Anonymous function can be made static + + Blank line not allowed after arrow expression clause token Nach dem Token der Pfeilausdrucksklausel ist keine leere Zeile zulässig @@ -92,6 +97,11 @@ Lambdaausdruck kann entfernt werden + + Make anonymous function static + Make anonymous function static + + Make member 'readonly' Member als "readonly" festlegen diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf index 10d8ff8bbc94a..cd2bfeec89e08 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf @@ -12,6 +12,11 @@ Agregar llaves a la instrucción '{0}'. + + Anonymous function can be made static + Anonymous function can be made static + + Blank line not allowed after arrow expression clause token No se permite una línea en blanco después del token de la cláusula de expresión de flecha @@ -92,6 +97,11 @@ Se puede quitar la expresión lambda + + Make anonymous function static + Make anonymous function static + + Make member 'readonly' Convertir el miembro en 'readonly' diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf index 240b308d2df21..e979f3c4e5b5a 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf @@ -12,6 +12,11 @@ Ajouter des accolades à l'instruction '{0}'. + + Anonymous function can be made static + Anonymous function can be made static + + Blank line not allowed after arrow expression clause token Ligne vide non autorisée après le jeton de clause d’expression fléchée @@ -92,6 +97,11 @@ L’expression lambda peut être supprimée. + + Make anonymous function static + Make anonymous function static + + Make member 'readonly' Définir comme membre 'readonly' diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf index 77f9052b7fd2a..5e635c12f814a 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf @@ -12,6 +12,11 @@ Aggiunge le parentesi graffe all'istruzione '{0}'. + + Anonymous function can be made static + Anonymous function can be made static + + Blank line not allowed after arrow expression clause token Riga vuota non consentita dopo il token della clausola dell'espressione arrow @@ -92,6 +97,11 @@ L'espressione lambda può essere rimossa + + Make anonymous function static + Make anonymous function static + + Make member 'readonly' Rendi il membro 'readonly' diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf index 11fad2f8d6078..9993870f89bfc 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf @@ -12,6 +12,11 @@ '{0}' ステートメントに波かっこを追加します。 + + Anonymous function can be made static + Anonymous function can be made static + + Blank line not allowed after arrow expression clause token 矢印式句トークンの後に空白行は許可されていません @@ -92,6 +97,11 @@ ラムダ式を削除できます + + Make anonymous function static + Make anonymous function static + + Make member 'readonly' メンバーを 'readonly' にする diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf index b46c592342a82..c484dbf881fe0 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf @@ -12,6 +12,11 @@ '{0}' 문에 중괄호를 추가합니다. + + Anonymous function can be made static + Anonymous function can be made static + + Blank line not allowed after arrow expression clause token 화살표 식 절 토큰 뒤에는 빈 줄을 사용할 수 없습니다. @@ -92,6 +97,11 @@ 람다 식을 제거할 수 있습니다. + + Make anonymous function static + Make anonymous function static + + Make member 'readonly' 멤버를 'readonly'로 만들기 diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf index 80aa29890c432..08f251bf46a7e 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf @@ -12,6 +12,11 @@ Dodaj nawiasy klamrowe do instrukcji „{0}”. + + Anonymous function can be made static + Anonymous function can be made static + + Blank line not allowed after arrow expression clause token Pusty wiersz jest niedozwolony po tokenie klauzuli wyrażenia strzałki @@ -92,6 +97,11 @@ Wyrażenie lambda można usunąć + + Make anonymous function static + Make anonymous function static + + Make member 'readonly' Ustaw element członkowski jako „readonly” diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf index 2299d1557e947..ab1e47b022881 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf @@ -12,6 +12,11 @@ Adicionar chaves à instrução '{0}'. + + Anonymous function can be made static + Anonymous function can be made static + + Blank line not allowed after arrow expression clause token Linha em branco não permitida após token de cláusula de expressão de seta @@ -92,6 +97,11 @@ A expressão lambda pode ser removida + + Make anonymous function static + Make anonymous function static + + Make member 'readonly' Tornar membro 'readonly' diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf index 102c9ed8b71a9..b6cf2a799b38c 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf @@ -12,6 +12,11 @@ Добавить фигурные скобки в оператор "{0}". + + Anonymous function can be made static + Anonymous function can be made static + + Blank line not allowed after arrow expression clause token Пустая строка недопустима после маркера предложения выражения со стрелкой @@ -92,6 +97,11 @@ Лямбда-выражение можно удалить + + Make anonymous function static + Make anonymous function static + + Make member 'readonly' Сделать элемент "readonly" diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf index 1d49d918214ce..2d59415e93203 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf @@ -12,6 +12,11 @@ '{0}' deyimine küme ayracı ekleyin. + + Anonymous function can be made static + Anonymous function can be made static + + Blank line not allowed after arrow expression clause token Ok ifadesi yan tümce belirtecinin ardından boş satıra izin verilmez @@ -92,6 +97,11 @@ Lambda ifadesi kaldırılabilir + + Make anonymous function static + Make anonymous function static + + Make member 'readonly' Üyeyi 'readonly' yap diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf index fb14afb9f4e93..62c96af13302f 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf @@ -12,6 +12,11 @@ 向 “{0}” 语句添加大括号。 + + Anonymous function can be made static + Anonymous function can be made static + + Blank line not allowed after arrow expression clause token 箭头表达式子句标记后不允许空行 @@ -92,6 +97,11 @@ 可以删除 Lambda 表达式 + + Make anonymous function static + Make anonymous function static + + Make member 'readonly' 将成员设为“readonly” diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf index 2460761cea7a4..c4f8b46f0e909 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf @@ -12,6 +12,11 @@ 為 '{0}' 陳述式加入大括號。 + + Anonymous function can be made static + Anonymous function can be made static + + Blank line not allowed after arrow expression clause token 在箭頭運算式子句權杖後不允許空白行 @@ -92,6 +97,11 @@ 可移除 Lambda 運算式 + + Make anonymous function static + Make anonymous function static + + Make member 'readonly' 讓成員 'readonly' diff --git a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems index b77968340c102..e8042dedf9e79 100644 --- a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems +++ b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems @@ -50,6 +50,7 @@ + diff --git a/src/Analyzers/CSharp/CodeFixes/MakeAnonymousFunctionStatic/CSharpMakeAnonymousFunctionStaticCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/MakeAnonymousFunctionStatic/CSharpMakeAnonymousFunctionStaticCodeFixProvider.cs new file mode 100644 index 0000000000000..4e592a23915ed --- /dev/null +++ b/src/Analyzers/CSharp/CodeFixes/MakeAnonymousFunctionStatic/CSharpMakeAnonymousFunctionStaticCodeFixProvider.cs @@ -0,0 +1,48 @@ +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.CSharp.MakeAnonymousFunctionStatic; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.MakeAnonymousFunctionStatic), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpMakeAnonymousFunctionStaticCodeFixProvider() : SyntaxEditorBasedCodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds { get; } = + [IDEDiagnosticIds.MakeAnonymousFunctionStaticDiagnosticId]; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + RegisterCodeFix(context, CSharpAnalyzersResources.Make_anonymous_function_static, nameof(CSharpAnalyzersResources.Make_anonymous_function_static)); + return Task.CompletedTask; + } + + protected override Task FixAllAsync(Document document, ImmutableArray diagnostics, SyntaxEditor editor, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) + { + var generator = editor.Generator; + + foreach (var diagnostic in diagnostics) + { + var anonymousFunction = diagnostic.Location.FindNode(getInnermostNodeForTie: true, cancellationToken); + var modifiers = generator.GetModifiers(anonymousFunction); + + var fixedAnonymousFunction = generator.WithModifiers(anonymousFunction, modifiers.WithIsStatic(true)); + editor.ReplaceNode(anonymousFunction, fixedAnonymousFunction); + } + + return Task.CompletedTask; + } +} diff --git a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems index ddd1745e0f5b3..1ce11ff7471ba 100644 --- a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems +++ b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems @@ -35,6 +35,7 @@ + diff --git a/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs b/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs new file mode 100644 index 0000000000000..0c858b35baadc --- /dev/null +++ b/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs @@ -0,0 +1,321 @@ +// 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.CSharp; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.MakeAnonymousFunctionStatic; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.MakeAnonymousFunctionStatic; + +using VerifyCS = CSharpCodeFixVerifier; + +[Trait(Traits.Feature, Traits.Features.CodeActionsMakeAnonymousFunctionStatic)] +public class MakeAnonymousFunctionStaticTests +{ + private static async Task TestWithCSharp9Async(string code, string fixedCode) + { + await new VerifyCS.Test + { + TestCode = code, + FixedCode = fixedCode, + LanguageVersion = LanguageVersion.CSharp9 + }.RunAsync(); + } + + [Theory] + [InlineData("i => { }")] + [InlineData("(i) => { }")] + [InlineData("delegate (int i) { }")] + public async Task TestBelowCSharp9(string anonymousFunctionSyntax) + { + var code = $$""" + using System; + + class C + { + void M() + { + N({{anonymousFunctionSyntax}}); + } + + void N(Action a) + { + } + } + """; + + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory] + [InlineData("i => { }")] + [InlineData("(i) => { }")] + [InlineData("delegate (int i) { }")] + public async Task TestWithOptionOff(string anonymousFunctionSyntax) + { + var code = $$""" + using System; + + class C + { + void M() + { + N({{anonymousFunctionSyntax}}); + } + + void N(Action a) + { + } + } + """; + + await new VerifyCS.Test + { + TestCode = code, + FixedCode = code, + LanguageVersion = LanguageVersion.CSharp9, + Options = + { + { CSharpCodeStyleOptions.PreferStaticAnonymousFunction, false } + } + }.RunAsync(); + } + + [Theory] + [InlineData("i => { }")] + [InlineData("(i) => { }")] + [InlineData("delegate (int i) { }")] + public async Task TestMissingWhenAlreadyStatic(string anonymousFunctionSyntax) + { + var code = $$""" + using System; + + class C + { + void M() + { + N(static {{anonymousFunctionSyntax}}); + } + + void N(Action a) + { + } + } + """; + + await TestWithCSharp9Async(code, code); + } + + [Theory] + [InlineData("i => { }")] + [InlineData("(i) => { }")] + [InlineData("delegate (int i) { }")] + public async Task TestNoCaptures(string anonymousFunctionSyntax) + { + var code = $$""" + using System; + + class C + { + void M() + { + N({|IDE0310:{{anonymousFunctionSyntax}}|}); + } + + void N(Action a) + { + } + } + """; + + var fixedCode = $$""" + using System; + + class C + { + void M() + { + N(static {{anonymousFunctionSyntax}}); + } + + void N(Action a) + { + } + } + """; + + await TestWithCSharp9Async(code, fixedCode); + } + + [Theory] + [InlineData("i => _field")] + [InlineData("(i) => _field")] + [InlineData("delegate (int i) { return _field; }")] + public async Task TestCapturesThis(string anonymousFunctionSyntax) + { + var code = $$""" + using System; + + class C + { + private int _field; + + void M() + { + N({{anonymousFunctionSyntax}}); + } + + void N(Func f) + { + } + } + """; + + await TestWithCSharp9Async(code, code); + } + + [Theory] + [InlineData("i => x")] + [InlineData("(i) => x")] + [InlineData("delegate (int i) { return x; }")] + public async Task TestCapturesParameter(string anonymousFunctionSyntax) + { + var code = $$""" + using System; + + class C + { + void M(int x) + { + N({{anonymousFunctionSyntax}}); + } + + void N(Func f) + { + } + } + """; + + await TestWithCSharp9Async(code, code); + } + + [Theory] + [InlineData("i => i")] + [InlineData("(i) => i")] + [InlineData("delegate (int i) { return i; }")] + public async Task TestNoCaptures_SameFunctionParameterNameAsOuterParameterName(string anonymousFunctionSyntax) + { + var code = $$""" + using System; + + class C + { + void M(int i) + { + N({|IDE0310:{{anonymousFunctionSyntax}}|}); + } + + void N(Func f) + { + } + } + """; + + var fixedCode = $$""" + using System; + + class C + { + void M(int i) + { + N(static {{anonymousFunctionSyntax}}); + } + + void N(Func f) + { + } + } + """; + + await TestWithCSharp9Async(code, fixedCode); + } + + [Theory] + [InlineData("i => x")] + [InlineData("(i) => x")] + [InlineData("delegate (int i) { return x; }")] + public async Task TestCapturesLocal(string anonymousFunctionSyntax) + { + var code = $$""" + using System; + + class C + { + void M() + { + int x = 0; + N({{anonymousFunctionSyntax}}); + } + + void N(Func f) + { + } + } + """; + + await TestWithCSharp9Async(code, code); + } + + [Theory] + [InlineData("i => i")] + [InlineData("(i) => i")] + [InlineData("delegate (int i) { return i; }")] + public async Task TestNoCaptures_SameFunctionParameterNameAsOuterLocalName(string anonymousFunctionSyntax) + { + var code = $$""" + using System; + + class C + { + void M() + { + int i = 0; + N({|IDE0310:{{anonymousFunctionSyntax}}|}); + } + + void N(Func f) + { + } + } + """; + + var fixedCode = $$""" + using System; + + class C + { + void M() + { + int i = 0; + N(static {{anonymousFunctionSyntax}}); + } + + void N(Func f) + { + } + } + """; + + await TestWithCSharp9Async(code, fixedCode); + } +} diff --git a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs index 65cd9ad9ee9e1..1373929bacae6 100644 --- a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs +++ b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs @@ -96,6 +96,7 @@ internal static class EnforceOnBuildValues public const EnforceOnBuild UseCollectionExpressionForCreate = /*IDE0303*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseCollectionExpressionForBuilder = /*IDE0304*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseCollectionExpressionForFluent = /*IDE0305*/ EnforceOnBuild.Recommended; + public const EnforceOnBuild MakeAnonymousFunctionStatic = /*IDE0310*/ EnforceOnBuild.Recommended; /* EnforceOnBuild.WhenExplicitlyEnabled */ public const EnforceOnBuild RemoveUnnecessaryCast = /*IDE0004*/ EnforceOnBuild.WhenExplicitlyEnabled; // TODO: Move to 'Recommended' OR 'HighlyRecommended' bucket once performance problems are addressed: https://github.com/dotnet/roslyn/issues/43304 diff --git a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs index fefb859cdeb09..6c4aec2037b4e 100644 --- a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs +++ b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs @@ -199,6 +199,8 @@ internal static class IDEDiagnosticIds public const string UseCollectionExpressionForBuilderDiagnosticId = "IDE0304"; public const string UseCollectionExpressionForFluentDiagnosticId = "IDE0305"; + public const string MakeAnonymousFunctionStaticDiagnosticId = "IDE0310"; + // Analyzer error Ids public const string AnalyzerChangedId = "IDE1001"; public const string AnalyzerDependencyConflictId = "IDE1002"; diff --git a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs index b9b3280c25209..bb030e6eb15c5 100644 --- a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs +++ b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs @@ -73,6 +73,7 @@ internal static class PredefinedCodeFixProviderNames public const string JsonDetection = nameof(JsonDetection); public const string MakeFieldReadonly = nameof(MakeFieldReadonly); public const string MakeLocalFunctionStatic = nameof(MakeLocalFunctionStatic); + public const string MakeAnonymousFunctionStatic = nameof(MakeAnonymousFunctionStatic); public const string MakeMemberRequired = nameof(MakeMemberRequired); public const string MakeMemberStatic = nameof(MakeMemberStatic); public const string MakeMethodSynchronous = nameof(MakeMethodSynchronous); diff --git a/src/Compilers/Test/Core/Traits/Traits.cs b/src/Compilers/Test/Core/Traits/Traits.cs index d29b1722e8107..f08f2fdc25b5d 100644 --- a/src/Compilers/Test/Core/Traits/Traits.cs +++ b/src/Compilers/Test/Core/Traits/Traits.cs @@ -125,6 +125,7 @@ public static class Features public const string CodeActionsMakeTypePartial = "CodeActions.MakeTypePartial"; public const string CodeActionsMakeFieldReadonly = "CodeActions.MakeFieldReadonly"; public const string CodeActionsMakeLocalFunctionStatic = "CodeActions.MakeLocalFunctionStatic"; + public const string CodeActionsMakeAnonymousFunctionStatic = "CodeActions.MakeAnonymousFunctionStatic"; public const string CodeActionsMakeMemberRequired = "CodeActions.MakeMemberRequired"; public const string CodeActionsMakeMethodAsynchronous = "CodeActions.MakeMethodAsynchronous"; public const string CodeActionsMakeMethodSynchronous = "CodeActions.MakeMethodSynchronous"; diff --git a/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs b/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs index fc8fe2951f882..ae782870da465 100644 --- a/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs +++ b/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs @@ -1548,6 +1548,23 @@ int fibonacci(int n) }} "; + private static readonly string s_preferStaticAnonymousFunction = $$""" + using System; + + //[ + // {{ServicesVSResources.Prefer_colon}} + Func f1 = static i => i * i; + Func f2 = static (i, j) => i * j; + Func f3 = static delegate (int i) { return i * i; }; + //] + //[ + // {{ServicesVSResources.Over_colon}} + Func f1 = i => i * i; + Func f2 = (i, j) => i * j; + Func f3 = delegate (int i) { return i * i; }; + //] + """; + #endregion #region New Line Preferences @@ -2292,6 +2309,7 @@ internal StyleViewModel(OptionStore optionStore, IServiceProvider serviceProvide CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CodeStyleOptions2.PreferReadonly, ServicesVSResources.Prefer_readonly_fields, s_preferReadonly, s_preferReadonly, this, optionStore, modifierGroupTitle)); CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferReadOnlyStruct, ServicesVSResources.Prefer_read_only_struct, s_preferReadOnlyStruct, s_preferReadOnlyStruct, this, optionStore, modifierGroupTitle)); CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferStaticLocalFunction, ServicesVSResources.Prefer_static_local_functions, s_preferStaticLocalFunction, s_preferStaticLocalFunction, this, optionStore, modifierGroupTitle)); + CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CSharpCodeStyleOptions.PreferStaticAnonymousFunction, ServicesVSResources.Prefer_static_anonymous_functions, s_preferStaticAnonymousFunction, s_preferStaticAnonymousFunction, this, optionStore, modifierGroupTitle)); // Parameter preferences AddParameterOptions(optionStore, parameterPreferencesGroupTitle); diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx index 0bef437805163..374efed02fbb5 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.resx +++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx @@ -1936,4 +1936,7 @@ Additional information: {1} Semantic Search ({0}) + + Prefer static anonymous functions + \ 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 f424498555d9c..870eb7f11ed19 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf @@ -1112,6 +1112,11 @@ Preferovat zjednodušenou interpolaci + + Prefer static anonymous functions + Prefer static anonymous functions + + Prefer static local functions Preferovat statické místní funkce diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf index 19f0cb047d13c..1e025b0a8dd62 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf @@ -1112,6 +1112,11 @@ Vereinfachte Interpolation bevorzugen + + Prefer static anonymous functions + Prefer static anonymous functions + + Prefer static local functions Statische lokale Funktionen bevorzugen diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf index da4d5b345dd4f..01c6dad649d78 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf @@ -1112,6 +1112,11 @@ Preferir interpolación simplificada + + Prefer static anonymous functions + Prefer static anonymous functions + + Prefer static local functions Preferir funciones locales estáticas diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf index ef6cee9527c67..6ccd478502a98 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf @@ -1112,6 +1112,11 @@ Préférer une interpolation simplifiée + + Prefer static anonymous functions + Prefer static anonymous functions + + Prefer static local functions Préférer les fonctions locales statiques diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf index b51d4e69d9e0b..e20fa8c9d0a67 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf @@ -1112,6 +1112,11 @@ Preferire l'interpolazione semplificata + + Prefer static anonymous functions + Prefer static anonymous functions + + Prefer static local functions Preferisci funzioni locali statiche diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf index 0b3a697dedd8f..a2906f99ae57e 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf @@ -1112,6 +1112,11 @@ シンプルな補間を優先する + + Prefer static anonymous functions + Prefer static anonymous functions + + Prefer static local functions 静的ローカル関数を優先する diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf index 7b0c7df05331a..98c46661fa844 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf @@ -1112,6 +1112,11 @@ 단순화된 보간 선호 + + Prefer static anonymous functions + Prefer static anonymous functions + + Prefer static local functions 정적 로컬 함수 선호 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf index 2422d9e9c4db7..eff47657ecf77 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf @@ -1112,6 +1112,11 @@ Preferuj uproszczoną interpolację + + Prefer static anonymous functions + Prefer static anonymous functions + + Prefer static local functions Preferuj statyczne funkcje lokalne diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf index b430acb6694bc..dfa6a6fd905c5 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf @@ -1112,6 +1112,11 @@ Prefira interpolação simplificada + + Prefer static anonymous functions + Prefer static anonymous functions + + Prefer static local functions Preferir as funções locais estáticas diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf index a1797952e12fc..cd9eb81db03a2 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf @@ -1112,6 +1112,11 @@ Предпочитать упрощенную интерполяцию + + Prefer static anonymous functions + Prefer static anonymous functions + + Prefer static local functions Предпочитать статические локальные функции diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf index 8f20a68b057a6..e56267e6af81e 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf @@ -1112,6 +1112,11 @@ Temel ilişkilendirmeyi tercih et + + Prefer static anonymous functions + Prefer static anonymous functions + + Prefer static local functions Statik yerel işlevleri 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 33b698875b618..4125ad72481bd 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf @@ -1112,6 +1112,11 @@ 首选简化内插 + + Prefer static anonymous functions + Prefer static anonymous functions + + Prefer static local functions 首选静态本地函数 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf index 912b5db1a36cc..2abaa2a348650 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf @@ -1112,6 +1112,11 @@ 優先使用簡易動畫插補 + + Prefer static anonymous functions + Prefer static anonymous functions + + Prefer static local functions 優先使用靜態區域函式 diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs index 04b4aebcf373b..a5b4438a9d83a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.CodeStyle; @@ -155,6 +154,11 @@ private static Option2> CreatePreferE "csharp_prefer_static_local_function", CSharpIdeCodeStyleOptions.Default.PreferStaticLocalFunction); + public static readonly Option2> PreferStaticAnonymousFunction = CreateOption( + CodeStyleOptionGroups.Modifier, + "csharp_prefer_static_anonymous_function", + CSharpIdeCodeStyleOptions.Default.PreferStaticAnonymousFunction); + public static readonly Option2> PreferReadOnlyStruct = CreateOption( CodeStyleOptionGroups.Modifier, "csharp_style_prefer_readonly_struct", diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpIdeCodeStyleOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpIdeCodeStyleOptions.cs index 7e0b6e0ade8ab..3dab88bb89ec6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpIdeCodeStyleOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpIdeCodeStyleOptions.cs @@ -6,7 +6,6 @@ using System.Collections.Immutable; using System.Linq; using System.Runtime.Serialization; -using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Options; @@ -80,6 +79,7 @@ internal sealed record class CSharpIdeCodeStyleOptions : IdeCodeStyleOptions, IE [DataMember] public CodeStyleOption2 PreferReadOnlyStruct { get; init; } = CodeStyleOption2.TrueWithSuggestionEnforcement; [DataMember] public CodeStyleOption2 PreferReadOnlyStructMember { get; init; } = CodeStyleOption2.TrueWithSuggestionEnforcement; [DataMember] public CodeStyleOption2 PreferStaticLocalFunction { get; init; } = CodeStyleOption2.TrueWithSuggestionEnforcement; + [DataMember] public CodeStyleOption2 PreferStaticAnonymousFunction { get; init; } = CodeStyleOption2.TrueWithSuggestionEnforcement; [DataMember] public CodeStyleOption2 PreferExpressionBodiedLambdas { get; init; } = s_whenPossibleWithSilentEnforcement; [DataMember] public CodeStyleOption2 PreferPrimaryConstructors { get; init; } = CodeStyleOption2.TrueWithSuggestionEnforcement; From 2d31ad5250c0e340c69fff8bb6519036cc113b0f Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 13 Apr 2024 16:50:23 +0300 Subject: [PATCH 02/15] Fix editorconfig tests --- .../Core/Test/Options/CSharpEditorConfigGeneratorTests.vb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb index e37a598ab234b..aa0f91e6726dd 100644 --- a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb +++ b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb @@ -4,7 +4,6 @@ Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.CodeStyle -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Options.EditorConfig Imports Microsoft.CodeAnalysis.Test.Utilities @@ -124,6 +123,7 @@ csharp_style_prefer_switch_expression = true csharp_style_conditional_delegate_call = true # Modifier preferences +csharp_prefer_static_anonymous_function = true csharp_prefer_static_local_function = true csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async csharp_style_prefer_readonly_struct = true @@ -371,6 +371,7 @@ csharp_style_prefer_switch_expression = true csharp_style_conditional_delegate_call = true # Modifier preferences +csharp_prefer_static_anonymous_function = true csharp_prefer_static_local_function = true csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async csharp_style_prefer_readonly_struct = true From da06c48d664bcfa5514caa9cd41b657a005a1894 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 13 Apr 2024 16:53:40 +0300 Subject: [PATCH 03/15] Fix and refactor code cleanup test --- .../CSharpTest/Formatting/CodeCleanupTests.cs | 23 ++++--------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs index ed4f45d17d1b9..03a2dfe0fdc1a 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs @@ -16,24 +16,17 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.UseExpressionBody; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics.CSharp; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Editor.UnitTests; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.CodeAnalysis.Simplification; -using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UnitTests.Diagnostics; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Formatting @@ -633,9 +626,9 @@ private void Method() } [Theory] - [InlineData(LanguageNames.CSharp)] - [InlineData(LanguageNames.VisualBasic)] - public void VerifyAllCodeStyleFixersAreSupportedByCodeCleanup(string language) + [InlineData(LanguageNames.CSharp, 49)] + [InlineData(LanguageNames.VisualBasic, 86)] + public void VerifyAllCodeStyleFixersAreSupportedByCodeCleanup(string language, int numberOfUnsupportedDiagnosticIds) { var supportedDiagnostics = GetSupportedDiagnosticIdsForCodeCleanupService(language); @@ -646,15 +639,7 @@ public void VerifyAllCodeStyleFixersAreSupportedByCodeCleanup(string language) var ideDiagnosticIds = typeof(IDEDiagnosticIds).GetFields().Select(f => f.GetValue(f) as string).ToArray(); var unsupportedDiagnosticIds = ideDiagnosticIds.Except(supportedDiagnostics).ToArray(); - var expectedNumberOfUnsupportedDiagnosticIds = - language switch - { - LanguageNames.CSharp => 48, - LanguageNames.VisualBasic => 85, - _ => throw ExceptionUtilities.UnexpectedValue(language), - }; - - Assert.Equal(expectedNumberOfUnsupportedDiagnosticIds, unsupportedDiagnosticIds.Length); + Assert.Equal(numberOfUnsupportedDiagnosticIds, unsupportedDiagnosticIds.Length); } private const string _code = """ From d6c68734a73e4e3995620134057ac918ab7e49c3 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 13 Apr 2024 18:43:34 +0300 Subject: [PATCH 04/15] Refactor --- .../MakeAnonymousFunctionStaticDiagnosticAnalyzer.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticDiagnosticAnalyzer.cs index 737781ec44eeb..69727298a2de4 100644 --- a/src/Analyzers/CSharp/Analyzers/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticDiagnosticAnalyzer.cs @@ -2,7 +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. -using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; @@ -47,7 +46,7 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) if (anonymousFunction.Modifiers.Any(SyntaxKind.StaticKeyword)) return; - if (TryGetCaptures(anonymousFunction, context.SemanticModel, out var captures) && captures.IsEmpty) + if (CanAnonymousFunctionByMadeStatic(anonymousFunction, context.SemanticModel)) { context.ReportDiagnostic( Diagnostic.Create( @@ -56,16 +55,14 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) } } - private static bool TryGetCaptures(AnonymousFunctionExpressionSyntax anonymousFunction, SemanticModel semanticModel, out ImmutableArray captures) + private static bool CanAnonymousFunctionByMadeStatic(AnonymousFunctionExpressionSyntax anonymousFunction, SemanticModel semanticModel) { var dataFlow = semanticModel.AnalyzeDataFlow(anonymousFunction); - if (dataFlow is null) + if (dataFlow is not { Succeeded: true }) { - captures = default; return false; } - captures = dataFlow.Captured; - return dataFlow.Succeeded; + return dataFlow.Captured.IsEmpty; } } From ad6845fe19cbfe90acb383956e0e984eeb2a4157 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 13 Apr 2024 18:55:06 +0300 Subject: [PATCH 05/15] Fix more tests --- .../IDEDiagnosticIDConfigurationTests.cs | 471 +++++++++--------- .../DataProvider/DataProviderTests.cs | 5 +- 2 files changed, 239 insertions(+), 237 deletions(-) diff --git a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs index a26df2ed7e046..164f6ad6bb559 100644 --- a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs @@ -8,11 +8,8 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Reflection; using System.Text; -using System.Text.RegularExpressions; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Test.Utilities; @@ -175,352 +172,355 @@ private static void VerifyConfigureSeverityCore(string expected, string language [Fact] public void CSharp_VerifyIDEDiagnosticSeveritiesAreConfigurable() { - var expected = @" -# IDE0001 -dotnet_diagnostic.IDE0001.severity = %value% + var expected = """ + # IDE0001 + dotnet_diagnostic.IDE0001.severity = %value% -# IDE0002 -dotnet_diagnostic.IDE0002.severity = %value% + # IDE0002 + dotnet_diagnostic.IDE0002.severity = %value% -# IDE0003 -dotnet_diagnostic.IDE0003.severity = %value% + # IDE0003 + dotnet_diagnostic.IDE0003.severity = %value% -# IDE0004 -dotnet_diagnostic.IDE0004.severity = %value% + # IDE0004 + dotnet_diagnostic.IDE0004.severity = %value% -# IDE0005 -dotnet_diagnostic.IDE0005.severity = %value% + # IDE0005 + dotnet_diagnostic.IDE0005.severity = %value% -# IDE0007 -dotnet_diagnostic.IDE0007.severity = %value% + # IDE0007 + dotnet_diagnostic.IDE0007.severity = %value% -# IDE0008 -dotnet_diagnostic.IDE0008.severity = %value% + # IDE0008 + dotnet_diagnostic.IDE0008.severity = %value% -# IDE0009 -dotnet_diagnostic.IDE0009.severity = %value% + # IDE0009 + dotnet_diagnostic.IDE0009.severity = %value% -# IDE0010 -dotnet_diagnostic.IDE0010.severity = %value% + # IDE0010 + dotnet_diagnostic.IDE0010.severity = %value% -# IDE0011 -dotnet_diagnostic.IDE0011.severity = %value% + # IDE0011 + dotnet_diagnostic.IDE0011.severity = %value% -# IDE0016 -dotnet_diagnostic.IDE0016.severity = %value% + # IDE0016 + dotnet_diagnostic.IDE0016.severity = %value% -# IDE0017 -dotnet_diagnostic.IDE0017.severity = %value% + # IDE0017 + dotnet_diagnostic.IDE0017.severity = %value% -# IDE0018 -dotnet_diagnostic.IDE0018.severity = %value% + # IDE0018 + dotnet_diagnostic.IDE0018.severity = %value% -# IDE0019 -dotnet_diagnostic.IDE0019.severity = %value% + # IDE0019 + dotnet_diagnostic.IDE0019.severity = %value% -# IDE0020 -dotnet_diagnostic.IDE0020.severity = %value% + # IDE0020 + dotnet_diagnostic.IDE0020.severity = %value% -# IDE0021 -dotnet_diagnostic.IDE0021.severity = %value% + # IDE0021 + dotnet_diagnostic.IDE0021.severity = %value% -# IDE0022 -dotnet_diagnostic.IDE0022.severity = %value% + # IDE0022 + dotnet_diagnostic.IDE0022.severity = %value% -# IDE0023 -dotnet_diagnostic.IDE0023.severity = %value% + # IDE0023 + dotnet_diagnostic.IDE0023.severity = %value% -# IDE0024 -dotnet_diagnostic.IDE0024.severity = %value% + # IDE0024 + dotnet_diagnostic.IDE0024.severity = %value% -# IDE0025 -dotnet_diagnostic.IDE0025.severity = %value% + # IDE0025 + dotnet_diagnostic.IDE0025.severity = %value% -# IDE0026 -dotnet_diagnostic.IDE0026.severity = %value% + # IDE0026 + dotnet_diagnostic.IDE0026.severity = %value% -# IDE0027 -dotnet_diagnostic.IDE0027.severity = %value% + # IDE0027 + dotnet_diagnostic.IDE0027.severity = %value% -# IDE0028 -dotnet_diagnostic.IDE0028.severity = %value% + # IDE0028 + dotnet_diagnostic.IDE0028.severity = %value% -# IDE0029 -dotnet_diagnostic.IDE0029.severity = %value% + # IDE0029 + dotnet_diagnostic.IDE0029.severity = %value% -# IDE0030 -dotnet_diagnostic.IDE0030.severity = %value% + # IDE0030 + dotnet_diagnostic.IDE0030.severity = %value% -# IDE0031 -dotnet_diagnostic.IDE0031.severity = %value% + # IDE0031 + dotnet_diagnostic.IDE0031.severity = %value% -# IDE0032 -dotnet_diagnostic.IDE0032.severity = %value% + # IDE0032 + dotnet_diagnostic.IDE0032.severity = %value% -# IDE0033 -dotnet_diagnostic.IDE0033.severity = %value% + # IDE0033 + dotnet_diagnostic.IDE0033.severity = %value% -# IDE0034 -dotnet_diagnostic.IDE0034.severity = %value% + # IDE0034 + dotnet_diagnostic.IDE0034.severity = %value% -# IDE0035 -dotnet_diagnostic.IDE0035.severity = %value% + # IDE0035 + dotnet_diagnostic.IDE0035.severity = %value% -# IDE0036 -dotnet_diagnostic.IDE0036.severity = %value% + # IDE0036 + dotnet_diagnostic.IDE0036.severity = %value% -# IDE0037 -dotnet_diagnostic.IDE0037.severity = %value% + # IDE0037 + dotnet_diagnostic.IDE0037.severity = %value% -# IDE0038 -dotnet_diagnostic.IDE0038.severity = %value% + # IDE0038 + dotnet_diagnostic.IDE0038.severity = %value% -# IDE0039 -dotnet_diagnostic.IDE0039.severity = %value% + # IDE0039 + dotnet_diagnostic.IDE0039.severity = %value% -# IDE0040 -dotnet_diagnostic.IDE0040.severity = %value% + # IDE0040 + dotnet_diagnostic.IDE0040.severity = %value% -# IDE0041 -dotnet_diagnostic.IDE0041.severity = %value% + # IDE0041 + dotnet_diagnostic.IDE0041.severity = %value% -# IDE0042 -dotnet_diagnostic.IDE0042.severity = %value% + # IDE0042 + dotnet_diagnostic.IDE0042.severity = %value% -# IDE0043 -dotnet_diagnostic.IDE0043.severity = %value% + # IDE0043 + dotnet_diagnostic.IDE0043.severity = %value% -# IDE0044 -dotnet_diagnostic.IDE0044.severity = %value% + # IDE0044 + dotnet_diagnostic.IDE0044.severity = %value% -# IDE0045 -dotnet_diagnostic.IDE0045.severity = %value% + # IDE0045 + dotnet_diagnostic.IDE0045.severity = %value% -# IDE0046 -dotnet_diagnostic.IDE0046.severity = %value% + # IDE0046 + dotnet_diagnostic.IDE0046.severity = %value% -# IDE0047 -dotnet_diagnostic.IDE0047.severity = %value% + # IDE0047 + dotnet_diagnostic.IDE0047.severity = %value% -# IDE0048 -dotnet_diagnostic.IDE0048.severity = %value% + # IDE0048 + dotnet_diagnostic.IDE0048.severity = %value% -# IDE0049 -dotnet_diagnostic.IDE0049.severity = %value% + # IDE0049 + dotnet_diagnostic.IDE0049.severity = %value% -# IDE0051 -dotnet_diagnostic.IDE0051.severity = %value% + # IDE0051 + dotnet_diagnostic.IDE0051.severity = %value% -# IDE0052 -dotnet_diagnostic.IDE0052.severity = %value% + # IDE0052 + dotnet_diagnostic.IDE0052.severity = %value% -# IDE0053 -dotnet_diagnostic.IDE0053.severity = %value% + # IDE0053 + dotnet_diagnostic.IDE0053.severity = %value% -# IDE0054 -dotnet_diagnostic.IDE0054.severity = %value% + # IDE0054 + dotnet_diagnostic.IDE0054.severity = %value% -# IDE0055 -dotnet_diagnostic.IDE0055.severity = %value% + # IDE0055 + dotnet_diagnostic.IDE0055.severity = %value% -# IDE0056 -dotnet_diagnostic.IDE0056.severity = %value% + # IDE0056 + dotnet_diagnostic.IDE0056.severity = %value% -# IDE0057 -dotnet_diagnostic.IDE0057.severity = %value% + # IDE0057 + dotnet_diagnostic.IDE0057.severity = %value% -# IDE0058 -dotnet_diagnostic.IDE0058.severity = %value% + # IDE0058 + dotnet_diagnostic.IDE0058.severity = %value% -# IDE0059 -dotnet_diagnostic.IDE0059.severity = %value% + # IDE0059 + dotnet_diagnostic.IDE0059.severity = %value% -# IDE0060 -dotnet_diagnostic.IDE0060.severity = %value% + # IDE0060 + dotnet_diagnostic.IDE0060.severity = %value% -# IDE0061 -dotnet_diagnostic.IDE0061.severity = %value% + # IDE0061 + dotnet_diagnostic.IDE0061.severity = %value% -# IDE0062 -dotnet_diagnostic.IDE0062.severity = %value% + # IDE0062 + dotnet_diagnostic.IDE0062.severity = %value% -# IDE0063 -dotnet_diagnostic.IDE0063.severity = %value% + # IDE0063 + dotnet_diagnostic.IDE0063.severity = %value% -# IDE0064 -dotnet_diagnostic.IDE0064.severity = %value% + # IDE0064 + dotnet_diagnostic.IDE0064.severity = %value% -# IDE0065 -dotnet_diagnostic.IDE0065.severity = %value% + # IDE0065 + dotnet_diagnostic.IDE0065.severity = %value% -# IDE0066 -dotnet_diagnostic.IDE0066.severity = %value% + # IDE0066 + dotnet_diagnostic.IDE0066.severity = %value% -# IDE0070 -dotnet_diagnostic.IDE0070.severity = %value% + # IDE0070 + dotnet_diagnostic.IDE0070.severity = %value% -# IDE0071 -dotnet_diagnostic.IDE0071.severity = %value% + # IDE0071 + dotnet_diagnostic.IDE0071.severity = %value% -# IDE0072 -dotnet_diagnostic.IDE0072.severity = %value% + # IDE0072 + dotnet_diagnostic.IDE0072.severity = %value% -# IDE0073 -dotnet_diagnostic.IDE0073.severity = %value% + # IDE0073 + dotnet_diagnostic.IDE0073.severity = %value% -# IDE0074 -dotnet_diagnostic.IDE0074.severity = %value% + # IDE0074 + dotnet_diagnostic.IDE0074.severity = %value% -# IDE0075 -dotnet_diagnostic.IDE0075.severity = %value% + # IDE0075 + dotnet_diagnostic.IDE0075.severity = %value% -# IDE0076 -dotnet_diagnostic.IDE0076.severity = %value% + # IDE0076 + dotnet_diagnostic.IDE0076.severity = %value% -# IDE0077 -dotnet_diagnostic.IDE0077.severity = %value% + # IDE0077 + dotnet_diagnostic.IDE0077.severity = %value% -# IDE0078 -dotnet_diagnostic.IDE0078.severity = %value% + # IDE0078 + dotnet_diagnostic.IDE0078.severity = %value% -# IDE0079 -dotnet_diagnostic.IDE0079.severity = %value% + # IDE0079 + dotnet_diagnostic.IDE0079.severity = %value% -# IDE0080 -dotnet_diagnostic.IDE0080.severity = %value% + # IDE0080 + dotnet_diagnostic.IDE0080.severity = %value% -# IDE0082 -dotnet_diagnostic.IDE0082.severity = %value% + # IDE0082 + dotnet_diagnostic.IDE0082.severity = %value% -# IDE0083 -dotnet_diagnostic.IDE0083.severity = %value% + # IDE0083 + dotnet_diagnostic.IDE0083.severity = %value% -# IDE0090 -dotnet_diagnostic.IDE0090.severity = %value% + # IDE0090 + dotnet_diagnostic.IDE0090.severity = %value% -# IDE0100 -dotnet_diagnostic.IDE0100.severity = %value% + # IDE0100 + dotnet_diagnostic.IDE0100.severity = %value% -# IDE0110 -dotnet_diagnostic.IDE0110.severity = %value% + # IDE0110 + dotnet_diagnostic.IDE0110.severity = %value% -# IDE0120 -dotnet_diagnostic.IDE0120.severity = %value% + # IDE0120 + dotnet_diagnostic.IDE0120.severity = %value% -# IDE0130 -dotnet_diagnostic.IDE0130.severity = %value% + # IDE0130 + dotnet_diagnostic.IDE0130.severity = %value% -# IDE0150 -dotnet_diagnostic.IDE0150.severity = %value% + # IDE0150 + dotnet_diagnostic.IDE0150.severity = %value% -# IDE0160 -dotnet_diagnostic.IDE0160.severity = %value% + # IDE0160 + dotnet_diagnostic.IDE0160.severity = %value% -# IDE0161 -dotnet_diagnostic.IDE0161.severity = %value% + # IDE0161 + dotnet_diagnostic.IDE0161.severity = %value% -# IDE0170 -dotnet_diagnostic.IDE0170.severity = %value% + # IDE0170 + dotnet_diagnostic.IDE0170.severity = %value% -# IDE0180 -dotnet_diagnostic.IDE0180.severity = %value% + # IDE0180 + dotnet_diagnostic.IDE0180.severity = %value% -# IDE0200 -dotnet_diagnostic.IDE0200.severity = %value% + # IDE0200 + dotnet_diagnostic.IDE0200.severity = %value% -# IDE0210 -dotnet_diagnostic.IDE0210.severity = %value% + # IDE0210 + dotnet_diagnostic.IDE0210.severity = %value% -# IDE0211 -dotnet_diagnostic.IDE0211.severity = %value% + # IDE0211 + dotnet_diagnostic.IDE0211.severity = %value% -# IDE0220 -dotnet_diagnostic.IDE0220.severity = %value% + # IDE0220 + dotnet_diagnostic.IDE0220.severity = %value% -# IDE0230 -dotnet_diagnostic.IDE0230.severity = %value% + # IDE0230 + dotnet_diagnostic.IDE0230.severity = %value% -# IDE0240 -dotnet_diagnostic.IDE0240.severity = %value% + # IDE0240 + dotnet_diagnostic.IDE0240.severity = %value% -# IDE0241 -dotnet_diagnostic.IDE0241.severity = %value% + # IDE0241 + dotnet_diagnostic.IDE0241.severity = %value% -# IDE0250 -dotnet_diagnostic.IDE0250.severity = %value% + # IDE0250 + dotnet_diagnostic.IDE0250.severity = %value% -# IDE0251 -dotnet_diagnostic.IDE0251.severity = %value% + # IDE0251 + dotnet_diagnostic.IDE0251.severity = %value% -# IDE0260 -dotnet_diagnostic.IDE0260.severity = %value% + # IDE0260 + dotnet_diagnostic.IDE0260.severity = %value% -# IDE0270 -dotnet_diagnostic.IDE0270.severity = %value% + # IDE0270 + dotnet_diagnostic.IDE0270.severity = %value% -# IDE0280 -dotnet_diagnostic.IDE0280.severity = %value% + # IDE0280 + dotnet_diagnostic.IDE0280.severity = %value% -# IDE0290 -dotnet_diagnostic.IDE0290.severity = %value% + # IDE0290 + dotnet_diagnostic.IDE0290.severity = %value% -# IDE0300 -dotnet_diagnostic.IDE0300.severity = %value% + # IDE0300 + dotnet_diagnostic.IDE0300.severity = %value% -# IDE0301 -dotnet_diagnostic.IDE0301.severity = %value% + # IDE0301 + dotnet_diagnostic.IDE0301.severity = %value% -# IDE0302 -dotnet_diagnostic.IDE0302.severity = %value% + # IDE0302 + dotnet_diagnostic.IDE0302.severity = %value% -# IDE0303 -dotnet_diagnostic.IDE0303.severity = %value% + # IDE0303 + dotnet_diagnostic.IDE0303.severity = %value% -# IDE0304 -dotnet_diagnostic.IDE0304.severity = %value% + # IDE0304 + dotnet_diagnostic.IDE0304.severity = %value% -# IDE0305 -dotnet_diagnostic.IDE0305.severity = %value% + # IDE0305 + dotnet_diagnostic.IDE0305.severity = %value% -# IDE1005 -dotnet_diagnostic.IDE1005.severity = %value% + # IDE0310 + dotnet_diagnostic.IDE0310.severity = %value% -# IDE1006 -dotnet_diagnostic.IDE1006.severity = %value% + # IDE1005 + dotnet_diagnostic.IDE1005.severity = %value% -# IDE1007 -dotnet_diagnostic.IDE1007.severity = %value% + # IDE1006 + dotnet_diagnostic.IDE1006.severity = %value% -# IDE2000 -dotnet_diagnostic.IDE2000.severity = %value% + # IDE1007 + dotnet_diagnostic.IDE1007.severity = %value% -# IDE2001 -dotnet_diagnostic.IDE2001.severity = %value% + # IDE2000 + dotnet_diagnostic.IDE2000.severity = %value% -# IDE2002 -dotnet_diagnostic.IDE2002.severity = %value% + # IDE2001 + dotnet_diagnostic.IDE2001.severity = %value% -# IDE2003 -dotnet_diagnostic.IDE2003.severity = %value% + # IDE2002 + dotnet_diagnostic.IDE2002.severity = %value% -# IDE2004 -dotnet_diagnostic.IDE2004.severity = %value% + # IDE2003 + dotnet_diagnostic.IDE2003.severity = %value% -# IDE2005 -dotnet_diagnostic.IDE2005.severity = %value% + # IDE2004 + dotnet_diagnostic.IDE2004.severity = %value% -# IDE2006 -dotnet_diagnostic.IDE2006.severity = %value% + # IDE2005 + dotnet_diagnostic.IDE2005.severity = %value% -# RE0001 -dotnet_diagnostic.RE0001.severity = %value% + # IDE2006 + dotnet_diagnostic.IDE2006.severity = %value% -# JSON001 -dotnet_diagnostic.JSON001.severity = %value% + # RE0001 + dotnet_diagnostic.RE0001.severity = %value% -# JSON002 -dotnet_diagnostic.JSON002.severity = %value% -"; + # JSON001 + dotnet_diagnostic.JSON001.severity = %value% + + # JSON002 + dotnet_diagnostic.JSON002.severity = %value% + """; VerifyConfigureSeverityCore(expected, LanguageNames.CSharp); } @@ -895,6 +895,7 @@ public void CSharp_VerifyIDECodeStyleOptionsAreConfigurable() ("IDE0303", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), ("IDE0304", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), ("IDE0305", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), + ("IDE0310", "csharp_prefer_static_anonymous_function", "true"), ("IDE1005", "csharp_style_conditional_delegate_call", "true"), ("IDE1006", null, null), ("IDE1007", null, null), diff --git a/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs b/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs index cc30ed940278c..14e4d2d695301 100644 --- a/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs +++ b/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs @@ -165,9 +165,10 @@ public void TestGettingCodeStyleSettingsProviderLanguageServiceAsync() settingsProvider.RegisterViewModel(model); var dataSnapShot = settingsProvider.GetCurrentDataSnapshot(); - // We don't support PreferredModifierOrder yet: + // We don't support PreferredModifierOrder and PreferStaticAnonymousFunction yet: var optionsWithUI = CSharpCodeStyleOptions.AllOptions - .Remove(CSharpCodeStyleOptions.PreferredModifierOrder); + .Remove(CSharpCodeStyleOptions.PreferredModifierOrder) + .Remove(CSharpCodeStyleOptions.PreferStaticAnonymousFunction); AssertEx.SetEqual(optionsWithUI, dataSnapShot.Select(setting => setting.Key.Option)); } From ebc7c0141f2447cb4921d41005f8a5628e8ae169 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 13 Apr 2024 19:19:17 +0300 Subject: [PATCH 06/15] Fix nested case --- ...eAnonymousFunctionStaticCodeFixProvider.cs | 5 +- .../MakeAnonymousFunctionStaticTests.cs | 88 +++++++++++++++++++ 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/src/Analyzers/CSharp/CodeFixes/MakeAnonymousFunctionStatic/CSharpMakeAnonymousFunctionStaticCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/MakeAnonymousFunctionStatic/CSharpMakeAnonymousFunctionStaticCodeFixProvider.cs index 4e592a23915ed..2c0071a9d9e8c 100644 --- a/src/Analyzers/CSharp/CodeFixes/MakeAnonymousFunctionStatic/CSharpMakeAnonymousFunctionStaticCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/MakeAnonymousFunctionStatic/CSharpMakeAnonymousFunctionStaticCodeFixProvider.cs @@ -37,10 +37,7 @@ protected override Task FixAllAsync(Document document, ImmutableArray generator.WithModifiers(node, generator.GetModifiers(node).WithIsStatic(true))); } return Task.CompletedTask; diff --git a/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs b/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs index 0c858b35baadc..0b4b1e156aaf3 100644 --- a/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs @@ -318,4 +318,92 @@ void N(Func f) await TestWithCSharp9Async(code, fixedCode); } + + [Fact] + public async Task TestNestedLambdasWithNoCaptures() + { + var code = """ + using System; + + class C + { + void M() + { + N({|IDE0310:() => + { + Action a = {|IDE0310:() => { }|}; + }|}); + } + + void N(Action a) + { + } + } + """; + + var fixedCode = """ + using System; + + class C + { + void M() + { + N(static () => + { + Action a = static () => { }; + }); + } + + void N(Action a) + { + } + } + """; + + await TestWithCSharp9Async(code, fixedCode); + } + + [Fact] + public async Task TestNestedAnonymousMethodsWithNoCaptures() + { + var code = """ + using System; + + class C + { + void M() + { + N({|IDE0310:delegate () + { + Action a = {|IDE0310:delegate () { }|}; + }|}); + } + + void N(Action a) + { + } + } + """; + + var fixedCode = """ + using System; + + class C + { + void M() + { + N(static delegate () + { + Action a = static delegate () { }; + }); + } + + void N(Action a) + { + } + } + """; + + await TestWithCSharp9Async(code, fixedCode); + } } From 622c8b83b7ae62fedde50549bd350c57f2fba67d Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 13 Apr 2024 19:26:08 +0300 Subject: [PATCH 07/15] Make example semantically correct --- .../CSharp/Impl/Options/Formatting/StyleViewModel.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs b/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs index ae782870da465..166ffa7af88cc 100644 --- a/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs +++ b/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs @@ -1550,18 +1550,17 @@ int fibonacci(int n) private static readonly string s_preferStaticAnonymousFunction = $$""" using System; - //[ // {{ServicesVSResources.Prefer_colon}} Func f1 = static i => i * i; - Func f2 = static (i, j) => i * j; - Func f3 = static delegate (int i) { return i * i; }; + Func f2 = static delegate (int i) { return i * i; }; + Func f3 = static (i, j) => i * j; //] //[ // {{ServicesVSResources.Over_colon}} Func f1 = i => i * i; - Func f2 = (i, j) => i * j; - Func f3 = delegate (int i) { return i * i; }; + Func f2 = delegate (int i) { return i * i; }; + Func f3 = (i, j) => i * j; //] """; From 7757059c745410433bef77c383d1bc3496f3ed2c Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 13 Apr 2024 20:35:04 +0300 Subject: [PATCH 08/15] Simplify --- ...MakeAnonymousFunctionStaticDiagnosticAnalyzer.cs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/Analyzers/CSharp/Analyzers/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticDiagnosticAnalyzer.cs index 69727298a2de4..d1edd47f80269 100644 --- a/src/Analyzers/CSharp/Analyzers/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticDiagnosticAnalyzer.cs @@ -46,7 +46,7 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) if (anonymousFunction.Modifiers.Any(SyntaxKind.StaticKeyword)) return; - if (CanAnonymousFunctionByMadeStatic(anonymousFunction, context.SemanticModel)) + if (context.SemanticModel.AnalyzeDataFlow(anonymousFunction) is { Succeeded: true, Captured.IsEmpty: true }) { context.ReportDiagnostic( Diagnostic.Create( @@ -54,15 +54,4 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) anonymousFunction.GetLocation())); } } - - private static bool CanAnonymousFunctionByMadeStatic(AnonymousFunctionExpressionSyntax anonymousFunction, SemanticModel semanticModel) - { - var dataFlow = semanticModel.AnalyzeDataFlow(anonymousFunction); - if (dataFlow is not { Succeeded: true }) - { - return false; - } - - return dataFlow.Captured.IsEmpty; - } } From 73171b57bfe8a9aaeda2e4c672eb99e6f61488b5 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 13 Apr 2024 21:54:29 +0300 Subject: [PATCH 09/15] Bump diagnostic ID --- .../MakeAnonymousFunctionStaticTests.cs | 14 +++++++------- .../Core/Analyzers/EnforceOnBuildValues.cs | 2 +- src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs | 2 +- .../IDEDiagnosticIDConfigurationTests.cs | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs b/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs index 0b4b1e156aaf3..f3f98bfccfc09 100644 --- a/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs @@ -129,7 +129,7 @@ class C { void M() { - N({|IDE0310:{{anonymousFunctionSyntax}}|}); + N({|IDE0320:{{anonymousFunctionSyntax}}|}); } void N(Action a) @@ -222,7 +222,7 @@ class C { void M(int i) { - N({|IDE0310:{{anonymousFunctionSyntax}}|}); + N({|IDE0320:{{anonymousFunctionSyntax}}|}); } void N(Func f) @@ -290,7 +290,7 @@ class C void M() { int i = 0; - N({|IDE0310:{{anonymousFunctionSyntax}}|}); + N({|IDE0320:{{anonymousFunctionSyntax}}|}); } void N(Func f) @@ -329,9 +329,9 @@ class C { void M() { - N({|IDE0310:() => + N({|IDE0320:() => { - Action a = {|IDE0310:() => { }|}; + Action a = {|IDE0320:() => { }|}; }|}); } @@ -373,9 +373,9 @@ class C { void M() { - N({|IDE0310:delegate () + N({|IDE0320:delegate () { - Action a = {|IDE0310:delegate () { }|}; + Action a = {|IDE0320:delegate () { }|}; }|}); } diff --git a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs index 1373929bacae6..99b146a48c43e 100644 --- a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs +++ b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs @@ -96,7 +96,7 @@ internal static class EnforceOnBuildValues public const EnforceOnBuild UseCollectionExpressionForCreate = /*IDE0303*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseCollectionExpressionForBuilder = /*IDE0304*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseCollectionExpressionForFluent = /*IDE0305*/ EnforceOnBuild.Recommended; - public const EnforceOnBuild MakeAnonymousFunctionStatic = /*IDE0310*/ EnforceOnBuild.Recommended; + public const EnforceOnBuild MakeAnonymousFunctionStatic = /*IDE0320*/ EnforceOnBuild.Recommended; /* EnforceOnBuild.WhenExplicitlyEnabled */ public const EnforceOnBuild RemoveUnnecessaryCast = /*IDE0004*/ EnforceOnBuild.WhenExplicitlyEnabled; // TODO: Move to 'Recommended' OR 'HighlyRecommended' bucket once performance problems are addressed: https://github.com/dotnet/roslyn/issues/43304 diff --git a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs index 6c4aec2037b4e..7d4b190395472 100644 --- a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs +++ b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs @@ -199,7 +199,7 @@ internal static class IDEDiagnosticIds public const string UseCollectionExpressionForBuilderDiagnosticId = "IDE0304"; public const string UseCollectionExpressionForFluentDiagnosticId = "IDE0305"; - public const string MakeAnonymousFunctionStaticDiagnosticId = "IDE0310"; + public const string MakeAnonymousFunctionStaticDiagnosticId = "IDE0320"; // Analyzer error Ids public const string AnalyzerChangedId = "IDE1001"; diff --git a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs index 164f6ad6bb559..6b82044ca95c0 100644 --- a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs @@ -479,8 +479,8 @@ public void CSharp_VerifyIDEDiagnosticSeveritiesAreConfigurable() # IDE0305 dotnet_diagnostic.IDE0305.severity = %value% - # IDE0310 - dotnet_diagnostic.IDE0310.severity = %value% + # IDE0320 + dotnet_diagnostic.IDE0320.severity = %value% # IDE1005 dotnet_diagnostic.IDE1005.severity = %value% @@ -895,7 +895,7 @@ public void CSharp_VerifyIDECodeStyleOptionsAreConfigurable() ("IDE0303", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), ("IDE0304", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), ("IDE0305", "dotnet_style_prefer_collection_expression", "when_types_loosely_match"), - ("IDE0310", "csharp_prefer_static_anonymous_function", "true"), + ("IDE0320", "csharp_prefer_static_anonymous_function", "true"), ("IDE1005", "csharp_style_conditional_delegate_call", "true"), ("IDE1006", null, null), ("IDE1007", null, null), From feb8c29788902410afb31c404caa10dd9a9ed2c1 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 13 Apr 2024 22:07:05 +0300 Subject: [PATCH 10/15] Lower code action priority --- .../CSharpMakeAnonymousFunctionStaticCodeFixProvider.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Analyzers/CSharp/CodeFixes/MakeAnonymousFunctionStatic/CSharpMakeAnonymousFunctionStaticCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/MakeAnonymousFunctionStatic/CSharpMakeAnonymousFunctionStaticCodeFixProvider.cs index 2c0071a9d9e8c..4691f825733e9 100644 --- a/src/Analyzers/CSharp/CodeFixes/MakeAnonymousFunctionStatic/CSharpMakeAnonymousFunctionStaticCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/MakeAnonymousFunctionStatic/CSharpMakeAnonymousFunctionStaticCodeFixProvider.cs @@ -26,7 +26,12 @@ internal sealed class CSharpMakeAnonymousFunctionStaticCodeFixProvider() : Synta public override Task RegisterCodeFixesAsync(CodeFixContext context) { - RegisterCodeFix(context, CSharpAnalyzersResources.Make_anonymous_function_static, nameof(CSharpAnalyzersResources.Make_anonymous_function_static)); + RegisterCodeFix( + context, + CSharpAnalyzersResources.Make_anonymous_function_static, + nameof(CSharpAnalyzersResources.Make_anonymous_function_static), + context.Diagnostics[0].Severity > DiagnosticSeverity.Hidden ? CodeActionPriority.Default : CodeActionPriority.Low); + return Task.CompletedTask; } From 8668c6d65ab6d81e8519bec37dd5747ab7c61eea Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 13 Apr 2024 22:08:45 +0300 Subject: [PATCH 11/15] Simplify example --- .../CSharp/Impl/Options/Formatting/StyleViewModel.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs b/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs index 166ffa7af88cc..7b7dfdecfe67e 100644 --- a/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs +++ b/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs @@ -1552,15 +1552,11 @@ int fibonacci(int n) using System; //[ // {{ServicesVSResources.Prefer_colon}} - Func f1 = static i => i * i; - Func f2 = static delegate (int i) { return i * i; }; - Func f3 = static (i, j) => i * j; + Func f = static i => i * i; //] //[ // {{ServicesVSResources.Over_colon}} - Func f1 = i => i * i; - Func f2 = delegate (int i) { return i * i; }; - Func f3 = (i, j) => i * j; + Func f = i => i * i; //] """; From 41bfda6c478914069d380df0ef0a5eacea81ccc4 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 13 Apr 2024 22:15:38 +0300 Subject: [PATCH 12/15] Add test --- .../MakeAnonymousFunctionStaticTests.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs b/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs index f3f98bfccfc09..d08492d3bc96c 100644 --- a/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs @@ -184,6 +184,51 @@ void N(Func f) await TestWithCSharp9Async(code, code); } + [Theory] + [InlineData("i => GetValueFromStaticMethod()")] + [InlineData("(i) => GetValueFromStaticMethod()")] + [InlineData("delegate (int i) { return GetValueFromStaticMethod(); }")] + public async Task TestNoCaptures_ReferencesStaticMethod(string anonymousFunctionSyntax) + { + var code = $$""" + using System; + + class C + { + private static int GetValueFromStaticMethod() => 0; + + void M() + { + N({|IDE0320:{{anonymousFunctionSyntax}}|}); + } + + void N(Func f) + { + } + } + """; + + var fixedCode = $$""" + using System; + + class C + { + private static int GetValueFromStaticMethod() => 0; + + void M() + { + N(static {{anonymousFunctionSyntax}}); + } + + void N(Func f) + { + } + } + """; + + await TestWithCSharp9Async(code, fixedCode); + } + [Theory] [InlineData("i => x")] [InlineData("(i) => x")] From 7c64a3515e4be3934c63756f7e60980da093374b Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 13 Apr 2024 22:27:41 +0300 Subject: [PATCH 13/15] Option work --- .../CodeStyle/CSharpCodeStyleSettingsProvider.cs | 2 +- .../Options/AutomationObject/AutomationObject.Style.cs | 6 ++++++ .../DataProvider/DataProviderTests.cs | 5 ++--- .../Core/Def/Options/VisualStudioOptionStorage.cs | 8 +------- .../CSharp/CodeStyle/CSharpIdeCodeStyleOptions.cs | 1 + 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs b/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs index d2a6585c92a74..c6575aafcd670 100644 --- a/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs +++ b/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Data; using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.DataProvider; using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Updater; @@ -84,6 +83,7 @@ private static IEnumerable GetNullCheckingCodeStyleOptions(Tie private static IEnumerable GetModifierCodeStyleOptions(TieredAnalyzerConfigOptions options, OptionUpdater updater) { yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferStaticLocalFunction, ServicesVSResources.Prefer_static_local_functions, options, updater); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferStaticAnonymousFunction, ServicesVSResources.Prefer_static_anonymous_functions, options, updater); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferReadOnlyStruct, ServicesVSResources.Prefer_read_only_struct, options, updater); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferReadOnlyStructMember, ServicesVSResources.Prefer_read_only_struct_member, options, updater); } diff --git a/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Style.cs b/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Style.cs index b707b2f1203c3..7c5d4766822c9 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Style.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Style.cs @@ -345,6 +345,12 @@ public string Style_PreferStaticLocalFunction set { SetXmlOption(CSharpCodeStyleOptions.PreferStaticLocalFunction, value); } } + public string Style_PreferStaticAnonymousFunction + { + get { return GetXmlOption(CSharpCodeStyleOptions.PreferStaticAnonymousFunction); } + set { SetXmlOption(CSharpCodeStyleOptions.PreferStaticAnonymousFunction, value); } + } + public string Style_PreferSimpleUsingStatement { get { return GetXmlOption(CSharpCodeStyleOptions.PreferSimpleUsingStatement); } diff --git a/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs b/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs index 14e4d2d695301..cc30ed940278c 100644 --- a/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs +++ b/src/VisualStudio/CSharp/Test/EditorConfigSettings/DataProvider/DataProviderTests.cs @@ -165,10 +165,9 @@ public void TestGettingCodeStyleSettingsProviderLanguageServiceAsync() settingsProvider.RegisterViewModel(model); var dataSnapShot = settingsProvider.GetCurrentDataSnapshot(); - // We don't support PreferredModifierOrder and PreferStaticAnonymousFunction yet: + // We don't support PreferredModifierOrder yet: var optionsWithUI = CSharpCodeStyleOptions.AllOptions - .Remove(CSharpCodeStyleOptions.PreferredModifierOrder) - .Remove(CSharpCodeStyleOptions.PreferStaticAnonymousFunction); + .Remove(CSharpCodeStyleOptions.PreferredModifierOrder); AssertEx.SetEqual(optionsWithUI, dataSnapShot.Select(setting => setting.Key.Option)); } diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index 200238b7eacd3..8841c4fa3071a 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -4,17 +4,10 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Options; -using Microsoft.VisualStudio.Language.Intellisense; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Options; @@ -171,6 +164,7 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"csharp_prefer_simple_default_expression", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferSimpleDefaultExpression")}, {"csharp_prefer_simple_using_statement", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferSimpleUsingStatement")}, {"csharp_prefer_static_local_function", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferStaticLocalFunction")}, + {"csharp_prefer_static_anonymous_function", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferStaticAnonymousFunction")}, {"csharp_preferred_modifier_order", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferredModifierOrder")}, {"csharp_preserve_single_line_blocks", new RoamingProfileStorage("TextEditor.CSharp.Specific.WrappingPreserveSingleLine")}, {"csharp_preserve_single_line_statements", new RoamingProfileStorage("TextEditor.CSharp.Specific.WrappingKeepStatementsOnSingleLine")}, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpIdeCodeStyleOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpIdeCodeStyleOptions.cs index 3dab88bb89ec6..e257332c63b21 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpIdeCodeStyleOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpIdeCodeStyleOptions.cs @@ -121,6 +121,7 @@ internal CSharpIdeCodeStyleOptions(IOptionsReader options, CSharpIdeCodeStyleOpt PreferReadOnlyStruct = options.GetOption(CSharpCodeStyleOptions.PreferReadOnlyStruct, fallbackOptions.PreferReadOnlyStruct); PreferReadOnlyStructMember = options.GetOption(CSharpCodeStyleOptions.PreferReadOnlyStructMember, fallbackOptions.PreferReadOnlyStructMember); PreferStaticLocalFunction = options.GetOption(CSharpCodeStyleOptions.PreferStaticLocalFunction, fallbackOptions.PreferStaticLocalFunction); + PreferStaticAnonymousFunction = options.GetOption(CSharpCodeStyleOptions.PreferStaticAnonymousFunction, fallbackOptions.PreferStaticAnonymousFunction); PreferPrimaryConstructors = options.GetOption(CSharpCodeStyleOptions.PreferPrimaryConstructors, fallbackOptions.PreferPrimaryConstructors); } } From 412b744e959cecf0147aadad160a02d4c4912fa6 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Mon, 15 Apr 2024 08:48:28 +0300 Subject: [PATCH 14/15] Simplify test markup --- .../MakeAnonymousFunctionStaticTests.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs b/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs index d08492d3bc96c..d3e2d6a664fa5 100644 --- a/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeAnonymousFunctionStatic/MakeAnonymousFunctionStaticTests.cs @@ -129,7 +129,7 @@ class C { void M() { - N({|IDE0320:{{anonymousFunctionSyntax}}|}); + N([|{{anonymousFunctionSyntax}}|]); } void N(Action a) @@ -199,7 +199,7 @@ class C void M() { - N({|IDE0320:{{anonymousFunctionSyntax}}|}); + N([|{{anonymousFunctionSyntax}}|]); } void N(Func f) @@ -267,7 +267,7 @@ class C { void M(int i) { - N({|IDE0320:{{anonymousFunctionSyntax}}|}); + N([|{{anonymousFunctionSyntax}}|]); } void N(Func f) @@ -335,7 +335,7 @@ class C void M() { int i = 0; - N({|IDE0320:{{anonymousFunctionSyntax}}|}); + N([|{{anonymousFunctionSyntax}}|]); } void N(Func f) @@ -374,10 +374,10 @@ class C { void M() { - N({|IDE0320:() => + N([|() => { - Action a = {|IDE0320:() => { }|}; - }|}); + Action a = [|() => { }|]; + }|]); } void N(Action a) @@ -418,10 +418,10 @@ class C { void M() { - N({|IDE0320:delegate () + N([|delegate () { - Action a = {|IDE0320:delegate () { }|}; - }|}); + Action a = [|delegate () { }|]; + }|]); } void N(Action a) From 1bf9c2d1b04c5f88370b8b750c665622c57c53f8 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Mon, 15 Apr 2024 20:46:29 +0300 Subject: [PATCH 15/15] Add undocumented diagnostic entry --- src/Features/RulesMissingDocumentation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Features/RulesMissingDocumentation.md b/src/Features/RulesMissingDocumentation.md index e515219729aa8..0e0f9aa6c62a3 100644 --- a/src/Features/RulesMissingDocumentation.md +++ b/src/Features/RulesMissingDocumentation.md @@ -11,6 +11,7 @@ IDE0302 | | Simplify collection initialization | IDE0304 | | Simplify collection initialization | IDE0305 | | Simplify collection initialization | +IDE0320 | | Make anonymous function static | IDE1007 | | | IDE2000 | | Avoid multiple blank lines | IDE2001 | | Embedded statements must be on their own line |