diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems index cd3b49725826e..f248575e467bb 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems @@ -122,6 +122,7 @@ + diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx index 1574a4e49a86a..4ebe1d626a4ea 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx @@ -305,6 +305,12 @@ 'new' expression can be simplified + + Lambda expression can be simplified + + + Use implicitly typed lambda + Discard can be removed diff --git a/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs b/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs index a2fc11e8f70c3..0e5b54eb44ac0 100644 --- a/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs +++ b/src/Analyzers/CSharp/Analyzers/CodeStyle/CSharpAnalyzerOptionsProvider.cs @@ -55,6 +55,7 @@ internal CSharpSimplifierOptions GetSimplifierOptions() public CodeStyleOption2 PreferPatternMatchingOverIsWithCastCheck => GetOption(CSharpCodeStyleOptions.PreferPatternMatchingOverIsWithCastCheck); public CodeStyleOption2 PreferNotPattern => GetOption(CSharpCodeStyleOptions.PreferNotPattern); public CodeStyleOption2 PreferExtendedPropertyPattern => GetOption(CSharpCodeStyleOptions.PreferExtendedPropertyPattern); + public CodeStyleOption2 PreferImplicitlyTypedLambdaExpression => GetOption(CSharpCodeStyleOptions.PreferImplicitlyTypedLambdaExpression); public CodeStyleOption2 PreferInlinedVariableDeclaration => GetOption(CSharpCodeStyleOptions.PreferInlinedVariableDeclaration); public CodeStyleOption2 PreferDeconstructedVariableDeclaration => GetOption(CSharpCodeStyleOptions.PreferDeconstructedVariableDeclaration); public CodeStyleOption2 PreferIndexOperator => GetOption(CSharpCodeStyleOptions.PreferIndexOperator); diff --git a/src/Analyzers/CSharp/Analyzers/UseImplicitlyTypedLambdaExpression/CSharpUseImplicitlyTypedLambdaExpressionDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseImplicitlyTypedLambdaExpression/CSharpUseImplicitlyTypedLambdaExpressionDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..747bd949e2fca --- /dev/null +++ b/src/Analyzers/CSharp/Analyzers/UseImplicitlyTypedLambdaExpression/CSharpUseImplicitlyTypedLambdaExpressionDiagnosticAnalyzer.cs @@ -0,0 +1,129 @@ +// 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.Linq; +using System.Threading; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.UseImplicitlyTypedLambdaExpression; + +using static SyntaxFactory; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +internal sealed class CSharpUseImplicitlyTypedLambdaExpressionDiagnosticAnalyzer() + : AbstractBuiltInCodeStyleDiagnosticAnalyzer(IDEDiagnosticIds.UseImplicitlyTypedLambdaExpressionDiagnosticId, + EnforceOnBuildValues.UseImplicitObjectCreation, + CSharpCodeStyleOptions.PreferImplicitlyTypedLambdaExpression, + new LocalizableResourceString(nameof(CSharpAnalyzersResources.Use_implicitly_typed_lambda), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)), + new LocalizableResourceString(nameof(CSharpAnalyzersResources.Lambda_expression_can_be_simplified), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources))) +{ + public override DiagnosticAnalyzerCategory GetAnalyzerCategory() + => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; + + protected override void InitializeWorker(AnalysisContext context) + => context.RegisterSyntaxNodeAction(AnalyzeIfEnabled, + SyntaxKind.ParenthesizedLambdaExpression); + + private void AnalyzeIfEnabled(SyntaxNodeAnalysisContext context) + { + var cancellationToken = context.CancellationToken; + var analyzerOptions = context.Options; + var semanticModel = context.SemanticModel; + var option = analyzerOptions.GetCSharpAnalyzerOptions(semanticModel.SyntaxTree).PreferImplicitlyTypedLambdaExpression; + if (!option.Value || ShouldSkipAnalysis(context, option.Notification)) + return; + + var explicitLambda = (ParenthesizedLambdaExpressionSyntax)context.Node; + if (!Analyze(semanticModel, explicitLambda, cancellationToken)) + return; + + context.ReportDiagnostic(DiagnosticHelper.Create( + Descriptor, + explicitLambda.ParameterList.OpenParenToken.GetLocation(), + option.Notification, + context.Options, + [explicitLambda.GetLocation()], + properties: null)); + } + + public static bool Analyze( + SemanticModel semanticModel, + ParenthesizedLambdaExpressionSyntax explicitLambda, + CancellationToken cancellationToken) + { + // If the lambda has an explicit return type, then do not offer the feature. Explicit return types are used to + // provide full semantic information to the compiler so it does not need to perform speculative lambda binding. + // Removing may cause code compilation performance to regress. + if (explicitLambda.ReturnType != null) + return false; + + // Needs to have at least one parameter, all parameters need to have a provided type, and no parameters can have a + // default value provided. + if (explicitLambda.ParameterList.Parameters.Count == 0 || + explicitLambda.ParameterList.Parameters.Any(p => p.Type is null || p.Default != null)) + { + return false; + } + + // Prior to C# 14, implicitly typed lambdas can't have modifiers on parameters. + var languageVersion = semanticModel.Compilation.LanguageVersion(); + if (!languageVersion.IsCSharp14OrAbove() && explicitLambda.ParameterList.Parameters.Any(p => p.Modifiers.Count > 0)) + return false; + + var implicitLambda = ConvertToImplicitlyTypedLambda(explicitLambda); + + var analyzer = new SpeculationAnalyzer( + explicitLambda, implicitLambda, semanticModel, cancellationToken); + if (analyzer.ReplacementChangesSemantics()) + return false; + + if (semanticModel.GetSymbolInfo(explicitLambda, cancellationToken).Symbol is not IMethodSymbol explicitLambdaMethod || + analyzer.SpeculativeSemanticModel.GetSymbolInfo(analyzer.ReplacedExpression, cancellationToken).Symbol is not IMethodSymbol implicitLambdaMethod) + { + return false; + } + + if (!SignatureComparer.Instance.HaveSameSignature(explicitLambdaMethod, implicitLambdaMethod, caseSensitive: true)) + return false; + + return true; + } + + public static LambdaExpressionSyntax ConvertToImplicitlyTypedLambda(ParenthesizedLambdaExpressionSyntax explicitLambda) + { + var implicitLambda = explicitLambda.ReplaceNodes( + explicitLambda.ParameterList.Parameters, + (parameter, _) => RemoveParamsModifier( + parameter.WithType(null) + .WithIdentifier(parameter.Identifier.WithPrependedLeadingTrivia(parameter.Type!.GetLeadingTrivia())))); + + // If the lambda only has one parameter, then convert it to the non-parenthesized form. + if (implicitLambda.ParameterList.Parameters is not ([{ AttributeLists.Count: 0, Modifiers.Count: 0 } parameter])) + return implicitLambda; + + return SimpleLambdaExpression( + explicitLambda.AttributeLists, + explicitLambda.Modifiers, + parameter.WithTriviaFrom(explicitLambda.ParameterList), + explicitLambda.Block, + explicitLambda.ExpressionBody); + } + + private static ParameterSyntax RemoveParamsModifier(ParameterSyntax parameter) + { + // Implicitly typed lambdas aren't ever allowed to have the 'params' modifier. + var paramsModifierIndex = parameter.Modifiers.IndexOf(SyntaxKind.ParamsKeyword); + return paramsModifierIndex >= 0 ? parameter.WithModifiers(parameter.Modifiers.RemoveAt(paramsModifierIndex)) : parameter; + } +} diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf index fd92a148a5131..a9944b6452e98 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf @@ -97,6 +97,11 @@ Výraz lambda je možné odebrat + + Lambda expression can be simplified + Lambda expression can be simplified + + Make anonymous function static Nastavit anonymní funkci jako statickou @@ -352,6 +357,11 @@ Použít implicitní typ + + Use implicitly typed lambda + Use implicitly typed lambda + + Use index operator Použít operátor indexu diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf index 62aefa1dfa65f..def7a379da0d0 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf @@ -97,6 +97,11 @@ Lambdaausdruck kann entfernt werden + + Lambda expression can be simplified + Lambda expression can be simplified + + Make anonymous function static Anonyme Funktion statisch machen @@ -352,6 +357,11 @@ Impliziten Typ verwenden + + Use implicitly typed lambda + Use implicitly typed lambda + + Use index operator Indexoperator verwenden diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf index 1c664a907efdc..adc7b18f02ee9 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf @@ -97,6 +97,11 @@ Se puede quitar la expresión lambda + + Lambda expression can be simplified + Lambda expression can be simplified + + Make anonymous function static Hacer que la función anónima sea estática @@ -352,6 +357,11 @@ Usar un tipo implícito + + Use implicitly typed lambda + Use implicitly typed lambda + + Use index operator Usar operador de índice diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf index 3104c437e6a0d..7d1240420e11c 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf @@ -97,6 +97,11 @@ L’expression lambda peut être supprimée. + + Lambda expression can be simplified + Lambda expression can be simplified + + Make anonymous function static Rendre la fonction anonyme statique @@ -352,6 +357,11 @@ Utiliser un type implicite + + Use implicitly typed lambda + Use implicitly typed lambda + + Use index operator Utiliser l'opérateur d'index diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf index c140510bbdf48..2e801e924b71b 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf @@ -97,6 +97,11 @@ L'espressione lambda può essere rimossa + + Lambda expression can be simplified + Lambda expression can be simplified + + Make anonymous function static Rendere statica la funzione anonima @@ -352,6 +357,11 @@ Usa il tipo implicito + + Use implicitly typed lambda + Use implicitly typed lambda + + Use index operator Usa operatore di indice diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf index 4b66e8433b16d..fe0ed3e9530ab 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf @@ -97,6 +97,11 @@ ラムダ式を削除できます + + Lambda expression can be simplified + Lambda expression can be simplified + + Make anonymous function static 匿名関数を静的にする @@ -352,6 +357,11 @@ 暗黙的な型の使用 + + Use implicitly typed lambda + Use implicitly typed lambda + + Use index operator インデックス演算子を使用 diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf index b31a57f0641d0..0d32ece7ae2f0 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf @@ -97,6 +97,11 @@ 람다 식을 제거할 수 있습니다. + + Lambda expression can be simplified + Lambda expression can be simplified + + Make anonymous function static 익명 함수를 정적으로 설정 @@ -352,6 +357,11 @@ 암시적 형식 사용 + + Use implicitly typed lambda + Use implicitly typed lambda + + Use index operator 인덱스 연산자 사용 diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf index 72bec10276155..ab3c71626dbc1 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf @@ -97,6 +97,11 @@ Wyrażenie lambda można usunąć + + Lambda expression can be simplified + Lambda expression can be simplified + + Make anonymous function static Ustaw funkcję anonimową jako statyczną @@ -352,6 +357,11 @@ Użyj niejawnego typu + + Use implicitly typed lambda + Use implicitly typed lambda + + Use index operator Użyj operatora indeksu diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf index 0b0f9af906709..2f3471d9fc516 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf @@ -97,6 +97,11 @@ A expressão lambda pode ser removida + + Lambda expression can be simplified + Lambda expression can be simplified + + Make anonymous function static Tornar a função anônima estática @@ -352,6 +357,11 @@ Usar o tipo implícito + + Use implicitly typed lambda + Use implicitly typed lambda + + Use index operator Usar operador de índice diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf index 2498b23b3f3be..6e903f2745ff0 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf @@ -97,6 +97,11 @@ Лямбда-выражение можно удалить + + Lambda expression can be simplified + Lambda expression can be simplified + + Make anonymous function static Сделать анонимную функцию статической @@ -352,6 +357,11 @@ Использование неявного типа + + Use implicitly typed lambda + Use implicitly typed lambda + + Use index operator Использовать оператор индекса diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf index 973bf1a1f2495..ab3d21c5b60a0 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf @@ -97,6 +97,11 @@ Lambda ifadesi kaldırılabilir + + Lambda expression can be simplified + Lambda expression can be simplified + + Make anonymous function static Anonim işlevi statik hale getir @@ -352,6 +357,11 @@ Örtük tür kullanma + + Use implicitly typed lambda + Use implicitly typed lambda + + Use index operator Dizin işleci kullan diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf index e3251991e3d76..7923c12961cd7 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf @@ -97,6 +97,11 @@ 可以删除 Lambda 表达式 + + Lambda expression can be simplified + Lambda expression can be simplified + + Make anonymous function static 使匿名函数成为静态函数 @@ -352,6 +357,11 @@ 使用隐式类型 + + Use implicitly typed lambda + Use implicitly typed lambda + + Use index operator 使用索引运算符 diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf index 81083e318ef16..45161bed0f596 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf @@ -97,6 +97,11 @@ 可移除 Lambda 運算式 + + Lambda expression can be simplified + Lambda expression can be simplified + + Make anonymous function static 讓匿名函式成為靜態 @@ -352,6 +357,11 @@ 使用隱含類型 + + Use implicitly typed lambda + Use implicitly typed lambda + + Use index operator 使用索引運算子 diff --git a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems index 9ffe839f76f3e..4df925fb0816e 100644 --- a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems +++ b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems @@ -111,6 +111,7 @@ + diff --git a/src/Analyzers/CSharp/CodeFixes/UseImplicitlyTypedLambdaExpression/CSharpUseImplicitObjectCreationCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseImplicitlyTypedLambdaExpression/CSharpUseImplicitObjectCreationCodeFixProvider.cs new file mode 100644 index 0000000000000..c7e1193eedf8b --- /dev/null +++ b/src/Analyzers/CSharp/CodeFixes/UseImplicitlyTypedLambdaExpression/CSharpUseImplicitObjectCreationCodeFixProvider.cs @@ -0,0 +1,63 @@ +// 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.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Simplification; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Simplification; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.UseImplicitlyTypedLambdaExpression; + +using static CSharpUseImplicitlyTypedLambdaExpressionDiagnosticAnalyzer; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseImplicitlyTypedLambdaExpression), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpUseImplicitlyTypedLambdaExpressionCodeFixProvider() : SyntaxEditorBasedCodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + => [IDEDiagnosticIds.UseImplicitlyTypedLambdaExpressionDiagnosticId]; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + RegisterCodeFix(context, CSharpAnalyzersResources.Use_implicitly_typed_lambda, nameof(CSharpAnalyzersResources.Use_implicitly_typed_lambda)); + return Task.CompletedTask; + } + + protected override async Task FixAllAsync( + Document document, ImmutableArray diagnostics, + SyntaxEditor editor, CancellationToken cancellationToken) + { + // process from inside->out so that outer rewrites see the effects of inner changes. + var nodes = diagnostics + .OrderBy(d => d.Location.SourceSpan.End) + .SelectAsArray(d => (ParenthesizedLambdaExpressionSyntax)d.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken)); + + var options = (CSharpSimplifierOptions)await document.GetSimplifierOptionsAsync( + CSharpSimplification.Instance, cancellationToken).ConfigureAwait(false); + + // Bulk apply these, except at the expression level. One fix at the expression level may prevent another fix + // from being valid. + await editor.ApplyExpressionLevelSemanticEditsAsync( + document, + nodes, + (semanticModel, node) => Analyze(semanticModel, node, cancellationToken), + (semanticModel, root, node) => FixOne(root, node), + cancellationToken).ConfigureAwait(false); + } + + private static SyntaxNode FixOne(SyntaxNode root, ParenthesizedLambdaExpressionSyntax explicitLambda) + => root.ReplaceNode(explicitLambda, ConvertToImplicitlyTypedLambda(explicitLambda)); +} diff --git a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems index ffbb78e02db66..373da8195efa9 100644 --- a/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems +++ b/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems @@ -158,6 +158,7 @@ + @@ -201,5 +202,6 @@ + \ No newline at end of file diff --git a/src/Analyzers/CSharp/Tests/UseImplicitlyTypedLambdaExpression/UseImplicitlyTypedLambdaExpressionTests.cs b/src/Analyzers/CSharp/Tests/UseImplicitlyTypedLambdaExpression/UseImplicitlyTypedLambdaExpressionTests.cs new file mode 100644 index 0000000000000..437372cd52917 --- /dev/null +++ b/src/Analyzers/CSharp/Tests/UseImplicitlyTypedLambdaExpression/UseImplicitlyTypedLambdaExpressionTests.cs @@ -0,0 +1,515 @@ +// 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.UseImplicitlyTypedLambdaExpression; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseImplicitlyTypedLambdaExpression; + +using VerifyCS = CSharpCodeFixVerifier< + CSharpUseImplicitlyTypedLambdaExpressionDiagnosticAnalyzer, + CSharpUseImplicitlyTypedLambdaExpressionCodeFixProvider>; + +[Trait(Traits.Feature, Traits.Features.CodeActionsUseImplicitObjectCreation)] +public sealed class UseImplicitlyTypedLambdaExpressionTests +{ + private static readonly LanguageVersion CSharp14 = LanguageVersion.Preview; + + [Fact] + public async Task TestAssignedToObject() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + void M() + { + object a = (int x) => { }; + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestCastedToDelegate() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + void M() + { + object a = (Delegate)((int x) => { }); + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestCastedToObject() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + void M() + { + object a = (object)((int x) => { }); + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestAssignedToDelegate() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + void M() + { + Delegate a = (int x) => { }; + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestAssignedToVar() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + void M() + { + var a = (int x) => { }; + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestAssignedToStronglyTypedDelegate() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + void M() + { + Action a = [|(|]int x) => { }; + } + } + """, + FixedCode = """ + using System; + + class C + { + void M() + { + Action a = x => { }; + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestExplicitReturnType() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + void M() + { + Action a = void (int x) => { }; + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestWithDefaultVAlue() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + void M() + { + Action a = (int x = 1) => { }; + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestCastToStronglyTypedDelegate() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + void M() + { + Delegate a = (Action)([|(|]int x) => { }); + } + } + """, + FixedCode = """ + using System; + + class C + { + void M() + { + Delegate a = (Action)(x => { }); + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestCreationOfStronglyTypedDelegate() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + void M() + { + Delegate a = new Action([|(|]int x) => { }); + } + } + """, + FixedCode = """ + using System; + + class C + { + void M() + { + Delegate a = new Action(x => { }); + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestArgument() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + void M(Action action) + { + M([|(|]int x) => { }); + } + } + """, + FixedCode = """ + using System; + + class C + { + void M(Action action) + { + M(x => { }); + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestOverloadResolution() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + void M(Action action) + { + M((int x) => { }); + } + + void M(Action action) + { + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestModifier_CSharp13() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + delegate void D(ref int i); + + class C + { + void M() + { + D d = (ref int i) => { }; + } + } + """, + LanguageVersion = LanguageVersion.CSharp13, + }.RunAsync(); + } + + [Fact] + public async Task TestModifier_CSharp14() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + delegate void D(ref int i); + + class C + { + void M() + { + D d = [|(|]ref int i) => { }; + } + } + """, + FixedCode = """ + using System; + + delegate void D(ref int i); + + class C + { + void M() + { + D d = [|(|]ref i) => { }; + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestNested() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + void M() + { + Action a = [|(|]int x) => + { + Action b = [|(|]int y) => { }; + }; + } + } + """, + FixedCode = """ + using System; + + class C + { + void M() + { + Action a = x => + { + Action b = y => { }; + }; + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestParams() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + delegate void D(params int[] x); + + class C + { + void M() + { + D d = [|(|]params int[] x) => { }; + } + } + """, + FixedCode = """ + using System; + + delegate void D(params int[] x); + + class C + { + void M() + { + D d = x => { }; + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestMultiLine() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + void M() + { + Action a = + [|(|]int x, + int y, + int z) => { }; + } + } + """, + FixedCode = """ + using System; + + class C + { + void M() + { + Action a = + (x, + y, + z) => { }; + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } + + [Fact] + public async Task TestAttribute() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class XAttribute : Attribute + { + } + + class C + { + void M() + { + Action d = [|(|][X] int i) => { }; + } + } + """, + FixedCode = """ + using System; + + class XAttribute : Attribute + { + } + + class C + { + void M() + { + Action d = [|(|][X] i) => { }; + } + } + """, + LanguageVersion = CSharp14, + }.RunAsync(); + } +} diff --git a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs index d928c1230975e..8f49df52e2dfb 100644 --- a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs +++ b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs @@ -100,6 +100,7 @@ internal static class EnforceOnBuildValues public const EnforceOnBuild MakeAnonymousFunctionStatic = /*IDE0320*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseSystemThreadingLock = /*IDE0330*/ EnforceOnBuild.Recommended; public const EnforceOnBuild UseUnboundGenericTypeInNameOf = /*IDE0340*/ EnforceOnBuild.Recommended; + public const EnforceOnBuild UseImplicitlyTypedLambdaExpression = /*IDE0350*/ 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 40a3080d0299f..0332f81299acd 100644 --- a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs +++ b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs @@ -207,6 +207,8 @@ internal static class IDEDiagnosticIds public const string UseUnboundGenericTypeInNameOfDiagnosticId = "IDE0340"; + public const string UseImplicitlyTypedLambdaExpressionDiagnosticId = "IDE0350"; + // 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 9f974b3e2bdb2..dc931b6b22c5f 100644 --- a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs +++ b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs @@ -154,6 +154,7 @@ internal static class PredefinedCodeFixProviderNames public const string UseExpressionBody = nameof(UseExpressionBody); public const string UseExpressionBodyForLambda = nameof(UseExpressionBodyForLambda); public const string UseImplicitObjectCreation = nameof(UseImplicitObjectCreation); + public const string UseImplicitlyTypedLambdaExpression = nameof(UseImplicitlyTypedLambdaExpression); public const string UseImplicitType = nameof(UseImplicitType); public const string UseIndexOperator = nameof(UseIndexOperator); public const string UseInferredMemberName = nameof(UseInferredMemberName); diff --git a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs index 8ddaef7086ff7..36980fea3c6c2 100644 --- a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs @@ -493,6 +493,9 @@ public void CSharp_VerifyIDEDiagnosticSeveritiesAreConfigurable() # IDE0340 dotnet_diagnostic.IDE0340.severity = %value% + + # IDE0350 + dotnet_diagnostic.IDE0350.severity = %value% # IDE1005 dotnet_diagnostic.IDE1005.severity = %value% @@ -912,6 +915,7 @@ public void CSharp_VerifyIDECodeStyleOptionsAreConfigurable() ("IDE0320", "csharp_prefer_static_anonymous_function", "true"), ("IDE0330", "csharp_prefer_system_threading_lock", "true"), ("IDE0340", "csharp_style_prefer_unbound_generic_type_in_nameof", "true"), + ("IDE0350", "csharp_style_prefer_implicitly_typed_lambda_expression", "true"), ("IDE1005", "csharp_style_conditional_delegate_call", "true"), ("IDE1006", null, null), ("IDE1007", null, null), diff --git a/src/Features/RulesMissingDocumentation.md b/src/Features/RulesMissingDocumentation.md index 4828131b75ff9..07536d5ee0624 100644 --- a/src/Features/RulesMissingDocumentation.md +++ b/src/Features/RulesMissingDocumentation.md @@ -16,6 +16,7 @@ IDE0306 | | Make anonymous function static | IDE0330 | | Use 'System.Threading.Lock' | IDE0340 | | Use unbound generic type | +IDE0350 | | Use implicitly typed lambda | IDE1007 | | | IDE2000 | | Avoid multiple blank lines | IDE2001 | | Embedded statements must be on their own line | diff --git a/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx b/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx index 1294054572933..02364e09a1e84 100644 --- a/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx +++ b/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx @@ -557,4 +557,7 @@ Cancel (Escape) + + Prefer implicitly typed lambda expression + \ No newline at end of file diff --git a/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs b/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs index 90b03e02d0973..799d1327ba962 100644 --- a/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs +++ b/src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs @@ -115,6 +115,7 @@ private static IEnumerable GetExpressionCodeStyleOptions(Tiere yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferIndexOperator, ServicesVSResources.Prefer_index_operator, options, updater); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferRangeOperator, ServicesVSResources.Prefer_range_operator, options, updater); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.ImplicitObjectCreationWhenTypeIsApparent, CSharpVSResources.Prefer_implicit_object_creation_when_type_is_apparent, options, updater); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferImplicitlyTypedLambdaExpression, CSharpVSResources.Prefer_implicitly_typed_lambda_expression, options, updater); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferTupleSwap, ServicesVSResources.Prefer_tuple_swap, options, updater); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferUnboundGenericTypeInNameOf, ServicesVSResources.Prefer_unbound_generic_type_in_nameof, options, updater); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferUtf8StringLiterals, ServicesVSResources.Prefer_Utf8_string_literals, options, updater); diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf index e1c4cc4776bb8..2afd0b93c4f35 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf @@ -142,6 +142,11 @@ Upřednostňovat implicitní vytvoření objektu, pokud je typ zřejmý + + Prefer implicitly typed lambda expression + Prefer implicitly typed lambda expression + + Prefer pattern matching Upřednostňovat porovnávání vzorů diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf index dd26b273bb22c..84959e8ae705f 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf @@ -142,6 +142,11 @@ Implizite Objekterstellung bevorzugen, wenn der Typ offensichtlich ist + + Prefer implicitly typed lambda expression + Prefer implicitly typed lambda expression + + Prefer pattern matching Musterabgleich bevorzugen diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf index f1a6af39bdd90..57551e43d2c1d 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf @@ -142,6 +142,11 @@ Preferir la creación implícita de objetos cuando el tipo sea aparente + + Prefer implicitly typed lambda expression + Prefer implicitly typed lambda expression + + Prefer pattern matching Preferir coincidencia de patrones diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf index 1c01ddbc6d47e..f81239c73f579 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf @@ -142,6 +142,11 @@ Préférer la création d'objets implicites quand le type est apparent + + Prefer implicitly typed lambda expression + Prefer implicitly typed lambda expression + + Prefer pattern matching Préférer les critères spéciaux diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf index cb563ea25c5b9..88e4ba0fb263e 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf @@ -142,6 +142,11 @@ Preferisci la creazione implicita di oggetti quando il tipo è apparente + + Prefer implicitly typed lambda expression + Prefer implicitly typed lambda expression + + Prefer pattern matching Preferisci i criteri di ricerca diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf index 4ddb98d5b905e..0f28b50458bbf 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf @@ -142,6 +142,11 @@ 型が明白な場合に暗黙的なオブジェクトの作成を優先する + + Prefer implicitly typed lambda expression + Prefer implicitly typed lambda expression + + Prefer pattern matching パターン マッチングを優先する diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf index 4869b58f2b810..0b768384a3d5b 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf @@ -142,6 +142,11 @@ 형식이 명백한 경우 암시적 개체 만들기 선호 + + Prefer implicitly typed lambda expression + Prefer implicitly typed lambda expression + + Prefer pattern matching 패턴 일치 선호 diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf index 3c8a0c83b66b0..f11c52cd3f1ce 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf @@ -142,6 +142,11 @@ Preferuj niejawne tworzenie obiektu, gdy typ jest oczywisty + + Prefer implicitly typed lambda expression + Prefer implicitly typed lambda expression + + Prefer pattern matching Preferuj dopasowywanie do wzorca diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf index 87455a313f37c..d120421b416d9 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf @@ -142,6 +142,11 @@ Preferir a criação de objeto implícito quando o tipo for aparente + + Prefer implicitly typed lambda expression + Prefer implicitly typed lambda expression + + Prefer pattern matching Preferir a correspondência de padrões diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf index 8337be2b11d50..9bb3a8631fa78 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf @@ -142,6 +142,11 @@ Предпочитать неявное создание объекта, когда тип очевиден. + + Prefer implicitly typed lambda expression + Prefer implicitly typed lambda expression + + Prefer pattern matching Предпочитать соответствие шаблону diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf index 9400db37331ff..77bb0bf7bb6de 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf @@ -142,6 +142,11 @@ Tür görünür olduğunda örtük nesne oluşturmayı tercih et + + Prefer implicitly typed lambda expression + Prefer implicitly typed lambda expression + + Prefer pattern matching Desen eşleştirmeyi tercih et diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf index 6e6d46a78e19c..b54dba08ed3f1 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf @@ -142,6 +142,11 @@ 类型明显时首选隐式对象创建 + + Prefer implicitly typed lambda expression + Prefer implicitly typed lambda expression + + Prefer pattern matching 首选模式匹配 diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf index 97b2e8f4ade82..e4f89e27cb877 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf @@ -142,6 +142,11 @@ 當類型為實際型態時,建議建立隱含物件 + + Prefer implicitly typed lambda expression + Prefer implicitly typed lambda expression + + Prefer pattern matching 建議使用模式比對 diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index a830da016df2c..b838cbd7a7fbf 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -206,6 +206,7 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"csharp_style_pattern_matching_over_as_with_null_check", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferPatternMatchingOverAsWithNullCheck")}, {"csharp_style_pattern_matching_over_is_with_cast_check", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferPatternMatchingOverIsWithCastCheck")}, {"csharp_style_prefer_extended_property_pattern", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferExtendedPropertyPattern")}, + {"csharp_style_prefer_implicitly_typed_lambda_expression", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferImplicitlyTypedLambdaExpression")}, {"csharp_style_prefer_index_operator", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferIndexOperator")}, {"csharp_style_prefer_local_over_anonymous_function", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferLocalOverAnonymousFunction")}, {"csharp_style_prefer_method_group_conversion", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferMethodGroupConversion")}, diff --git a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb index d04445a96d69a..9fa99df3d2858 100644 --- a/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb +++ b/src/VisualStudio/Core/Test/Options/CSharpEditorConfigGeneratorTests.vb @@ -153,6 +153,7 @@ csharp_prefer_simple_default_expression = true csharp_style_deconstructed_variable_declaration = true csharp_style_implicit_object_creation_when_type_is_apparent = true csharp_style_inlined_variable_declaration = true +csharp_style_prefer_implicitly_typed_lambda_expression = true csharp_style_prefer_index_operator = true csharp_style_prefer_local_over_anonymous_function = true csharp_style_prefer_null_check_over_type_check = true @@ -413,6 +414,7 @@ csharp_prefer_simple_default_expression = true csharp_style_deconstructed_variable_declaration = true csharp_style_implicit_object_creation_when_type_is_apparent = true csharp_style_inlined_variable_declaration = true +csharp_style_prefer_implicitly_typed_lambda_expression = true csharp_style_prefer_index_operator = true csharp_style_prefer_local_over_anonymous_function = true csharp_style_prefer_null_check_over_type_check = true diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs index 596029387e652..c484d9584c3a2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs @@ -149,6 +149,11 @@ private static Option2> CreatePreferE "csharp_prefer_simple_default_expression", CSharpSimplifierOptions.Default.PreferSimpleDefaultExpression); + public static readonly Option2> PreferImplicitlyTypedLambdaExpression = CreateOption( + CodeStyleOptionGroups.ExpressionLevelPreferences, + "csharp_style_prefer_implicitly_typed_lambda_expression", + CSharpSimplifierOptions.Default.PreferImplicitlyTypedLambdaExpression); + private static readonly ImmutableArray s_preferredModifierOrderDefault = [ SyntaxKind.PublicKeyword, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/CSharpSimplifierOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/CSharpSimplifierOptions.cs index bd616fa208148..4eb4949de556d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/CSharpSimplifierOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/CSharpSimplifierOptions.cs @@ -24,6 +24,7 @@ internal sealed record class CSharpSimplifierOptions : SimplifierOptions, IEquat [DataMember] public CodeStyleOption2 VarWhenTypeIsApparent { get; init; } = CodeStyleOption2.FalseWithSilentEnforcement; [DataMember] public CodeStyleOption2 VarElsewhere { get; init; } = CodeStyleOption2.FalseWithSilentEnforcement; [DataMember] public CodeStyleOption2 PreferSimpleDefaultExpression { get; init; } = CodeStyleOption2.TrueWithSuggestionEnforcement; + [DataMember] public CodeStyleOption2 PreferImplicitlyTypedLambdaExpression { get; init; } = CodeStyleOption2.TrueWithSuggestionEnforcement; [DataMember] public CodeStyleOption2 PreferParameterNullChecking { get; init; } = CodeStyleOption2.TrueWithSuggestionEnforcement; [DataMember] public CodeStyleOption2 AllowEmbeddedStatementsOnSameLine { get; init; } = CodeStyleOption2.TrueWithSilentEnforcement; [DataMember] public CodeStyleOption2 PreferBraces { get; init; } = s_defaultPreferBraces; @@ -41,6 +42,7 @@ public CSharpSimplifierOptions(IOptionsReader options) VarWhenTypeIsApparent = options.GetOption(CSharpCodeStyleOptions.VarWhenTypeIsApparent); VarElsewhere = options.GetOption(CSharpCodeStyleOptions.VarElsewhere); PreferSimpleDefaultExpression = options.GetOption(CSharpCodeStyleOptions.PreferSimpleDefaultExpression); + PreferImplicitlyTypedLambdaExpression = options.GetOption(CSharpCodeStyleOptions.PreferImplicitlyTypedLambdaExpression); AllowEmbeddedStatementsOnSameLine = options.GetOption(CSharpCodeStyleOptions.AllowEmbeddedStatementsOnSameLine); PreferBraces = options.GetOption(CSharpCodeStyleOptions.PreferBraces); PreferThrowExpression = options.GetOption(CSharpCodeStyleOptions.PreferThrowExpression); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SignatureComparer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SignatureComparer.cs index 67c9b538c5f15..1e800736f56c0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SignatureComparer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SignatureComparer.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; namespace Microsoft.CodeAnalysis.Shared.Utilities; @@ -103,34 +104,28 @@ private static bool IdentifiersMatch(string identifier1, string identifier2, boo } public bool HaveSameSignature( - IList parameters1, - IList parameters2) + ImmutableArray parameters1, + ImmutableArray parameters2) { - if (parameters1.Count != parameters2.Count) - { + if (parameters1.Length != parameters2.Length) return false; - } return parameters1.SequenceEqual(parameters2, this.ParameterEquivalenceComparer); } public bool HaveSameSignature( - IList parameters1, - IList parameters2, + ImmutableArray parameters1, + ImmutableArray parameters2, bool compareParameterName, bool isCaseSensitive) { - if (parameters1.Count != parameters2.Count) - { + if (parameters1.Length != parameters2.Length) return false; - } - for (var i = 0; i < parameters1.Count; ++i) + for (var i = 0; i < parameters1.Length; ++i) { if (!_symbolEquivalenceComparer.ParameterEquivalenceComparer.Equals(parameters1[i], parameters2[i], compareParameterName, isCaseSensitive)) - { return false; - } } return true;